mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2025-03-13 09:12:27 +01:00
Add 'Set Up System Files' option (#642)
* Add 'Set Up System Files' option * Fix CIA installation and HLE module loading when no console unique data provided.
This commit is contained in:
parent
6262ddafa6
commit
e3a21c8ef1
@ -489,7 +489,7 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
|
|
||||||
artic_traffic_label = new QLabel();
|
artic_traffic_label = new QLabel();
|
||||||
artic_traffic_label->setToolTip(
|
artic_traffic_label->setToolTip(
|
||||||
tr("Current Artic Base traffic speed. Higher values indicate bigger transfer loads."));
|
tr("Current Artic traffic speed. Higher values indicate bigger transfer loads."));
|
||||||
|
|
||||||
emu_speed_label = new QLabel();
|
emu_speed_label = new QLabel();
|
||||||
emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% "
|
emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% "
|
||||||
@ -982,6 +982,7 @@ void GMainWindow::ConnectMenuEvents() {
|
|||||||
connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
|
connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
|
||||||
connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA);
|
connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA);
|
||||||
connect_menu(ui->action_Connect_Artic, &GMainWindow::OnMenuConnectArticBase);
|
connect_menu(ui->action_Connect_Artic, &GMainWindow::OnMenuConnectArticBase);
|
||||||
|
connect_menu(ui->action_Setup_System_Files, &GMainWindow::OnMenuSetUpSystemFiles);
|
||||||
for (u32 region = 0; region < Core::NUM_SYSTEM_TITLE_REGIONS; region++) {
|
for (u32 region = 0; region < Core::NUM_SYSTEM_TITLE_REGIONS; region++) {
|
||||||
connect_menu(ui->menu_Boot_Home_Menu->actions().at(region),
|
connect_menu(ui->menu_Boot_Home_Menu->actions().at(region),
|
||||||
[this, region] { OnMenuBootHomeMenu(region); });
|
[this, region] { OnMenuBootHomeMenu(region); });
|
||||||
@ -1367,9 +1368,9 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
|||||||
|
|
||||||
case Core::System::ResultStatus::ErrorArticDisconnected:
|
case Core::System::ResultStatus::ErrorArticDisconnected:
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("Artic Base Server"),
|
this, tr("Artic Server"),
|
||||||
tr(fmt::format(
|
tr(fmt::format(
|
||||||
"An error has occurred whilst communicating with the Artic Base Server.\n{}",
|
"An error has occurred whilst communicating with the Artic Server.\n{}",
|
||||||
system.GetStatusDetails())
|
system.GetStatusDetails())
|
||||||
.c_str()));
|
.c_str()));
|
||||||
break;
|
break;
|
||||||
@ -1401,7 +1402,9 @@ void GMainWindow::BootGame(const QString& filename) {
|
|||||||
ShutdownGame();
|
ShutdownGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_artic = filename.startsWith(QString::fromStdString("articbase://"));
|
const bool is_artic = filename.startsWith(QString::fromStdString("articbase:/")) ||
|
||||||
|
filename.startsWith(QString::fromStdString("articinio:/")) ||
|
||||||
|
filename.startsWith(QString::fromStdString("articinin:/"));
|
||||||
|
|
||||||
if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) {
|
if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) {
|
||||||
const auto answer = QMessageBox::question(
|
const auto answer = QMessageBox::question(
|
||||||
@ -1444,7 +1447,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
|||||||
QtConfig per_game_config(config_file_name, QtConfig::ConfigType::PerGameConfig);
|
QtConfig per_game_config(config_file_name, QtConfig::ConfigType::PerGameConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Artic Base Server cannot accept a client multiple times, so multiple loaders are not
|
// Artic Server cannot accept a client multiple times, so multiple loaders are not
|
||||||
// possible. Instead register the app loader early and do not create it again on system load.
|
// possible. Instead register the app loader early and do not create it again on system load.
|
||||||
if (!loader->SupportsMultipleInstancesForSameFile()) {
|
if (!loader->SupportsMultipleInstancesForSameFile()) {
|
||||||
system.RegisterAppLoaderEarly(loader);
|
system.RegisterAppLoaderEarly(loader);
|
||||||
@ -2192,6 +2195,92 @@ void GMainWindow::OnMenuLoadFile() {
|
|||||||
BootGame(filename);
|
BootGame(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnMenuSetUpSystemFiles() {
|
||||||
|
QDialog dialog(this);
|
||||||
|
dialog.setWindowTitle(tr("Set Up System Files"));
|
||||||
|
|
||||||
|
QVBoxLayout layout(&dialog);
|
||||||
|
|
||||||
|
QLabel label_description(
|
||||||
|
tr("<p>Azahar needs files from a real console to be able to use some of its features.<br>"
|
||||||
|
"You can get such files with the <a "
|
||||||
|
"href=https://github.com/azahar-emu/ArticSetupTool>Azahar "
|
||||||
|
"Artic Setup Tool</a><br> Notes:<ul><li><b>This operation will install console unique "
|
||||||
|
"files "
|
||||||
|
"to Azahar, do not share your user or nand folders<br>after performing the setup "
|
||||||
|
"process!</b></li><li>Old 3DS setup is needed for the New 3DS setup to "
|
||||||
|
"work.</li><li>Both setup modes will work regardless of the model of the console "
|
||||||
|
"running the setup tool.</li></ul><hr></p>"),
|
||||||
|
&dialog);
|
||||||
|
label_description.setOpenExternalLinks(true);
|
||||||
|
layout.addWidget(&label_description);
|
||||||
|
|
||||||
|
QHBoxLayout layout_h(&dialog);
|
||||||
|
layout.addLayout(&layout_h);
|
||||||
|
|
||||||
|
QLabel label_enter(tr("Enter Azahar Artic Setup Tool address:"), &dialog);
|
||||||
|
|
||||||
|
layout_h.addWidget(&label_enter);
|
||||||
|
|
||||||
|
QLineEdit textInput(UISettings::values.last_artic_base_addr, &dialog);
|
||||||
|
layout_h.addWidget(&textInput);
|
||||||
|
|
||||||
|
QLabel label_select(tr("<br>Choose setup mode:"), &dialog);
|
||||||
|
layout.addWidget(&label_select);
|
||||||
|
|
||||||
|
std::pair<bool, bool> install_state = Core::AreSystemTitlesInstalled();
|
||||||
|
|
||||||
|
QRadioButton radio1(&dialog);
|
||||||
|
QRadioButton radio2(&dialog);
|
||||||
|
if (!install_state.first) {
|
||||||
|
radio1.setText(tr("(\u2139\uFE0F) Old 3DS setup"));
|
||||||
|
radio1.setToolTip(tr("Setup is possible."));
|
||||||
|
|
||||||
|
radio2.setText(tr("(\u26A0) New 3DS setup"));
|
||||||
|
radio2.setToolTip(tr("Old 3DS setup is required first."));
|
||||||
|
radio2.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
radio1.setText(tr("(\u2705) Old 3DS setup"));
|
||||||
|
radio1.setToolTip(tr("Setup completed."));
|
||||||
|
|
||||||
|
if (!install_state.second) {
|
||||||
|
radio2.setText(tr("(\u2139\uFE0F) New 3DS setup"));
|
||||||
|
radio2.setToolTip(tr("Setup is possible."));
|
||||||
|
} else {
|
||||||
|
radio2.setText(tr("(\u2705) New 3DS setup"));
|
||||||
|
radio2.setToolTip(tr("Setup completed."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
radio1.setChecked(true);
|
||||||
|
layout.addWidget(&radio1);
|
||||||
|
layout.addWidget(&radio2);
|
||||||
|
|
||||||
|
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
|
||||||
|
connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||||
|
connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||||
|
layout.addWidget(&buttonBox);
|
||||||
|
|
||||||
|
int res = dialog.exec();
|
||||||
|
if (res == QDialog::Accepted) {
|
||||||
|
bool is_o3ds = radio1.isChecked();
|
||||||
|
if ((is_o3ds && install_state.first) || (!is_o3ds && install_state.second)) {
|
||||||
|
QMessageBox::StandardButton answer =
|
||||||
|
QMessageBox::question(this, tr("Set Up System Files"),
|
||||||
|
tr("The system files for the selected mode are already set "
|
||||||
|
"up.\nReinstall the files anyway?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||||
|
if (answer != QMessageBox::Yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Core::UninstallSystemFiles(is_o3ds ? Core::SystemTitleSet::Old3ds
|
||||||
|
: Core::SystemTitleSet::New3ds);
|
||||||
|
QString addr = textInput.text();
|
||||||
|
UISettings::values.last_artic_base_addr = addr;
|
||||||
|
BootGame(QString::fromStdString(is_o3ds ? "articinio://" : "articinin://").append(addr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnMenuInstallCIA() {
|
void GMainWindow::OnMenuInstallCIA() {
|
||||||
QStringList filepaths = QFileDialog::getOpenFileNames(
|
QStringList filepaths = QFileDialog::getOpenFileNames(
|
||||||
this, tr("Load Files"), UISettings::values.roms_path,
|
this, tr("Load Files"), UISettings::values.roms_path,
|
||||||
@ -3131,16 +3220,16 @@ void GMainWindow::UpdateStatusBar() {
|
|||||||
QStringLiteral("QLabel { color: %0; }").arg(label_color[style_index]);
|
QStringLiteral("QLabel { color: %0; }").arg(label_color[style_index]);
|
||||||
|
|
||||||
artic_traffic_label->setText(
|
artic_traffic_label->setText(
|
||||||
tr("Artic Base Traffic: %1 %2%3").arg(value, 0, 'f', 0).arg(unit).arg(event));
|
tr("Artic Traffic: %1 %2%3").arg(value, 0, 'f', 0).arg(unit).arg(event));
|
||||||
artic_traffic_label->setStyleSheet(style_sheet);
|
artic_traffic_label->setStyleSheet(style_sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.frame_limit.GetValue() == 0) {
|
if (Settings::GetFrameLimit() == 0) {
|
||||||
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
|
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
|
||||||
} else {
|
} else {
|
||||||
emu_speed_label->setText(tr("Speed: %1% / %2%")
|
emu_speed_label->setText(tr("Speed: %1% / %2%")
|
||||||
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
|
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
|
||||||
.arg(Settings::values.frame_limit.GetValue()));
|
.arg(Settings::GetFrameLimit()));
|
||||||
}
|
}
|
||||||
game_fps_label->setText(tr("App: %1 FPS").arg(results.game_fps, 0, 'f', 0));
|
game_fps_label->setText(tr("App: %1 FPS").arg(results.game_fps, 0, 'f', 0));
|
||||||
emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
|
emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
|
||||||
@ -3321,7 +3410,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
|||||||
message = QString::fromStdString(details);
|
message = QString::fromStdString(details);
|
||||||
error_severity_icon = QMessageBox::Icon::Warning;
|
error_severity_icon = QMessageBox::Icon::Warning;
|
||||||
} else if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
|
} else if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
|
||||||
title = tr("Artic Base Server");
|
title = tr("Artic Server");
|
||||||
message =
|
message =
|
||||||
tr(fmt::format("A communication error has occurred. The game will quit.\n{}", details)
|
tr(fmt::format("A communication error has occurred. The game will quit.\n{}", details)
|
||||||
.c_str());
|
.c_str());
|
||||||
|
@ -244,6 +244,7 @@ private slots:
|
|||||||
void OnGameListOpenPerGameProperties(const QString& file);
|
void OnGameListOpenPerGameProperties(const QString& file);
|
||||||
void OnConfigurePerGame();
|
void OnConfigurePerGame();
|
||||||
void OnMenuLoadFile();
|
void OnMenuLoadFile();
|
||||||
|
void OnMenuSetUpSystemFiles();
|
||||||
void OnMenuInstallCIA();
|
void OnMenuInstallCIA();
|
||||||
void OnMenuConnectArticBase();
|
void OnMenuConnectArticBase();
|
||||||
void OnMenuBootHomeMenu(u32 region);
|
void OnMenuBootHomeMenu(u32 region);
|
||||||
|
@ -237,8 +237,7 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
|
|||||||
&ConfigureSystem::UpdateInitTicks);
|
&ConfigureSystem::UpdateInitTicks);
|
||||||
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
||||||
&ConfigureSystem::RefreshConsoleID);
|
&ConfigureSystem::RefreshConsoleID);
|
||||||
connect(ui->button_start_download, &QPushButton::clicked, this,
|
connect(ui->button_regenerate_mac, &QPushButton::clicked, this, &ConfigureSystem::RefreshMAC);
|
||||||
&ConfigureSystem::DownloadFromNUS);
|
|
||||||
|
|
||||||
connect(ui->button_secure_info, &QPushButton::clicked, this, [this] {
|
connect(ui->button_secure_info, &QPushButton::clicked, this, [this] {
|
||||||
ui->button_secure_info->setEnabled(false);
|
ui->button_secure_info->setEnabled(false);
|
||||||
@ -281,34 +280,6 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetupPerGameUI();
|
SetupPerGameUI();
|
||||||
|
|
||||||
ui->combo_download_set->setCurrentIndex(0); // set to Minimal
|
|
||||||
ui->combo_download_region->setCurrentIndex(0); // set to the base region
|
|
||||||
|
|
||||||
HW::AES::InitKeys(true);
|
|
||||||
bool keys_available = HW::AES::IsKeyXAvailable(HW::AES::KeySlotID::NCCHSecure1) &&
|
|
||||||
HW::AES::IsKeyXAvailable(HW::AES::KeySlotID::NCCHSecure2);
|
|
||||||
for (u8 i = 0; i < HW::AES::MaxCommonKeySlot && keys_available; i++) {
|
|
||||||
HW::AES::SelectCommonKeyIndex(i);
|
|
||||||
if (!HW::AES::IsNormalKeyAvailable(HW::AES::KeySlotID::TicketCommonKey)) {
|
|
||||||
keys_available = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keys_available) {
|
|
||||||
ui->button_start_download->setEnabled(true);
|
|
||||||
ui->combo_download_set->setEnabled(true);
|
|
||||||
ui->combo_download_region->setEnabled(true);
|
|
||||||
ui->label_nus_download->setText(tr("Download System Files from Nintendo servers"));
|
|
||||||
} else {
|
|
||||||
ui->button_start_download->setEnabled(false);
|
|
||||||
ui->combo_download_set->setEnabled(false);
|
|
||||||
ui->combo_download_region->setEnabled(false);
|
|
||||||
ui->label_nus_download->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
ui->label_nus_download->setOpenExternalLinks(true);
|
|
||||||
ui->label_nus_download->setText(tr("Azahar is missing keys to download system files."));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigureTime();
|
ConfigureTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,14 +356,13 @@ void ConfigureSystem::ReadSystemSettings() {
|
|||||||
u64 console_id = cfg->GetConsoleUniqueId();
|
u64 console_id = cfg->GetConsoleUniqueId();
|
||||||
ui->label_console_id->setText(
|
ui->label_console_id->setText(
|
||||||
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
|
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
|
||||||
|
mac_address = cfg->GetMacAddress();
|
||||||
|
ui->label_mac->setText(tr("MAC: %1").arg(QString::fromStdString(mac_address)));
|
||||||
|
|
||||||
// set play coin
|
// set play coin
|
||||||
play_coin = Service::PTM::Module::GetPlayCoins();
|
play_coin = Service::PTM::Module::GetPlayCoins();
|
||||||
ui->spinBox_play_coins->setValue(play_coin);
|
ui->spinBox_play_coins->setValue(play_coin);
|
||||||
|
|
||||||
// set firmware download region
|
|
||||||
ui->combo_download_region->setCurrentIndex(static_cast<int>(cfg->GetRegionValue()));
|
|
||||||
|
|
||||||
// Refresh secure data status
|
// Refresh secure data status
|
||||||
RefreshSecureDataStatus();
|
RefreshSecureDataStatus();
|
||||||
}
|
}
|
||||||
@ -484,6 +454,9 @@ void ConfigureSystem::ApplyConfiguration() {
|
|||||||
|
|
||||||
Settings::values.plugin_loader_enabled.SetValue(ui->plugin_loader->isChecked());
|
Settings::values.plugin_loader_enabled.SetValue(ui->plugin_loader->isChecked());
|
||||||
Settings::values.allow_plugin_loader.SetValue(ui->allow_plugin_loader->isChecked());
|
Settings::values.allow_plugin_loader.SetValue(ui->allow_plugin_loader->isChecked());
|
||||||
|
|
||||||
|
cfg->GetMacAddress() = mac_address;
|
||||||
|
cfg->SaveMacAddress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,10 +521,11 @@ void ConfigureSystem::UpdateInitTicks(int init_ticks_type) {
|
|||||||
|
|
||||||
void ConfigureSystem::RefreshConsoleID() {
|
void ConfigureSystem::RefreshConsoleID() {
|
||||||
QMessageBox::StandardButton reply;
|
QMessageBox::StandardButton reply;
|
||||||
QString warning_text = tr("This will replace your current virtual 3DS with a new one. "
|
QString warning_text =
|
||||||
"Your current virtual 3DS will not be recoverable. "
|
tr("This will replace your current virtual 3DS console ID with a new one. "
|
||||||
"This might have unexpected effects in applications. This might fail "
|
"Your current virtual 3DS console ID will not be recoverable. "
|
||||||
"if you use an outdated config save. Continue?");
|
"This might have unexpected effects in applications. This might fail "
|
||||||
|
"if you use an outdated config save. Continue?");
|
||||||
reply = QMessageBox::critical(this, tr("Warning"), warning_text,
|
reply = QMessageBox::critical(this, tr("Warning"), warning_text,
|
||||||
QMessageBox::No | QMessageBox::Yes);
|
QMessageBox::No | QMessageBox::Yes);
|
||||||
if (reply == QMessageBox::No) {
|
if (reply == QMessageBox::No) {
|
||||||
@ -565,6 +539,21 @@ 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::RefreshMAC() {
|
||||||
|
QMessageBox::StandardButton reply;
|
||||||
|
QString warning_text = tr("This will replace your current MAC address with a new one. "
|
||||||
|
"It is not recommended to do this if you got the MAC address from "
|
||||||
|
"your real console using the setup tool. Continue?");
|
||||||
|
reply =
|
||||||
|
QMessageBox::warning(this, tr("Warning"), warning_text, QMessageBox::No | QMessageBox::Yes);
|
||||||
|
if (reply == QMessageBox::No) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mac_address = Service::CFG::GenerateRandomMAC();
|
||||||
|
ui->label_mac->setText(tr("MAC: %1").arg(QString::fromStdString(mac_address)));
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigureSystem::InstallSecureData(const std::string& from_path, const std::string& to_path) {
|
void ConfigureSystem::InstallSecureData(const std::string& from_path, const std::string& to_path) {
|
||||||
std::string from =
|
std::string from =
|
||||||
FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault);
|
FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault);
|
||||||
@ -655,20 +644,9 @@ void ConfigureSystem::SetupPerGameUI() {
|
|||||||
ui->label_plugin_loader->setVisible(false);
|
ui->label_plugin_loader->setVisible(false);
|
||||||
ui->plugin_loader->setVisible(false);
|
ui->plugin_loader->setVisible(false);
|
||||||
ui->allow_plugin_loader->setVisible(false);
|
ui->allow_plugin_loader->setVisible(false);
|
||||||
// Disable the system firmware downloader.
|
|
||||||
ui->label_nus_download->setVisible(false);
|
|
||||||
ui->body_nus_download->setVisible(false);
|
|
||||||
|
|
||||||
ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
|
ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
|
||||||
is_new_3ds);
|
is_new_3ds);
|
||||||
ConfigurationShared::SetColoredTristate(ui->toggle_lle_applets, Settings::values.lle_applets,
|
ConfigurationShared::SetColoredTristate(ui->toggle_lle_applets, Settings::values.lle_applets,
|
||||||
lle_applets);
|
lle_applets);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureSystem::DownloadFromNUS() {
|
|
||||||
ui->button_start_download->setEnabled(false);
|
|
||||||
|
|
||||||
QMessageBox::critical(this, tr("Azahar"), tr("Downloading from NUS has been deprecated."));
|
|
||||||
|
|
||||||
ui->button_start_download->setEnabled(true);
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -51,14 +51,13 @@ private:
|
|||||||
void UpdateInitTime(int init_clock);
|
void UpdateInitTime(int init_clock);
|
||||||
void UpdateInitTicks(int init_ticks_type);
|
void UpdateInitTicks(int init_ticks_type);
|
||||||
void RefreshConsoleID();
|
void RefreshConsoleID();
|
||||||
|
void RefreshMAC();
|
||||||
|
|
||||||
void InstallSecureData(const std::string& from_path, const std::string& to_path);
|
void InstallSecureData(const std::string& from_path, const std::string& to_path);
|
||||||
void RefreshSecureDataStatus();
|
void RefreshSecureDataStatus();
|
||||||
|
|
||||||
void SetupPerGameUI();
|
void SetupPerGameUI();
|
||||||
|
|
||||||
void DownloadFromNUS();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::ConfigureSystem> ui;
|
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
@ -75,4 +74,5 @@ private:
|
|||||||
u8 country_code;
|
u8 country_code;
|
||||||
u16 play_coin;
|
u16 play_coin;
|
||||||
bool system_setup;
|
bool system_setup;
|
||||||
|
std::string mac_address;
|
||||||
};
|
};
|
||||||
|
@ -455,113 +455,49 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="16" column="0">
|
<item row="16" column="0">
|
||||||
|
<widget class="QLabel" name="label_mac">
|
||||||
|
<property name="text">
|
||||||
|
<string>MAC:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="1">
|
||||||
|
<widget class="QPushButton" name="button_regenerate_mac">
|
||||||
|
<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>Regenerate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="17" column="0">
|
||||||
<widget class="QLabel" name="label_plugin_loader">
|
<widget class="QLabel" name="label_plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>3GX Plugin Loader:</string>
|
<string>3GX Plugin Loader:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="16" column="1">
|
<item row="17" column="1">
|
||||||
<widget class="QCheckBox" name="plugin_loader">
|
<widget class="QCheckBox" name="plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable 3GX plugin loader</string>
|
<string>Enable 3GX plugin loader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="17" column="1">
|
<item row="18" column="1">
|
||||||
<widget class="QCheckBox" name="allow_plugin_loader">
|
<widget class="QCheckBox" name="allow_plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Allow applications to change plugin loader state</string>
|
<string>Allow applications to change plugin loader state</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="18" column="0">
|
|
||||||
<widget class="QLabel" name="label_nus_download">
|
|
||||||
<property name="text">
|
|
||||||
<string>Download System Files from Nintendo servers</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="18" column="1">
|
|
||||||
<widget class="QWidget" name="body_nus_download">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_nus_download">
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="combo_download_set">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimal</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Old 3DS</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>New 3DS</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="combo_download_region">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>JPN</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>USA</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>EUR</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>AUS</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>CHN</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>KOR</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>TWN</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="button_start_download">
|
|
||||||
<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>Download</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -762,6 +698,7 @@
|
|||||||
<tabstop>spinBox_play_coins</tabstop>
|
<tabstop>spinBox_play_coins</tabstop>
|
||||||
<tabstop>spinBox_steps_per_hour</tabstop>
|
<tabstop>spinBox_steps_per_hour</tabstop>
|
||||||
<tabstop>button_regenerate_console_id</tabstop>
|
<tabstop>button_regenerate_console_id</tabstop>
|
||||||
|
<tabstop>button_regenerate_mac</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
@ -79,6 +79,8 @@
|
|||||||
<addaction name="action_Load_File"/>
|
<addaction name="action_Load_File"/>
|
||||||
<addaction name="action_Install_CIA"/>
|
<addaction name="action_Install_CIA"/>
|
||||||
<addaction name="action_Connect_Artic"/>
|
<addaction name="action_Connect_Artic"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="action_Setup_System_Files"/>
|
||||||
<addaction name="menu_Boot_Home_Menu"/>
|
<addaction name="menu_Boot_Home_Menu"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_recent_files"/>
|
<addaction name="menu_recent_files"/>
|
||||||
@ -238,6 +240,11 @@
|
|||||||
<string>Connect to Artic Base...</string>
|
<string>Connect to Artic Base...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Setup_System_Files">
|
||||||
|
<property name="text">
|
||||||
|
<string>Set Up System Files...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_Boot_Home_Menu_JPN">
|
<action name="action_Boot_Home_Menu_JPN">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>JPN</string>
|
<string>JPN</string>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -74,6 +74,8 @@ std::string_view GetTextureSamplingName(TextureSampling sampling) {
|
|||||||
|
|
||||||
Values values = {};
|
Values values = {};
|
||||||
static bool configuring_global = true;
|
static bool configuring_global = true;
|
||||||
|
bool is_temporary_frame_limit;
|
||||||
|
double temporary_frame_limit;
|
||||||
|
|
||||||
void LogSettings() {
|
void LogSettings() {
|
||||||
const auto log_setting = [](std::string_view name, const auto& value) {
|
const auto log_setting = [](std::string_view name, const auto& value) {
|
||||||
@ -247,5 +249,4 @@ void DeleteProfile(int index) {
|
|||||||
void RenameCurrentProfile(std::string new_name) {
|
void RenameCurrentProfile(std::string new_name) {
|
||||||
Settings::values.current_input_profile.name = std::move(new_name);
|
Settings::values.current_input_profile.name = std::move(new_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -618,4 +618,10 @@ void CreateProfile(std::string name);
|
|||||||
void DeleteProfile(int index);
|
void DeleteProfile(int index);
|
||||||
void RenameCurrentProfile(std::string new_name);
|
void RenameCurrentProfile(std::string new_name);
|
||||||
|
|
||||||
|
extern bool is_temporary_frame_limit;
|
||||||
|
extern double temporary_frame_limit;
|
||||||
|
static inline double GetFrameLimit() {
|
||||||
|
return is_temporary_frame_limit ? temporary_frame_limit : values.frame_limit.GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -110,9 +110,14 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (signal) {
|
switch (signal) {
|
||||||
case Signal::Reset:
|
case Signal::Reset: {
|
||||||
|
if (app_loader && app_loader->DoingInitialSetup()) {
|
||||||
|
// Treat reset as shutdown if we are doing the initial setup
|
||||||
|
return ResultStatus::ShutdownRequested;
|
||||||
|
}
|
||||||
Reset();
|
Reset();
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
|
}
|
||||||
case Signal::Shutdown:
|
case Signal::Shutdown:
|
||||||
return ResultStatus::ShutdownRequested;
|
return ResultStatus::ShutdownRequested;
|
||||||
case Signal::Load: {
|
case Signal::Load: {
|
||||||
@ -471,7 +476,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
|
|||||||
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
||||||
|
|
||||||
HW::AES::InitKeys();
|
HW::AES::InitKeys();
|
||||||
Service::Init(*this);
|
Service::Init(*this, !app_loader->DoingInitialSetup());
|
||||||
GDBStub::DeferStart();
|
GDBStub::DeferStart();
|
||||||
|
|
||||||
if (!registered_image_interface) {
|
if (!registered_image_interface) {
|
||||||
@ -708,6 +713,10 @@ void System::RegisterAppLoaderEarly(std::unique_ptr<Loader::AppLoader>& loader)
|
|||||||
early_app_loader = std::move(loader);
|
early_app_loader = std::move(loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::IsInitialSetup() {
|
||||||
|
return app_loader && app_loader->DoingInitialSetup();
|
||||||
|
}
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void System::serialize(Archive& ar, const unsigned int file_version) {
|
void System::serialize(Archive& ar, const unsigned int file_version) {
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -363,6 +363,8 @@ public:
|
|||||||
|
|
||||||
void RegisterAppLoaderEarly(std::unique_ptr<Loader::AppLoader>& loader);
|
void RegisterAppLoaderEarly(std::unique_ptr<Loader::AppLoader>& loader);
|
||||||
|
|
||||||
|
bool IsInitialSetup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Initialize the emulated system.
|
* Initialize the emulated system.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -47,9 +47,6 @@ union BatteryState {
|
|||||||
|
|
||||||
using MacAddress = std::array<u8, 6>;
|
using MacAddress = std::array<u8, 6>;
|
||||||
|
|
||||||
// Default MAC address in the Nintendo 3DS range
|
|
||||||
constexpr MacAddress DefaultMac = {0x40, 0xF4, 0x07, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
enum class WifiLinkLevel : u8 {
|
enum class WifiLinkLevel : u8 {
|
||||||
Off = 0,
|
Off = 0,
|
||||||
Poor = 1,
|
Poor = 1,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -274,8 +274,8 @@ enum class SystemInfoMemUsageRegion {
|
|||||||
* Accepted by svcGetSystemInfo param with CITRA_INFORMATION type. Selects which information
|
* Accepted by svcGetSystemInfo param with CITRA_INFORMATION type. Selects which information
|
||||||
* to fetch from Citra. Some string params don't fit in 7 bytes, so they are split.
|
* to fetch from Citra. Some string params don't fit in 7 bytes, so they are split.
|
||||||
*/
|
*/
|
||||||
enum class SystemInfoCitraInformation {
|
enum class SystemInfoEmulatorInformation {
|
||||||
IS_CITRA = 0, // Always set the output to 1, signaling the app is running on Citra.
|
EMULATOR_ID = 0, // Always set the output to 1, signaling the app is running on Citra.
|
||||||
HOST_TICK = 1, // Tick reference from the host in ns, unaffected by lag or cpu speed.
|
HOST_TICK = 1, // Tick reference from the host in ns, unaffected by lag or cpu speed.
|
||||||
EMULATION_SPEED = 2, // Gets the emulation speed set by the user or by KernelSetState.
|
EMULATION_SPEED = 2, // Gets the emulation speed set by the user or by KernelSetState.
|
||||||
BUILD_NAME = 10, // (ie: Nightly, Canary).
|
BUILD_NAME = 10, // (ie: Nightly, Canary).
|
||||||
@ -291,6 +291,15 @@ enum class SystemInfoCitraInformation {
|
|||||||
BUILD_GIT_DESCRIPTION_PART2 = 41, // Git description (commit) last 7 characters.
|
BUILD_GIT_DESCRIPTION_PART2 = 41, // Git description (commit) last 7 characters.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the IS_EMULATOR information
|
||||||
|
*/
|
||||||
|
enum class EmulatorIDs {
|
||||||
|
NONE = 0,
|
||||||
|
CITRA_EMULATOR = 1,
|
||||||
|
AZAHAR_EMULATOR = 2,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current officially supported platforms.
|
* Current officially supported platforms.
|
||||||
*/
|
*/
|
||||||
@ -1443,7 +1452,11 @@ Result SVC::KernelSetState(u32 kernel_state, u32 varg1, u32 varg2) {
|
|||||||
// Citra specific states.
|
// Citra specific states.
|
||||||
case KernelState::KERNEL_STATE_CITRA_EMULATION_SPEED: {
|
case KernelState::KERNEL_STATE_CITRA_EMULATION_SPEED: {
|
||||||
u16 new_value = static_cast<u16>(varg1);
|
u16 new_value = static_cast<u16>(varg1);
|
||||||
Settings::values.frame_limit.SetValue(new_value);
|
if (new_value == 0xFFFF) {
|
||||||
|
Settings::is_temporary_frame_limit = false;
|
||||||
|
} else {
|
||||||
|
Settings::temporary_frame_limit = static_cast<double>(new_value);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Kernel_SVC, "Unknown KernelSetState state={} varg1={} varg2={}", kernel_state,
|
LOG_ERROR(Kernel_SVC, "Unknown KernelSetState state={} varg1={} varg2={}", kernel_state,
|
||||||
@ -1811,25 +1824,25 @@ Result SVC::GetSystemInfo(s64* out, u32 type, s32 param) {
|
|||||||
*out = 0;
|
*out = 0;
|
||||||
return (system.GetNumCores() == 4) ? ResultSuccess : ResultInvalidEnumValue;
|
return (system.GetNumCores() == 4) ? ResultSuccess : ResultInvalidEnumValue;
|
||||||
case SystemInfoType::CITRA_INFORMATION:
|
case SystemInfoType::CITRA_INFORMATION:
|
||||||
switch ((SystemInfoCitraInformation)param) {
|
switch ((SystemInfoEmulatorInformation)param) {
|
||||||
case SystemInfoCitraInformation::IS_CITRA:
|
case SystemInfoEmulatorInformation::EMULATOR_ID:
|
||||||
*out = 1;
|
*out = static_cast<s64>(EmulatorIDs::AZAHAR_EMULATOR);
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::HOST_TICK:
|
case SystemInfoEmulatorInformation::HOST_TICK:
|
||||||
*out = static_cast<s64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
*out = static_cast<s64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
std::chrono::steady_clock::now().time_since_epoch())
|
std::chrono::steady_clock::now().time_since_epoch())
|
||||||
.count());
|
.count());
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::EMULATION_SPEED:
|
case SystemInfoEmulatorInformation::EMULATION_SPEED:
|
||||||
*out = static_cast<s64>(Settings::values.frame_limit.GetValue());
|
*out = static_cast<s64>(Settings::values.frame_limit.GetValue());
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_NAME:
|
case SystemInfoEmulatorInformation::BUILD_NAME:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_name, 0, sizeof(s64));
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_name, 0, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_VERSION:
|
case SystemInfoEmulatorInformation::BUILD_VERSION:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_version, 0, sizeof(s64));
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_version, 0, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_PLATFORM: {
|
case SystemInfoEmulatorInformation::BUILD_PLATFORM: {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
*out = static_cast<s64>(SystemInfoCitraPlatform::PLATFORM_WINDOWS);
|
*out = static_cast<s64>(SystemInfoCitraPlatform::PLATFORM_WINDOWS);
|
||||||
#elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
@ -1843,35 +1856,35 @@ Result SVC::GetSystemInfo(s64* out, u32 type, s32 param) {
|
|||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SystemInfoCitraInformation::BUILD_DATE_PART1:
|
case SystemInfoEmulatorInformation::BUILD_DATE_PART1:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
||||||
(sizeof(s64) - 1) * 0, sizeof(s64));
|
(sizeof(s64) - 1) * 0, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_DATE_PART2:
|
case SystemInfoEmulatorInformation::BUILD_DATE_PART2:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
||||||
(sizeof(s64) - 1) * 1, sizeof(s64));
|
(sizeof(s64) - 1) * 1, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_DATE_PART3:
|
case SystemInfoEmulatorInformation::BUILD_DATE_PART3:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
||||||
(sizeof(s64) - 1) * 2, sizeof(s64));
|
(sizeof(s64) - 1) * 2, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_DATE_PART4:
|
case SystemInfoEmulatorInformation::BUILD_DATE_PART4:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_build_date,
|
||||||
(sizeof(s64) - 1) * 3, sizeof(s64));
|
(sizeof(s64) - 1) * 3, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_GIT_BRANCH_PART1:
|
case SystemInfoEmulatorInformation::BUILD_GIT_BRANCH_PART1:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_branch,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_branch,
|
||||||
(sizeof(s64) - 1) * 0, sizeof(s64));
|
(sizeof(s64) - 1) * 0, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_GIT_BRANCH_PART2:
|
case SystemInfoEmulatorInformation::BUILD_GIT_BRANCH_PART2:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_branch,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_branch,
|
||||||
(sizeof(s64) - 1) * 1, sizeof(s64));
|
(sizeof(s64) - 1) * 1, sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_GIT_DESCRIPTION_PART1:
|
case SystemInfoEmulatorInformation::BUILD_GIT_DESCRIPTION_PART1:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_desc, (sizeof(s64) - 1) * 0,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_desc, (sizeof(s64) - 1) * 0,
|
||||||
sizeof(s64));
|
sizeof(s64));
|
||||||
break;
|
break;
|
||||||
case SystemInfoCitraInformation::BUILD_GIT_DESCRIPTION_PART2:
|
case SystemInfoEmulatorInformation::BUILD_GIT_DESCRIPTION_PART2:
|
||||||
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_desc, (sizeof(s64) - 1) * 1,
|
CopyStringPart(reinterpret_cast<char*>(out), Common::g_scm_desc, (sizeof(s64) - 1) * 1,
|
||||||
sizeof(s64));
|
sizeof(s64));
|
||||||
break;
|
break;
|
||||||
|
@ -94,12 +94,17 @@ public:
|
|||||||
std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content;
|
std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content;
|
||||||
};
|
};
|
||||||
|
|
||||||
NCCHCryptoFile::NCCHCryptoFile(const std::string& out_file) {
|
NCCHCryptoFile::NCCHCryptoFile(const std::string& out_file, bool encrypted_content) {
|
||||||
// A console unique crypto file is used to store the decrypted NCCH file. This is done
|
if (encrypted_content) {
|
||||||
// to prevent Azahar being used as a tool to download easy shareable decrypted contents
|
// A console unique crypto file is used to store the decrypted NCCH file. This is done
|
||||||
// from the eshop.
|
// to prevent Azahar being used as a tool to download easy shareable decrypted contents
|
||||||
file = HW::UniqueData::OpenUniqueCryptoFile(out_file, "wb",
|
// from the eshop.
|
||||||
HW::UniqueData::UniqueCryptoFileID::NCCH);
|
file = HW::UniqueData::OpenUniqueCryptoFile(out_file, "wb",
|
||||||
|
HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||||
|
} else {
|
||||||
|
file = std::make_unique<FileUtil::IOFile>(out_file, "wb");
|
||||||
|
}
|
||||||
|
|
||||||
if (!file->IsOpen()) {
|
if (!file->IsOpen()) {
|
||||||
is_error = true;
|
is_error = true;
|
||||||
}
|
}
|
||||||
@ -573,7 +578,8 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
|||||||
const FileSys::TitleMetadata& tmd = container.GetTitleMetadata();
|
const FileSys::TitleMetadata& tmd = container.GetTitleMetadata();
|
||||||
if (i != current_content_index) {
|
if (i != current_content_index) {
|
||||||
current_content_index = static_cast<u16>(i);
|
current_content_index = static_cast<u16>(i);
|
||||||
current_content_file = std::make_unique<NCCHCryptoFile>(content_file_paths[i]);
|
current_content_file =
|
||||||
|
std::make_unique<NCCHCryptoFile>(content_file_paths[i], decryption_authorized);
|
||||||
current_content_file->decryption_authorized = decryption_authorized;
|
current_content_file->decryption_authorized = decryption_authorized;
|
||||||
}
|
}
|
||||||
auto& file = *current_content_file;
|
auto& file = *current_content_file;
|
||||||
@ -709,7 +715,8 @@ ResultVal<std::size_t> CIAFile::WriteContentDataIndexed(u16 content_index, u64 o
|
|||||||
|
|
||||||
if (content_index != current_content_index) {
|
if (content_index != current_content_index) {
|
||||||
current_content_index = content_index;
|
current_content_index = content_index;
|
||||||
current_content_file = std::make_unique<NCCHCryptoFile>(content_file_paths[content_index]);
|
current_content_file = std::make_unique<NCCHCryptoFile>(content_file_paths[content_index],
|
||||||
|
decryption_authorized);
|
||||||
current_content_file->decryption_authorized = decryption_authorized;
|
current_content_file->decryption_authorized = decryption_authorized;
|
||||||
}
|
}
|
||||||
auto& file = *current_content_file;
|
auto& file = *current_content_file;
|
||||||
@ -1663,9 +1670,12 @@ Result GetTitleInfoFromList(std::span<const u64> title_id_list, Service::FS::Med
|
|||||||
title_info.version = tmd.GetTitleVersion();
|
title_info.version = tmd.GetTitleVersion();
|
||||||
title_info.type = tmd.GetTitleType();
|
title_info.type = tmd.GetTitleType();
|
||||||
} else {
|
} else {
|
||||||
|
LOG_DEBUG(Service_AM, "not found title_id={:016X}", title_id_list[i]);
|
||||||
return Result(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
|
return Result(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||||
ErrorLevel::Permanent);
|
ErrorLevel::Permanent);
|
||||||
}
|
}
|
||||||
|
LOG_DEBUG(Service_AM, "found title_id={:016X} version={:04X}", title_id_list[i],
|
||||||
|
title_info.version);
|
||||||
title_info_out.Write(&title_info, write_offset, sizeof(TitleInfo));
|
title_info_out.Write(&title_info, write_offset, sizeof(TitleInfo));
|
||||||
write_offset += sizeof(TitleInfo);
|
write_offset += sizeof(TitleInfo);
|
||||||
}
|
}
|
||||||
@ -1679,7 +1689,7 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool
|
|||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u32 title_count = rp.Pop<u32>();
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_AM, "media_type={}", media_type);
|
LOG_DEBUG(Service_AM, "media_type={}, ignore_platform={}", media_type, ignore_platform);
|
||||||
|
|
||||||
if (artic_client.get()) {
|
if (artic_client.get()) {
|
||||||
struct AsyncData {
|
struct AsyncData {
|
||||||
@ -1688,7 +1698,7 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool
|
|||||||
std::vector<u64> title_id_list;
|
std::vector<u64> title_id_list;
|
||||||
|
|
||||||
Result res{0};
|
Result res{0};
|
||||||
std::vector<u8> out;
|
std::vector<TitleInfo> out;
|
||||||
Kernel::MappedBuffer* title_id_list_buffer;
|
Kernel::MappedBuffer* title_id_list_buffer;
|
||||||
Kernel::MappedBuffer* title_info_out;
|
Kernel::MappedBuffer* title_info_out;
|
||||||
};
|
};
|
||||||
@ -1730,7 +1740,7 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async_data->out.resize(title_infos->second);
|
async_data->out.resize(title_infos->second / sizeof(TitleInfo));
|
||||||
memcpy(async_data->out.data(), title_infos->first, title_infos->second);
|
memcpy(async_data->out.data(), title_infos->first, title_infos->second);
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
@ -1744,7 +1754,7 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
async_data->title_info_out->Write(async_data->out.data(), 0,
|
async_data->title_info_out->Write(async_data->out.data(), 0,
|
||||||
async_data->out.size());
|
async_data->out.size() / sizeof(TitleInfo));
|
||||||
|
|
||||||
IPC::RequestBuilder rb(ctx, 1, async_data->ignore_platform ? 0 : 4);
|
IPC::RequestBuilder rb(ctx, 1, async_data->ignore_platform ? 0 : 4);
|
||||||
rb.Push(async_data->res);
|
rb.Push(async_data->res);
|
||||||
@ -1763,16 +1773,20 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool
|
|||||||
std::vector<u64> title_id_list(title_count);
|
std::vector<u64> title_id_list(title_count);
|
||||||
title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64));
|
title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64));
|
||||||
|
|
||||||
// eShop checks if the current importing title already exists during installation.
|
// nim checks if the current importing title already exists during installation.
|
||||||
// Normally, since the program wouldn't be commited, getting the title info returns not
|
// Normally, since the program wouldn't be commited, getting the title info returns not
|
||||||
// found. However, since GetTitleInfoFromList does not care if the program was commited and
|
// found. However, since GetTitleInfoFromList does not care if the program was commited and
|
||||||
// only checks for the tmd, it will detect the title and return information while it
|
// only checks for the tmd, it will detect the title and return information while it
|
||||||
// shouldn't. To prevent this, we check if the title ID corresponds to the currently
|
// shouldn't. To prevent this, we check if the importing context is present and not
|
||||||
// importing title and return not found if so.
|
// committed. If that's the case, return not found
|
||||||
Result result = ResultSuccess;
|
Result result = ResultSuccess;
|
||||||
if (am->importing_title) {
|
for (auto tid : title_id_list) {
|
||||||
for (auto tid : title_id_list) {
|
for (auto& import_ctx : am->import_title_contexts) {
|
||||||
if (tid == am->importing_title->title_id) {
|
if (import_ctx.first == tid &&
|
||||||
|
(import_ctx.second.state == ImportTitleContextState::WAITING_FOR_IMPORT ||
|
||||||
|
import_ctx.second.state == ImportTitleContextState::WAITING_FOR_COMMIT ||
|
||||||
|
import_ctx.second.state == ImportTitleContextState::RESUMABLE)) {
|
||||||
|
LOG_DEBUG(Service_AM, "title pending commit title_id={:016X}", tid);
|
||||||
result = Result(ErrorDescription::NotFound, ErrorModule::AM,
|
result = Result(ErrorDescription::NotFound, ErrorModule::AM,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||||
}
|
}
|
||||||
@ -2545,9 +2559,25 @@ void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service_AM, "(STUBBED) media_type=0x{:02x}", media_type);
|
LOG_DEBUG(Service_AM, "(STUBBED) media_type=0x{:02x}", media_type);
|
||||||
|
|
||||||
|
bool needs_cleanup = false;
|
||||||
|
for (auto& import_ctx : am->import_title_contexts) {
|
||||||
|
if (import_ctx.second.state == ImportTitleContextState::NEEDS_CLEANUP) {
|
||||||
|
needs_cleanup = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needs_cleanup) {
|
||||||
|
for (auto& import_ctx : am->import_content_contexts) {
|
||||||
|
if (import_ctx.second.state == ImportTitleContextState::NEEDS_CLEANUP) {
|
||||||
|
needs_cleanup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<bool>(false);
|
rb.Push<bool>(needs_cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::DoCleanup(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::DoCleanup(Kernel::HLERequestContext& ctx) {
|
||||||
@ -2556,6 +2586,22 @@ void Module::Interface::DoCleanup(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service_AM, "(STUBBED) called, media_type={:#02x}", media_type);
|
LOG_DEBUG(Service_AM, "(STUBBED) called, media_type={:#02x}", media_type);
|
||||||
|
|
||||||
|
for (auto it = am->import_content_contexts.begin(); it != am->import_content_contexts.end();) {
|
||||||
|
if (it->second.state == ImportTitleContextState::NEEDS_CLEANUP) {
|
||||||
|
it = am->import_content_contexts.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = am->import_title_contexts.begin(); it != am->import_title_contexts.end();) {
|
||||||
|
if (it->second.state == ImportTitleContextState::NEEDS_CLEANUP) {
|
||||||
|
it = am->import_title_contexts.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
@ -2746,21 +2792,7 @@ void Module::Interface::EndImportProgramWithoutCommit(Kernel::HLERequestContext&
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::CommitImportPrograms(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::CommitImportPrograms(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
CommitImportTitlesImpl(ctx, false, false);
|
||||||
[[maybe_unused]] const auto media_type = static_cast<FS::MediaType>(rp.Pop<u8>());
|
|
||||||
[[maybe_unused]] const u32 title_count = rp.Pop<u32>();
|
|
||||||
[[maybe_unused]] const u8 database = rp.Pop<u8>();
|
|
||||||
const auto buffer = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
// Note: This function is basically a no-op for us since we don't use title.db or ticket.db
|
|
||||||
// files to keep track of installed titles.
|
|
||||||
am->ScanForAllTitles();
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.PushMappedBuffer(buffer);
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps all File operations to allow adding an offset to them.
|
/// Wraps all File operations to allow adding an offset to them.
|
||||||
@ -3074,6 +3106,46 @@ void Module::Interface::GetRequiredSizeFromCia(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(container.GetTitleMetadata().GetContentSizeByIndex(FileSys::TMDContentIndex::Main));
|
rb.Push(container.GetTitleMetadata().GetContentSizeByIndex(FileSys::TMDContentIndex::Main));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::CommitImportProgramsAndUpdateFirmwareAuto(Kernel::HLERequestContext& ctx) {
|
||||||
|
CommitImportTitlesImpl(ctx, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::CommitImportTitlesImpl(Kernel::HLERequestContext& ctx,
|
||||||
|
bool is_update_firm_auto, bool is_titles) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const auto media_type = static_cast<FS::MediaType>(rp.Pop<u8>());
|
||||||
|
[[maybe_unused]] u32 count = rp.Pop<u32>();
|
||||||
|
[[maybe_unused]] u8 database = rp.Pop<u8>();
|
||||||
|
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) update_firm_auto={} is_titles={}", is_update_firm_auto,
|
||||||
|
is_titles);
|
||||||
|
|
||||||
|
auto& title_id_buf = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
std::vector<u64> title_ids(title_id_buf.GetSize() / sizeof(u64));
|
||||||
|
title_id_buf.Read(title_ids.data(), 0, title_id_buf.GetSize());
|
||||||
|
|
||||||
|
for (auto& key_value : am->import_content_contexts) {
|
||||||
|
if (std::find(title_ids.begin(), title_ids.end(), key_value.first) != title_ids.end() &&
|
||||||
|
key_value.second.state == ImportTitleContextState::WAITING_FOR_COMMIT) {
|
||||||
|
key_value.second.state = ImportTitleContextState::NEEDS_CLEANUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto tid : title_ids) {
|
||||||
|
auto it = am->import_title_contexts.find(tid);
|
||||||
|
if (it != am->import_title_contexts.end() &&
|
||||||
|
it->second.state == ImportTitleContextState::WAITING_FOR_COMMIT) {
|
||||||
|
it->second.state = ImportTitleContextState::NEEDS_CLEANUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
am->ScanForTitles(media_type);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
Result UninstallProgram(const FS::MediaType media_type, const u64 title_id) {
|
Result UninstallProgram(const FS::MediaType media_type, const u64 title_id) {
|
||||||
// Use the content folder so we don't delete the user's save data.
|
// Use the content folder so we don't delete the user's save data.
|
||||||
const auto path = GetTitlePath(media_type, title_id) + "content/";
|
const auto path = GetTitlePath(media_type, title_id) + "content/";
|
||||||
@ -3351,6 +3423,10 @@ void Module::Interface::EndImportTitle(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::CommitImportTitles(Kernel::HLERequestContext& ctx) {
|
||||||
|
CommitImportTitlesImpl(ctx, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
void Module::Interface::BeginImportTmd(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::BeginImportTmd(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
@ -3720,6 +3796,10 @@ void Module::Interface::GetDeviceCert(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.PushMappedBuffer(buffer);
|
rb.PushMappedBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::CommitImportTitlesAndUpdateFirmwareAuto(Kernel::HLERequestContext& ctx) {
|
||||||
|
CommitImportTitlesImpl(ctx, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
void Module::Interface::DeleteTicketId(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::DeleteTicketId(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
|
@ -112,7 +112,7 @@ using ProgressCallback = void(std::size_t, std::size_t);
|
|||||||
|
|
||||||
class NCCHCryptoFile final {
|
class NCCHCryptoFile final {
|
||||||
public:
|
public:
|
||||||
NCCHCryptoFile(const std::string& out_file);
|
NCCHCryptoFile(const std::string& out_file, bool encrypted_content);
|
||||||
|
|
||||||
void Write(const u8* buffer, std::size_t length);
|
void Write(const u8* buffer, std::size_t length);
|
||||||
bool IsError() {
|
bool IsError() {
|
||||||
@ -128,7 +128,7 @@ private:
|
|||||||
|
|
||||||
std::size_t written = 0;
|
std::size_t written = 0;
|
||||||
|
|
||||||
NCCH_Header ncch_header;
|
NCCH_Header ncch_header{};
|
||||||
std::size_t header_size = 0;
|
std::size_t header_size = 0;
|
||||||
bool header_parsed = false;
|
bool header_parsed = false;
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ private:
|
|||||||
|
|
||||||
std::vector<CryptoRegion> regions;
|
std::vector<CryptoRegion> regions;
|
||||||
|
|
||||||
ExeFs_Header exefs_header;
|
ExeFs_Header exefs_header{};
|
||||||
std::size_t exefs_header_written = 0;
|
std::size_t exefs_header_written = 0;
|
||||||
bool exefs_header_processed = false;
|
bool exefs_header_processed = false;
|
||||||
};
|
};
|
||||||
@ -414,6 +414,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform);
|
void GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform);
|
||||||
|
|
||||||
|
void CommitImportTitlesImpl(Kernel::HLERequestContext& ctx, bool is_update_firm_auto,
|
||||||
|
bool is_titles);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::GetNumPrograms service function
|
* AM::GetNumPrograms service function
|
||||||
* Gets the number of installed titles in the requested media type
|
* Gets the number of installed titles in the requested media type
|
||||||
@ -873,6 +876,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetRequiredSizeFromCia(Kernel::HLERequestContext& ctx);
|
void GetRequiredSizeFromCia(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
void CommitImportProgramsAndUpdateFirmwareAuto(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::DeleteProgram service function
|
* AM::DeleteProgram service function
|
||||||
* Deletes a program
|
* Deletes a program
|
||||||
@ -949,6 +954,8 @@ public:
|
|||||||
|
|
||||||
void EndImportTitle(Kernel::HLERequestContext& ctx);
|
void EndImportTitle(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
void CommitImportTitles(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
void BeginImportTmd(Kernel::HLERequestContext& ctx);
|
void BeginImportTmd(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
void EndImportTmd(Kernel::HLERequestContext& ctx);
|
void EndImportTmd(Kernel::HLERequestContext& ctx);
|
||||||
@ -983,6 +990,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetDeviceCert(Kernel::HLERequestContext& ctx);
|
void GetDeviceCert(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
void CommitImportTitlesAndUpdateFirmwareAuto(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
void DeleteTicketId(Kernel::HLERequestContext& ctx);
|
void DeleteTicketId(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
void GetNumTicketIds(Kernel::HLERequestContext& ctx);
|
void GetNumTicketIds(Kernel::HLERequestContext& ctx);
|
||||||
@ -1002,6 +1011,14 @@ public:
|
|||||||
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void ForceO3DSDeviceID() {
|
||||||
|
force_old_device_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForceN3DSDeviceID() {
|
||||||
|
force_new_device_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ScanForTickets();
|
void ScanForTickets();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -57,18 +57,18 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x002D, &AM_NET::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
|
{0x002D, &AM_NET::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
|
||||||
{0x0401, nullptr, "UpdateFirmwareTo"},
|
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||||
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
||||||
{0x0403, nullptr, "BeginImportProgramTemporarily"},
|
{0x0403, &AM_NET::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
||||||
{0x0404, nullptr, "CancelImportProgram"},
|
{0x0404, nullptr, "CancelImportProgram"},
|
||||||
{0x0405, &AM_NET::EndImportProgram, "EndImportProgram"},
|
{0x0405, &AM_NET::EndImportProgram, "EndImportProgram"},
|
||||||
{0x0406, nullptr, "EndImportProgramWithoutCommit"},
|
{0x0406, &AM_NET::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
||||||
{0x0407, nullptr, "CommitImportPrograms"},
|
{0x0407, &AM_NET::CommitImportPrograms, "CommitImportPrograms"},
|
||||||
{0x0408, &AM_NET::GetProgramInfoFromCia, "GetProgramInfoFromCia"},
|
{0x0408, &AM_NET::GetProgramInfoFromCia, "GetProgramInfoFromCia"},
|
||||||
{0x0409, &AM_NET::GetSystemMenuDataFromCia, "GetSystemMenuDataFromCia"},
|
{0x0409, &AM_NET::GetSystemMenuDataFromCia, "GetSystemMenuDataFromCia"},
|
||||||
{0x040A, &AM_NET::GetDependencyListFromCia, "GetDependencyListFromCia"},
|
{0x040A, &AM_NET::GetDependencyListFromCia, "GetDependencyListFromCia"},
|
||||||
{0x040B, &AM_NET::GetTransferSizeFromCia, "GetTransferSizeFromCia"},
|
{0x040B, &AM_NET::GetTransferSizeFromCia, "GetTransferSizeFromCia"},
|
||||||
{0x040C, &AM_NET::GetCoreVersionFromCia, "GetCoreVersionFromCia"},
|
{0x040C, &AM_NET::GetCoreVersionFromCia, "GetCoreVersionFromCia"},
|
||||||
{0x040D, &AM_NET::GetRequiredSizeFromCia, "GetRequiredSizeFromCia"},
|
{0x040D, &AM_NET::GetRequiredSizeFromCia, "GetRequiredSizeFromCia"},
|
||||||
{0x040E, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
|
{0x040E, &AM_NET::CommitImportProgramsAndUpdateFirmwareAuto, "CommitImportProgramsAndUpdateFirmwareAuto"},
|
||||||
{0x040F, nullptr, "UpdateFirmwareAuto"},
|
{0x040F, nullptr, "UpdateFirmwareAuto"},
|
||||||
{0x0410, &AM_NET::DeleteProgram, "DeleteProgram"},
|
{0x0410, &AM_NET::DeleteProgram, "DeleteProgram"},
|
||||||
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
||||||
@ -88,7 +88,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0806, &AM_NET::ResumeImportTitle, "ResumeImportTitle"},
|
{0x0806, &AM_NET::ResumeImportTitle, "ResumeImportTitle"},
|
||||||
{0x0807, &AM_NET::CancelImportTitle, "CancelImportTitle"},
|
{0x0807, &AM_NET::CancelImportTitle, "CancelImportTitle"},
|
||||||
{0x0808, &AM_NET::EndImportTitle, "EndImportTitle"},
|
{0x0808, &AM_NET::EndImportTitle, "EndImportTitle"},
|
||||||
{0x0809, nullptr, "CommitImportTitles"},
|
{0x0809, &AM_NET::CommitImportTitles, "CommitImportTitles"},
|
||||||
{0x080A, &AM_NET::BeginImportTmd, "BeginImportTmd"},
|
{0x080A, &AM_NET::BeginImportTmd, "BeginImportTmd"},
|
||||||
{0x080B, nullptr, "CancelImportTmd"},
|
{0x080B, nullptr, "CancelImportTmd"},
|
||||||
{0x080C, &AM_NET::EndImportTmd, "EndImportTmd"},
|
{0x080C, &AM_NET::EndImportTmd, "EndImportTmd"},
|
||||||
@ -106,7 +106,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0818, &AM_NET::GetDeviceCert, "GetDeviceCert"},
|
{0x0818, &AM_NET::GetDeviceCert, "GetDeviceCert"},
|
||||||
{0x0819, nullptr, "ImportCertificates"},
|
{0x0819, nullptr, "ImportCertificates"},
|
||||||
{0x081A, nullptr, "ImportCertificate"},
|
{0x081A, nullptr, "ImportCertificate"},
|
||||||
{0x081B, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"},
|
{0x081B, &AM_NET::CommitImportTitlesAndUpdateFirmwareAuto, "CommitImportTitlesAndUpdateFirmwareAuto"},
|
||||||
{0x081C, &AM_NET::DeleteTicketId, "DeleteTicketId"},
|
{0x081C, &AM_NET::DeleteTicketId, "DeleteTicketId"},
|
||||||
{0x081D, &AM_NET::GetNumTicketIds, "GetNumTicketIds"},
|
{0x081D, &AM_NET::GetNumTicketIds, "GetNumTicketIds"},
|
||||||
{0x081E, &AM_NET::GetTicketIdList, "GetTicketIdList"},
|
{0x081E, &AM_NET::GetTicketIdList, "GetTicketIdList"},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -20,21 +20,21 @@ AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u"
|
|||||||
{0x0008, &AM_U::GetNumTickets, "GetNumTickets"},
|
{0x0008, &AM_U::GetNumTickets, "GetNumTickets"},
|
||||||
{0x0009, &AM_U::GetTicketList, "GetTicketList"},
|
{0x0009, &AM_U::GetTicketList, "GetTicketList"},
|
||||||
{0x000A, &AM_U::GetDeviceID, "GetDeviceID"},
|
{0x000A, &AM_U::GetDeviceID, "GetDeviceID"},
|
||||||
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
{0x000B, &AM_U::GetNumImportTitleContexts, "GetNumImportTitleContexts"},
|
||||||
{0x000C, nullptr, "GetImportTitleContextList"},
|
{0x000C, &AM_U::GetImportTitleContextList, "GetImportTitleContextList"},
|
||||||
{0x000D, nullptr, "GetImportTitleContexts"},
|
{0x000D, &AM_U::GetImportTitleContexts, "GetImportTitleContexts"},
|
||||||
{0x000E, nullptr, "DeleteImportTitleContext"},
|
{0x000E, &AM_U::DeleteImportTitleContext, "DeleteImportTitleContext"},
|
||||||
{0x000F, nullptr, "GetNumImportContentContexts"},
|
{0x000F, &AM_U::GetNumImportContentContexts, "GetNumImportContentContexts"},
|
||||||
{0x0010, nullptr, "GetImportContentContextList"},
|
{0x0010, &AM_U::GetImportContentContextList, "GetImportContentContextList"},
|
||||||
{0x0011, nullptr, "GetImportContentContexts"},
|
{0x0011, &AM_U::GetImportContentContexts, "GetImportContentContexts"},
|
||||||
{0x0012, nullptr, "DeleteImportContentContexts"},
|
{0x0012, nullptr, "DeleteImportContentContexts"},
|
||||||
{0x0013, &AM_U::NeedsCleanup, "NeedsCleanup"},
|
{0x0013, &AM_U::NeedsCleanup, "NeedsCleanup"},
|
||||||
{0x0014, nullptr, "DoCleanup"},
|
{0x0014, &AM_U::DoCleanup, "DoCleanup"},
|
||||||
{0x0015, nullptr, "DeleteAllImportContexts"},
|
{0x0015, nullptr, "DeleteAllImportContexts"},
|
||||||
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
|
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
|
||||||
{0x0017, nullptr, "ImportTwlBackupLegacy"},
|
{0x0017, nullptr, "ImportTwlBackupLegacy"},
|
||||||
{0x0018, nullptr, "InitializeTitleDatabase"},
|
{0x0018, nullptr, "InitializeTitleDatabase"},
|
||||||
{0x0019, nullptr, "QueryAvailableTitleDatabase"},
|
{0x0019, &AM_U::QueryAvailableTitleDatabase, "QueryAvailableTitleDatabase"},
|
||||||
{0x001A, nullptr, "CalcTwlBackupSize"},
|
{0x001A, nullptr, "CalcTwlBackupSize"},
|
||||||
{0x001B, nullptr, "ExportTwlBackup"},
|
{0x001B, nullptr, "ExportTwlBackup"},
|
||||||
{0x001C, nullptr, "ImportTwlBackup"},
|
{0x001C, nullptr, "ImportTwlBackup"},
|
||||||
@ -42,19 +42,19 @@ AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u"
|
|||||||
{0x001E, nullptr, "ReadTwlBackupInfo"},
|
{0x001E, nullptr, "ReadTwlBackupInfo"},
|
||||||
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
|
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
|
||||||
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
|
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
|
||||||
{0x0021, nullptr, "GetPersonalizedTicketInfoList"},
|
{0x0021, &AM_U::GetPersonalizedTicketInfoList, "GetPersonalizedTicketInfoList"},
|
||||||
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
|
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
|
||||||
{0x0023, nullptr, "GetNumImportTitleContextsFiltered"},
|
{0x0023, &AM_U::GetNumImportTitleContextsFiltered, "GetNumImportTitleContextsFiltered"},
|
||||||
{0x0024, nullptr, "GetImportTitleContextListFiltered"},
|
{0x0024, &AM_U::GetImportTitleContextListFiltered, "GetImportTitleContextListFiltered"},
|
||||||
{0x0025, nullptr, "CheckContentRights"},
|
{0x0025, &AM_U::CheckContentRights, "CheckContentRights"},
|
||||||
{0x0026, nullptr, "GetTicketLimitInfos"},
|
{0x0026, nullptr, "GetTicketLimitInfos"},
|
||||||
{0x0027, nullptr, "GetDemoLaunchInfos"},
|
{0x0027, nullptr, "GetDemoLaunchInfos"},
|
||||||
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
|
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
|
||||||
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
|
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
|
||||||
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
|
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
|
||||||
{0x002B, nullptr, "ListExistingContentInfosSystem"},
|
{0x002B, nullptr, "ListExistingContentInfosSystem"},
|
||||||
{0x002C, nullptr, "GetProgramInfosIgnorePlatform"},
|
{0x002C, &AM_U::GetProgramInfosIgnorePlatform, "GetProgramInfosIgnorePlatform"},
|
||||||
{0x002D, nullptr, "CheckContentRightsIgnorePlatform"},
|
{0x002D, &AM_U::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
|
||||||
{0x0401, nullptr, "UpdateFirmwareTo"},
|
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||||
{0x0402, &AM_U::BeginImportProgram, "BeginImportProgram"},
|
{0x0402, &AM_U::BeginImportProgram, "BeginImportProgram"},
|
||||||
{0x0403, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
{0x0403, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
||||||
@ -68,7 +68,7 @@ AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u"
|
|||||||
{0x040B, &AM_U::GetTransferSizeFromCia, "GetTransferSizeFromCia"},
|
{0x040B, &AM_U::GetTransferSizeFromCia, "GetTransferSizeFromCia"},
|
||||||
{0x040C, &AM_U::GetCoreVersionFromCia, "GetCoreVersionFromCia"},
|
{0x040C, &AM_U::GetCoreVersionFromCia, "GetCoreVersionFromCia"},
|
||||||
{0x040D, &AM_U::GetRequiredSizeFromCia, "GetRequiredSizeFromCia"},
|
{0x040D, &AM_U::GetRequiredSizeFromCia, "GetRequiredSizeFromCia"},
|
||||||
{0x040E, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
|
{0x040E, &AM_U::CommitImportProgramsAndUpdateFirmwareAuto, "CommitImportProgramsAndUpdateFirmwareAuto"},
|
||||||
{0x040F, nullptr, "UpdateFirmwareAuto"},
|
{0x040F, nullptr, "UpdateFirmwareAuto"},
|
||||||
{0x0410, &AM_U::DeleteProgram, "DeleteProgram"},
|
{0x0410, &AM_U::DeleteProgram, "DeleteProgram"},
|
||||||
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -40,6 +40,7 @@ template <class Archive>
|
|||||||
void Module::serialize(Archive& ar, const unsigned int) {
|
void Module::serialize(Archive& ar, const unsigned int) {
|
||||||
ar & cfg_config_file_buffer;
|
ar & cfg_config_file_buffer;
|
||||||
ar & cfg_system_save_data_archive;
|
ar & cfg_system_save_data_archive;
|
||||||
|
ar & mac_address;
|
||||||
ar & preferred_region_code;
|
ar & preferred_region_code;
|
||||||
ar & preferred_region_chosen;
|
ar & preferred_region_chosen;
|
||||||
}
|
}
|
||||||
@ -722,6 +723,7 @@ void Module::SaveMCUConfig() {
|
|||||||
Module::Module(Core::System& system_) : system(system_) {
|
Module::Module(Core::System& system_) : system(system_) {
|
||||||
LoadConfigNANDSaveFile();
|
LoadConfigNANDSaveFile();
|
||||||
LoadMCUConfig();
|
LoadMCUConfig();
|
||||||
|
(void)GetMacAddress();
|
||||||
// Check the config savegame EULA Version and update it to 0x7F7F if necessary
|
// Check the config savegame EULA Version and update it to 0x7F7F if necessary
|
||||||
// so users will never get a prompt to accept EULA
|
// so users will never get a prompt to accept EULA
|
||||||
auto version = GetEULAVersion();
|
auto version = GetEULAVersion();
|
||||||
@ -771,6 +773,45 @@ static std::tuple<u32 /*region*/, SystemLanguage> AdjustLanguageInfoBlock(
|
|||||||
return {default_region, region_languages[default_region][0]};
|
return {default_region, region_languages[default_region][0]};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string& Module::GetMacAddress() {
|
||||||
|
if (!mac_address.empty()) {
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtil::IOFile mac_address_file(
|
||||||
|
fmt::format("{}/mac.txt", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "rb");
|
||||||
|
if (!mac_address_file.IsOpen() || mac_address_file.GetSize() > 100) {
|
||||||
|
LOG_INFO(Service_CFG, "Cannot open mac address file for read, generating a new one");
|
||||||
|
mac_address = GenerateRandomMAC();
|
||||||
|
SaveMacAddress();
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file_size = mac_address_file.GetSize();
|
||||||
|
mac_address.resize(file_size);
|
||||||
|
mac_address_file.ReadBytes(mac_address.data(), file_size);
|
||||||
|
if (mac_address != MacToString(MacToU64(mac_address))) {
|
||||||
|
LOG_WARNING(Service_CFG, "Imvalid saved MAC address, generating a new one");
|
||||||
|
mac_address = GenerateRandomMAC();
|
||||||
|
SaveMacAddress();
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::SaveMacAddress() {
|
||||||
|
FileUtil::IOFile mac_address_file(
|
||||||
|
fmt::format("{}/mac.txt", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "wb");
|
||||||
|
|
||||||
|
if (!mac_address_file.IsOpen()) {
|
||||||
|
LOG_ERROR(Service_CFG, "Cannot open mac address file for write");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mac_address_file.WriteBytes(mac_address.data(), mac_address.size());
|
||||||
|
}
|
||||||
|
|
||||||
void Module::UpdatePreferredRegionCode() {
|
void Module::UpdatePreferredRegionCode() {
|
||||||
if (preferred_region_chosen || !system.IsPoweredOn()) {
|
if (preferred_region_chosen || !system.IsPoweredOn()) {
|
||||||
return;
|
return;
|
||||||
@ -883,8 +924,13 @@ std::pair<u32, u64> Module::GenerateConsoleUniqueId() const {
|
|||||||
const u32 random_number = rng.GenerateWord32(0, 0xFFFF);
|
const u32 random_number = rng.GenerateWord32(0, 0xFFFF);
|
||||||
|
|
||||||
u64_le local_friend_code_seed;
|
u64_le local_friend_code_seed;
|
||||||
rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
|
auto& lfcs = HW::UniqueData::GetLocalFriendCodeSeedB();
|
||||||
sizeof(local_friend_code_seed));
|
if (lfcs.IsValid()) {
|
||||||
|
local_friend_code_seed = lfcs.body.friend_code_seed;
|
||||||
|
} else {
|
||||||
|
rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
|
||||||
|
sizeof(local_friend_code_seed));
|
||||||
|
}
|
||||||
|
|
||||||
const u64 console_id =
|
const u64 console_id =
|
||||||
(local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
|
(local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
|
||||||
@ -976,4 +1022,59 @@ std::string GetConsoleIdHash(Core::System& system) {
|
|||||||
return fmt::format("{:02x}", fmt::join(hash.begin(), hash.end(), ""));
|
return fmt::format("{:02x}", fmt::join(hash.begin(), hash.end(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<u8, 6> MacToArray(const std::string& mac) {
|
||||||
|
std::array<u8, 6> ret;
|
||||||
|
int last = -1;
|
||||||
|
int rc = sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n", ret.data() + 0, ret.data() + 1,
|
||||||
|
ret.data() + 2, ret.data() + 3, ret.data() + 4, ret.data() + 5, &last);
|
||||||
|
if (rc != 6 || static_cast<int>(mac.size()) != last) {
|
||||||
|
return MacToArray(GenerateRandomMAC());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MacToString(u64 mac) {
|
||||||
|
return fmt::format("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", (mac >> (5 * 8)) & 0xFF,
|
||||||
|
(mac >> (4 * 8)) & 0xFF, (mac >> (3 * 8)) & 0xFF, (mac >> (2 * 8)) & 0xFF,
|
||||||
|
(mac >> (1 * 8)) & 0xFF, (mac >> (0 * 8)) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MacToString(const std::array<u8, 6>& mac) {
|
||||||
|
u64 mac_u64 = u64(mac[0]) << 40 | u64(mac[1]) << 32 | u64(mac[2]) << 24 | u64(mac[3]) << 16 |
|
||||||
|
u64(mac[4]) << 8 | u64(mac[5]);
|
||||||
|
return MacToString(mac_u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 MacToU64(const std::string& mac) {
|
||||||
|
auto ret = MacToArray(mac);
|
||||||
|
return u64(ret[0]) << 40 | u64(ret[1]) << 32 | u64(ret[2]) << 24 | u64(ret[3]) << 16 |
|
||||||
|
u64(ret[4]) << 8 | u64(ret[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GenerateRandomMAC() {
|
||||||
|
static const std::array<std::pair<u64, u64>, 16> ranges = {{
|
||||||
|
{0x182A7B000000ULL, 0x182A7BFFFFFFULL},
|
||||||
|
{0x2C10C1000000ULL, 0x2C10C1FFFFFFULL},
|
||||||
|
{0x342FBD000000ULL, 0x342FBDFFFFFFULL},
|
||||||
|
{0x34AF2C000000ULL, 0x34AF2CFFFFFFULL},
|
||||||
|
{0x40D28A000000ULL, 0x40D28AFFFFFFULL},
|
||||||
|
{0x40F407000000ULL, 0x40F407FFFFFFULL},
|
||||||
|
{0x48A5E7000000ULL, 0x48A5E7FFFFFFULL},
|
||||||
|
{0x582F40000000ULL, 0x582F40FFFFFFULL},
|
||||||
|
{0x68BD13000000ULL, 0x68BD13FFFFFFULL},
|
||||||
|
{0x58BDA3000000ULL, 0x58BDA3FFFFFFULL},
|
||||||
|
{0x5C521E000000ULL, 0x5C521EFFFFFFULL},
|
||||||
|
{0x606BFF000000ULL, 0x606BFFFFFFFFULL},
|
||||||
|
{0x64B5C6000000ULL, 0x64B5C6FFFFFFULL},
|
||||||
|
{0x78A2A0000000ULL, 0x78A2A0FFFFFFULL},
|
||||||
|
{0x7CBB8A000000ULL, 0x7CBB8AFFFFFFULL},
|
||||||
|
{0x8CCDE8000000ULL, 0x8CCDE8FFFFFFULL},
|
||||||
|
}};
|
||||||
|
CryptoPP::AutoSeededRandomPool rng;
|
||||||
|
auto& range = ranges[rng.GenerateWord32(0, static_cast<CryptoPP::word32>(ranges.size() - 1))];
|
||||||
|
u64 mac = range.first +
|
||||||
|
rng.GenerateWord32(0, static_cast<CryptoPP::word32>(range.second - range.first));
|
||||||
|
return MacToString(mac);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::CFG
|
} // namespace Service::CFG
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -622,6 +622,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SaveMCUConfig();
|
void SaveMCUConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a reference to the console's MAC address
|
||||||
|
*/
|
||||||
|
std::string& GetMacAddress();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current MAC address to the filesystem
|
||||||
|
*/
|
||||||
|
void SaveMacAddress();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdatePreferredRegionCode();
|
void UpdatePreferredRegionCode();
|
||||||
SystemLanguage GetRawSystemLanguage();
|
SystemLanguage GetRawSystemLanguage();
|
||||||
@ -634,6 +644,7 @@ private:
|
|||||||
u32 preferred_region_code = 0;
|
u32 preferred_region_code = 0;
|
||||||
bool preferred_region_chosen = false;
|
bool preferred_region_chosen = false;
|
||||||
MCUData mcu_data{};
|
MCUData mcu_data{};
|
||||||
|
std::string mac_address{};
|
||||||
|
|
||||||
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
|
||||||
|
|
||||||
@ -649,6 +660,16 @@ void InstallInterfaces(Core::System& system);
|
|||||||
/// Convenience function for getting a SHA256 hash of the Console ID
|
/// Convenience function for getting a SHA256 hash of the Console ID
|
||||||
std::string GetConsoleIdHash(Core::System& system);
|
std::string GetConsoleIdHash(Core::System& system);
|
||||||
|
|
||||||
|
std::array<u8, 6> MacToArray(const std::string& mac);
|
||||||
|
|
||||||
|
std::string MacToString(u64 mac);
|
||||||
|
|
||||||
|
std::string MacToString(const std::array<u8, 6>& mac);
|
||||||
|
|
||||||
|
u64 MacToU64(const std::string& mac);
|
||||||
|
|
||||||
|
std::string GenerateRandomMAC();
|
||||||
|
|
||||||
} // namespace Service::CFG
|
} // namespace Service::CFG
|
||||||
|
|
||||||
SERVICE_CONSTRUCT(Service::CFG::Module)
|
SERVICE_CONSTRUCT(Service::CFG::Module)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -916,7 +916,7 @@ void FS_USER::GetFreeBytes(Kernel::HLERequestContext& ctx) {
|
|||||||
void FS_USER::GetSdmcArchiveResource(Kernel::HLERequestContext& ctx) {
|
void FS_USER::GetSdmcArchiveResource(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
LOG_DEBUG(Service_FS, "(STUBBED) called");
|
||||||
|
|
||||||
auto resource = archives.GetArchiveResource(MediaType::SDMC);
|
auto resource = archives.GetArchiveResource(MediaType::SDMC);
|
||||||
|
|
||||||
@ -934,7 +934,7 @@ void FS_USER::GetSdmcArchiveResource(Kernel::HLERequestContext& ctx) {
|
|||||||
void FS_USER::GetNandArchiveResource(Kernel::HLERequestContext& ctx) {
|
void FS_USER::GetNandArchiveResource(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
LOG_DEBUG(Service_FS, "(STUBBED) called");
|
||||||
|
|
||||||
auto resource = archives.GetArchiveResource(MediaType::NAND);
|
auto resource = archives.GetArchiveResource(MediaType::NAND);
|
||||||
if (resource.Failed()) {
|
if (resource.Failed()) {
|
||||||
@ -1103,7 +1103,7 @@ void FS_USER::GetArchiveResource(Kernel::HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
auto media_type = rp.PopEnum<MediaType>();
|
auto media_type = rp.PopEnum<MediaType>();
|
||||||
|
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called Media type=0x{:08X}", media_type);
|
LOG_DEBUG(Service_FS, "(STUBBED) called Media type=0x{:08X}", media_type);
|
||||||
|
|
||||||
auto resource = archives.GetArchiveResource(media_type);
|
auto resource = archives.GetArchiveResource(media_type);
|
||||||
if (resource.Failed()) {
|
if (resource.Failed()) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -11,6 +11,10 @@
|
|||||||
namespace Service::NIM {
|
namespace Service::NIM {
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system) {
|
void InstallInterfaces(Core::System& system) {
|
||||||
|
// Don't register HLE nim on initial setup
|
||||||
|
if (system.IsInitialSetup()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto& service_manager = system.ServiceManager();
|
auto& service_manager = system.ServiceManager();
|
||||||
std::make_shared<NIM_AOC>()->InstallAsService(service_manager);
|
std::make_shared<NIM_AOC>()->InstallAsService(service_manager);
|
||||||
std::make_shared<NIM_S>()->InstallAsService(service_manager);
|
std::make_shared<NIM_S>()->InstallAsService(service_manager);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -10,6 +10,7 @@
|
|||||||
#include "common/archives.h"
|
#include "common/archives.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
@ -17,6 +18,8 @@
|
|||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
#include "core/hle/kernel/shared_page.h"
|
#include "core/hle/kernel/shared_page.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
|
#include "core/hle/service/cfg/cfg_u.h"
|
||||||
#include "core/hle/service/nwm/nwm_uds.h"
|
#include "core/hle/service/nwm/nwm_uds.h"
|
||||||
#include "core/hle/service/nwm/uds_beacon.h"
|
#include "core/hle/service/nwm/uds_beacon.h"
|
||||||
#include "core/hle/service/nwm/uds_connection.h"
|
#include "core/hle/service/nwm/uds_connection.h"
|
||||||
@ -1494,10 +1497,13 @@ NWM_UDS::NWM_UDS(Core::System& system) : ServiceFramework("nwm::UDS"), system(sy
|
|||||||
BeaconBroadcastCallback(user_data, cycles_late);
|
BeaconBroadcastCallback(user_data, cycles_late);
|
||||||
});
|
});
|
||||||
|
|
||||||
CryptoPP::AutoSeededRandomPool rng;
|
MacAddress mac;
|
||||||
auto mac = SharedPage::DefaultMac;
|
|
||||||
// Keep the Nintendo 3DS MAC header and randomly generate the last 3 bytes
|
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
|
||||||
rng.GenerateBlock(static_cast<CryptoPP::byte*>(mac.data() + 3), 3);
|
if (cfg.get()) {
|
||||||
|
auto cfg_module = cfg->GetModule();
|
||||||
|
mac = Service::CFG::MacToArray(cfg_module->GetMacAddress());
|
||||||
|
}
|
||||||
|
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (room_member->IsConnected()) {
|
if (room_member->IsConnected()) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ void Module::Interface::GetAdapterState(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(ptm->battery_is_charging);
|
rb.Push(ptm->battery_is_charging);
|
||||||
|
|
||||||
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
LOG_DEBUG(Service_PTM, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetShellState(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetShellState(Kernel::HLERequestContext& ctx) {
|
||||||
@ -52,7 +52,7 @@ void Module::Interface::GetBatteryLevel(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(static_cast<u32>(ChargeLevels::CompletelyFull)); // Set to a completely full battery
|
rb.Push(static_cast<u32>(ChargeLevels::CompletelyFull)); // Set to a completely full battery
|
||||||
|
|
||||||
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
LOG_DEBUG(Service_PTM, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetBatteryChargeState(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetBatteryChargeState(Kernel::HLERequestContext& ctx) {
|
||||||
@ -62,7 +62,7 @@ void Module::Interface::GetBatteryChargeState(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(ptm->battery_is_charging);
|
rb.Push(ptm->battery_is_charging);
|
||||||
|
|
||||||
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
LOG_DEBUG(Service_PTM, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetPedometerState(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetPedometerState(Kernel::HLERequestContext& ctx) {
|
||||||
@ -72,7 +72,7 @@ void Module::Interface::GetPedometerState(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(ptm->pedometer_is_counting);
|
rb.Push(ptm->pedometer_is_counting);
|
||||||
|
|
||||||
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
LOG_DEBUG(Service_PTM, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetStepHistory(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetStepHistory(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -207,19 +207,26 @@ static bool AttemptLLE(const ServiceModuleInfo& service_module) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::shared_ptr<Kernel::Process> process;
|
std::shared_ptr<Kernel::Process> process;
|
||||||
loader->Load(process);
|
Loader::ResultStatus load_result = loader->Load(process);
|
||||||
|
if (load_result != Loader::ResultStatus::Success) {
|
||||||
|
LOG_ERROR(Service,
|
||||||
|
"Service module \"{}\" could not be loaded (ResultStatus={}); Defaulting to HLE "
|
||||||
|
"implementation.",
|
||||||
|
service_module.name, static_cast<int>(load_result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
LOG_DEBUG(Service, "Service module \"{}\" has been successfully loaded.", service_module.name);
|
LOG_DEBUG(Service, "Service module \"{}\" has been successfully loaded.", service_module.name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize ServiceManager
|
/// Initialize ServiceManager
|
||||||
void Init(Core::System& core) {
|
void Init(Core::System& core, bool allow_lle) {
|
||||||
SM::ServiceManager::InstallInterfaces(core);
|
SM::ServiceManager::InstallInterfaces(core);
|
||||||
core.Kernel().SetAppMainThreadExtendedSleep(false);
|
core.Kernel().SetAppMainThreadExtendedSleep(false);
|
||||||
bool lle_module_present = false;
|
bool lle_module_present = false;
|
||||||
|
|
||||||
for (const auto& service_module : service_module_map) {
|
for (const auto& service_module : service_module_map) {
|
||||||
const bool has_lle = AttemptLLE(service_module);
|
const bool has_lle = allow_lle && AttemptLLE(service_module);
|
||||||
if (!has_lle && service_module.init_function != nullptr) {
|
if (!has_lle && service_module.init_function != nullptr) {
|
||||||
service_module.init_function(core);
|
service_module.init_function(core);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Initialize ServiceManager
|
/// Initialize ServiceManager
|
||||||
void Init(Core::System& system);
|
void Init(Core::System& system, bool allow_lle);
|
||||||
|
|
||||||
struct ServiceModuleInfo {
|
struct ServiceModuleInfo {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -13,21 +13,26 @@
|
|||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/certificate.h"
|
||||||
#include "core/file_sys/ncch_container.h"
|
#include "core/file_sys/ncch_container.h"
|
||||||
|
#include "core/file_sys/otp.h"
|
||||||
#include "core/file_sys/romfs_reader.h"
|
#include "core/file_sys/romfs_reader.h"
|
||||||
#include "core/file_sys/secure_value_backend_artic.h"
|
#include "core/file_sys/secure_value_backend_artic.h"
|
||||||
#include "core/file_sys/title_metadata.h"
|
#include "core/file_sys/title_metadata.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/kernel/resource_limit.h"
|
#include "core/hle/kernel/resource_limit.h"
|
||||||
|
#include "core/hle/kernel/shared_page.h"
|
||||||
#include "core/hle/service/am/am.h"
|
#include "core/hle/service/am/am.h"
|
||||||
#include "core/hle/service/am/am_app.h"
|
#include "core/hle/service/am/am_app.h"
|
||||||
#include "core/hle/service/am/am_net.h"
|
#include "core/hle/service/am/am_net.h"
|
||||||
|
#include "core/hle/service/apt/apt.h"
|
||||||
#include "core/hle/service/cfg/cfg.h"
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
#include "core/hle/service/cfg/cfg_u.h"
|
#include "core/hle/service/cfg/cfg_u.h"
|
||||||
#include "core/hle/service/fs/archive.h"
|
#include "core/hle/service/fs/archive.h"
|
||||||
#include "core/hle/service/fs/fs_user.h"
|
#include "core/hle/service/fs/fs_user.h"
|
||||||
#include "core/hle/service/hid/hid_user.h"
|
#include "core/hle/service/hid/hid_user.h"
|
||||||
|
#include "core/hw/unique_data.h"
|
||||||
#include "core/loader/artic.h"
|
#include "core/loader/artic.h"
|
||||||
#include "core/loader/smdh.h"
|
#include "core/loader/smdh.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
@ -38,6 +43,26 @@ namespace Loader {
|
|||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
|
|
||||||
|
Apploader_Artic::Apploader_Artic(Core::System& system_, const std::string& server_addr,
|
||||||
|
u16 server_port, ArticInitMode init_mode)
|
||||||
|
: AppLoader(system_, FileUtil::IOFile()) {
|
||||||
|
client = std::make_shared<Network::ArticBase::Client>(server_addr, server_port);
|
||||||
|
client->SetCommunicationErrorCallback([&system_](const std::string& msg) {
|
||||||
|
system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected,
|
||||||
|
msg.empty() ? nullptr : msg.c_str());
|
||||||
|
});
|
||||||
|
client->SetArticReportTrafficCallback(
|
||||||
|
[&system_](u32 bytes) { system_.ReportArticTraffic(bytes); });
|
||||||
|
client->SetReportArticEventCallback([&system_](u64 event) {
|
||||||
|
Core::PerfStats::PerfArticEventBits ev =
|
||||||
|
static_cast<Core::PerfStats::PerfArticEventBits>(event & 0xFFFFFFFF);
|
||||||
|
bool set = (event > 32) != 0;
|
||||||
|
system_.ReportPerfArticEvent(ev, set);
|
||||||
|
});
|
||||||
|
is_initial_setup = init_mode != ArticInitMode::NONE;
|
||||||
|
artic_init_mode = init_mode;
|
||||||
|
}
|
||||||
|
|
||||||
Apploader_Artic::~Apploader_Artic() {
|
Apploader_Artic::~Apploader_Artic() {
|
||||||
// TODO(PabloMK7) Find memory leak that prevents the romfs readers being destroyed
|
// TODO(PabloMK7) Find memory leak that prevents the romfs readers being destroyed
|
||||||
// when emulation stops. Looks like the mem leak comes from IVFCFile objects
|
// when emulation stops. Looks like the mem leak comes from IVFCFile objects
|
||||||
@ -110,7 +135,6 @@ Apploader_Artic::LoadNew3dsHwCapabilities() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus Apploader_Artic::LoadExec(std::shared_ptr<Kernel::Process>& process) {
|
ResultStatus Apploader_Artic::LoadExec(std::shared_ptr<Kernel::Process>& process) {
|
||||||
using Kernel::CodeSet;
|
|
||||||
|
|
||||||
if (!is_loaded)
|
if (!is_loaded)
|
||||||
return ResultStatus::ErrorNotLoaded;
|
return ResultStatus::ErrorNotLoaded;
|
||||||
@ -119,108 +143,110 @@ ResultStatus Apploader_Artic::LoadExec(std::shared_ptr<Kernel::Process>& process
|
|||||||
u64_le program_id;
|
u64_le program_id;
|
||||||
if (ResultStatus::Success == ReadCode(code) &&
|
if (ResultStatus::Success == ReadCode(code) &&
|
||||||
ResultStatus::Success == ReadProgramId(program_id)) {
|
ResultStatus::Success == ReadProgramId(program_id)) {
|
||||||
|
return LoadExecImpl(process, program_id, program_exheader, code);
|
||||||
std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
|
|
||||||
(const char*)program_exheader.codeset_info.name, 8);
|
|
||||||
|
|
||||||
std::shared_ptr<CodeSet> codeset = system.Kernel().CreateCodeSet(process_name, program_id);
|
|
||||||
|
|
||||||
codeset->CodeSegment().offset = 0;
|
|
||||||
codeset->CodeSegment().addr = program_exheader.codeset_info.text.address;
|
|
||||||
codeset->CodeSegment().size =
|
|
||||||
program_exheader.codeset_info.text.num_max_pages * Memory::CITRA_PAGE_SIZE;
|
|
||||||
|
|
||||||
codeset->RODataSegment().offset =
|
|
||||||
codeset->CodeSegment().offset + codeset->CodeSegment().size;
|
|
||||||
codeset->RODataSegment().addr = program_exheader.codeset_info.ro.address;
|
|
||||||
codeset->RODataSegment().size =
|
|
||||||
program_exheader.codeset_info.ro.num_max_pages * Memory::CITRA_PAGE_SIZE;
|
|
||||||
|
|
||||||
// TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
|
|
||||||
// to the regular size. Playing it safe for now.
|
|
||||||
u32 bss_page_size = (program_exheader.codeset_info.bss_size + 0xFFF) & ~0xFFF;
|
|
||||||
code.resize(code.size() + bss_page_size, 0);
|
|
||||||
|
|
||||||
codeset->DataSegment().offset =
|
|
||||||
codeset->RODataSegment().offset + codeset->RODataSegment().size;
|
|
||||||
codeset->DataSegment().addr = program_exheader.codeset_info.data.address;
|
|
||||||
codeset->DataSegment().size =
|
|
||||||
program_exheader.codeset_info.data.num_max_pages * Memory::CITRA_PAGE_SIZE +
|
|
||||||
bss_page_size;
|
|
||||||
|
|
||||||
// Apply patches now that the entire codeset (including .bss) has been allocated
|
|
||||||
// const ResultStatus patch_result = overlay_ncch->ApplyCodePatch(code);
|
|
||||||
// if (patch_result != ResultStatus::Success && patch_result != ResultStatus::ErrorNotUsed)
|
|
||||||
// return patch_result;
|
|
||||||
|
|
||||||
codeset->entrypoint = codeset->CodeSegment().addr;
|
|
||||||
codeset->memory = std::move(code);
|
|
||||||
|
|
||||||
process = system.Kernel().CreateProcess(std::move(codeset));
|
|
||||||
|
|
||||||
// Attach a resource limit to the process based on the resource limit category
|
|
||||||
const auto category = static_cast<Kernel::ResourceLimitCategory>(
|
|
||||||
program_exheader.arm11_system_local_caps.resource_limit_category);
|
|
||||||
process->resource_limit = system.Kernel().ResourceLimit().GetForCategory(category);
|
|
||||||
|
|
||||||
// When running N3DS-unaware titles pm will lie about the amount of memory available.
|
|
||||||
// This means RESLIMIT_COMMIT = APPMEMALLOC doesn't correspond to the actual size of
|
|
||||||
// APPLICATION. See:
|
|
||||||
// https://github.com/LumaTeam/Luma3DS/blob/e2778a45/sysmodules/pm/source/launch.c#L237
|
|
||||||
auto& ncch_caps = program_exheader.arm11_system_local_caps;
|
|
||||||
const auto o3ds_mode = *LoadKernelMemoryMode().first;
|
|
||||||
const auto n3ds_mode = static_cast<Kernel::New3dsMemoryMode>(ncch_caps.n3ds_mode);
|
|
||||||
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
|
|
||||||
if (is_new_3ds && n3ds_mode == Kernel::New3dsMemoryMode::Legacy &&
|
|
||||||
category == Kernel::ResourceLimitCategory::Application) {
|
|
||||||
u64 new_limit = 0;
|
|
||||||
switch (o3ds_mode) {
|
|
||||||
case Kernel::MemoryMode::Prod:
|
|
||||||
new_limit = 64_MiB;
|
|
||||||
break;
|
|
||||||
case Kernel::MemoryMode::Dev1:
|
|
||||||
new_limit = 96_MiB;
|
|
||||||
break;
|
|
||||||
case Kernel::MemoryMode::Dev2:
|
|
||||||
new_limit = 80_MiB;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
process->resource_limit->SetLimitValue(Kernel::ResourceLimitType::Commit,
|
|
||||||
static_cast<s32>(new_limit));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the default CPU core for this process
|
|
||||||
process->ideal_processor = program_exheader.arm11_system_local_caps.ideal_processor;
|
|
||||||
|
|
||||||
// Copy data while converting endianness
|
|
||||||
using KernelCaps = std::array<u32, ExHeader_ARM11_KernelCaps::NUM_DESCRIPTORS>;
|
|
||||||
KernelCaps kernel_caps;
|
|
||||||
std::copy_n(program_exheader.arm11_kernel_caps.descriptors, kernel_caps.size(),
|
|
||||||
begin(kernel_caps));
|
|
||||||
process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
|
|
||||||
|
|
||||||
s32 priority = program_exheader.arm11_system_local_caps.priority;
|
|
||||||
u32 stack_size = program_exheader.codeset_info.stack_size;
|
|
||||||
|
|
||||||
// On real HW this is done with FS:Reg, but we can be lazy
|
|
||||||
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
|
||||||
fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id,
|
|
||||||
"articbase://");
|
|
||||||
|
|
||||||
Service::FS::FS_USER::ProductInfo product_info{};
|
|
||||||
if (LoadProductInfo(product_info) != ResultStatus::Success) {
|
|
||||||
return ResultStatus::ErrorArtic;
|
|
||||||
}
|
|
||||||
fs_user->RegisterProductInfo(process->process_id, product_info);
|
|
||||||
|
|
||||||
process->Run(priority, stack_size);
|
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
}
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultStatus Apploader_Artic::LoadExecImpl(std::shared_ptr<Kernel::Process>& process,
|
||||||
|
u64_le program_id, const ExHeader_Header& exheader,
|
||||||
|
std::vector<u8>& code) {
|
||||||
|
using Kernel::CodeSet;
|
||||||
|
|
||||||
|
std::string process_name =
|
||||||
|
Common::StringFromFixedZeroTerminatedBuffer((const char*)exheader.codeset_info.name, 8);
|
||||||
|
|
||||||
|
std::shared_ptr<CodeSet> codeset = system.Kernel().CreateCodeSet(process_name, program_id);
|
||||||
|
|
||||||
|
codeset->CodeSegment().offset = 0;
|
||||||
|
codeset->CodeSegment().addr = exheader.codeset_info.text.address;
|
||||||
|
codeset->CodeSegment().size =
|
||||||
|
exheader.codeset_info.text.num_max_pages * Memory::CITRA_PAGE_SIZE;
|
||||||
|
|
||||||
|
codeset->RODataSegment().offset = codeset->CodeSegment().offset + codeset->CodeSegment().size;
|
||||||
|
codeset->RODataSegment().addr = exheader.codeset_info.ro.address;
|
||||||
|
codeset->RODataSegment().size =
|
||||||
|
exheader.codeset_info.ro.num_max_pages * Memory::CITRA_PAGE_SIZE;
|
||||||
|
|
||||||
|
// TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
|
||||||
|
// to the regular size. Playing it safe for now.
|
||||||
|
u32 bss_page_size = (exheader.codeset_info.bss_size + 0xFFF) & ~0xFFF;
|
||||||
|
code.resize(code.size() + bss_page_size, 0);
|
||||||
|
|
||||||
|
codeset->DataSegment().offset = codeset->RODataSegment().offset + codeset->RODataSegment().size;
|
||||||
|
codeset->DataSegment().addr = exheader.codeset_info.data.address;
|
||||||
|
codeset->DataSegment().size =
|
||||||
|
exheader.codeset_info.data.num_max_pages * Memory::CITRA_PAGE_SIZE + bss_page_size;
|
||||||
|
|
||||||
|
// Apply patches now that the entire codeset (including .bss) has been allocated
|
||||||
|
// const ResultStatus patch_result = overlay_ncch->ApplyCodePatch(code);
|
||||||
|
// if (patch_result != ResultStatus::Success && patch_result != ResultStatus::ErrorNotUsed)
|
||||||
|
// return patch_result;
|
||||||
|
|
||||||
|
codeset->entrypoint = codeset->CodeSegment().addr;
|
||||||
|
codeset->memory = std::move(code);
|
||||||
|
|
||||||
|
process = system.Kernel().CreateProcess(std::move(codeset));
|
||||||
|
|
||||||
|
// Attach a resource limit to the process based on the resource limit category
|
||||||
|
const auto category = static_cast<Kernel::ResourceLimitCategory>(
|
||||||
|
exheader.arm11_system_local_caps.resource_limit_category);
|
||||||
|
process->resource_limit = system.Kernel().ResourceLimit().GetForCategory(category);
|
||||||
|
|
||||||
|
// When running N3DS-unaware titles pm will lie about the amount of memory available.
|
||||||
|
// This means RESLIMIT_COMMIT = APPMEMALLOC doesn't correspond to the actual size of
|
||||||
|
// APPLICATION. See:
|
||||||
|
// https://github.com/LumaTeam/Luma3DS/blob/e2778a45/sysmodules/pm/source/launch.c#L237
|
||||||
|
auto& ncch_caps = exheader.arm11_system_local_caps;
|
||||||
|
const auto o3ds_mode = *LoadKernelMemoryMode().first;
|
||||||
|
const auto n3ds_mode = static_cast<Kernel::New3dsMemoryMode>(ncch_caps.n3ds_mode);
|
||||||
|
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
|
||||||
|
if (is_new_3ds && n3ds_mode == Kernel::New3dsMemoryMode::Legacy &&
|
||||||
|
category == Kernel::ResourceLimitCategory::Application) {
|
||||||
|
u64 new_limit = 0;
|
||||||
|
switch (o3ds_mode) {
|
||||||
|
case Kernel::MemoryMode::Prod:
|
||||||
|
new_limit = 64_MiB;
|
||||||
|
break;
|
||||||
|
case Kernel::MemoryMode::Dev1:
|
||||||
|
new_limit = 96_MiB;
|
||||||
|
break;
|
||||||
|
case Kernel::MemoryMode::Dev2:
|
||||||
|
new_limit = 80_MiB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
process->resource_limit->SetLimitValue(Kernel::ResourceLimitType::Commit,
|
||||||
|
static_cast<s32>(new_limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default CPU core for this process
|
||||||
|
process->ideal_processor = exheader.arm11_system_local_caps.ideal_processor;
|
||||||
|
|
||||||
|
// Copy data while converting endianness
|
||||||
|
using KernelCaps = std::array<u32, ExHeader_ARM11_KernelCaps::NUM_DESCRIPTORS>;
|
||||||
|
KernelCaps kernel_caps;
|
||||||
|
std::copy_n(exheader.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps));
|
||||||
|
process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
|
||||||
|
|
||||||
|
s32 priority = exheader.arm11_system_local_caps.priority;
|
||||||
|
u32 stack_size = exheader.codeset_info.stack_size;
|
||||||
|
|
||||||
|
// On real HW this is done with FS:Reg, but we can be lazy
|
||||||
|
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||||
|
fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, "articbase://");
|
||||||
|
|
||||||
|
Service::FS::FS_USER::ProductInfo product_info{};
|
||||||
|
if (LoadProductInfo(product_info) != ResultStatus::Success) {
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
}
|
||||||
|
fs_user->RegisterProductInfo(process->process_id, product_info);
|
||||||
|
|
||||||
|
process->Run(priority, stack_size);
|
||||||
|
return ResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
void Apploader_Artic::ParseRegionLockoutInfo(u64 program_id) {
|
void Apploader_Artic::ParseRegionLockoutInfo(u64 program_id) {
|
||||||
if (Settings::values.region_value.GetValue() != Settings::REGION_VALUE_AUTO_SELECT) {
|
if (Settings::values.region_value.GetValue() != Settings::REGION_VALUE_AUTO_SELECT) {
|
||||||
return;
|
return;
|
||||||
@ -252,8 +278,7 @@ bool Apploader_Artic::LoadExheader() {
|
|||||||
if (program_exheader_loaded)
|
if (program_exheader_loaded)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -287,8 +312,7 @@ ResultStatus Apploader_Artic::LoadProductInfo(Service::FS::FS_USER::ProductInfo&
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
@ -307,6 +331,34 @@ ResultStatus Apploader_Artic::LoadProductInfo(Service::FS::FS_USER::ProductInfo&
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Apploader_Artic::EnsureClientConnected() {
|
||||||
|
if (client_connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client_connected = client->Connect();
|
||||||
|
if (!client_connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_initial_setup) {
|
||||||
|
// Ensure we are running the initial setup app in the correct version
|
||||||
|
auto req = client->NewRequest("System_IsAzaharInitialSetup");
|
||||||
|
auto resp = client->Send(req);
|
||||||
|
if (!resp.has_value()) {
|
||||||
|
client_connected = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ret_buf = resp->GetResponseBuffer(0);
|
||||||
|
if (!ret_buf.has_value() || ret_buf->second != sizeof(u32)) {
|
||||||
|
client_connected = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_connected = *reinterpret_cast<u32*>(ret_buf->first) == INITIAL_SETUP_APP_VERSION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ResultStatus Apploader_Artic::Load(std::shared_ptr<Kernel::Process>& process) {
|
ResultStatus Apploader_Artic::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||||
u64_le ncch_program_id;
|
u64_le ncch_program_id;
|
||||||
|
|
||||||
@ -331,41 +383,173 @@ ResultStatus Apploader_Artic::Load(std::shared_ptr<Kernel::Process>& process) {
|
|||||||
|
|
||||||
is_loaded = true; // Set state to loaded
|
is_loaded = true; // Set state to loaded
|
||||||
|
|
||||||
|
if (is_initial_setup) {
|
||||||
|
|
||||||
|
// Request console unique data
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
std::string path;
|
||||||
|
std::size_t expected_size;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
path = HW::UniqueData::GetSecureInfoAPath();
|
||||||
|
expected_size = sizeof(HW::UniqueData::SecureInfoA);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
path = HW::UniqueData::GetLocalFriendCodeSeedBPath();
|
||||||
|
expected_size = sizeof(HW::UniqueData::LocalFriendCodeSeedB);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
path = HW::UniqueData::GetMovablePath();
|
||||||
|
expected_size = sizeof(HW::UniqueData::MovableSedFull);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
path = HW::UniqueData::GetOTPPath();
|
||||||
|
expected_size = sizeof(FileSys::OTP::OTPBin);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
expected_size = sizeof(u64) + sizeof(u32);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
expected_size = sizeof(u8) * 6;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto req = client->NewRequest("System_GetSystemFile");
|
||||||
|
req.AddParameterU8(static_cast<u8>(i));
|
||||||
|
auto resp = client->Send(req);
|
||||||
|
if (!resp.has_value() || !resp->Succeeded())
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
if (resp->GetMethodResult() != 0)
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
auto resp_buff = resp->GetResponseBuffer(0);
|
||||||
|
if (!resp_buff.has_value() || resp_buff->second != expected_size)
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
if (i < 4) {
|
||||||
|
FileUtil::CreateFullPath(path);
|
||||||
|
FileUtil::IOFile out_file(path, "wb");
|
||||||
|
if (!out_file.IsOpen()) {
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
}
|
||||||
|
out_file.WriteBytes(reinterpret_cast<u8*>(resp_buff->first), resp_buff->second);
|
||||||
|
} else if (i == 4) {
|
||||||
|
u64 console_id;
|
||||||
|
u32 random_id;
|
||||||
|
memcpy(&console_id, resp_buff->first, sizeof(u64));
|
||||||
|
memcpy(&random_id, reinterpret_cast<u8*>(resp_buff->first) + sizeof(u64),
|
||||||
|
sizeof(u32));
|
||||||
|
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
|
||||||
|
if (cfg.get()) {
|
||||||
|
auto cfg_module = cfg->GetModule();
|
||||||
|
cfg_module->SetConsoleUniqueId(random_id, console_id);
|
||||||
|
cfg_module->UpdateConfigNANDSavegame();
|
||||||
|
}
|
||||||
|
} else if (i == 5) {
|
||||||
|
std::array<u8, 6> mac;
|
||||||
|
memcpy(mac.data(), resp_buff->first, mac.size());
|
||||||
|
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
|
||||||
|
if (cfg.get()) {
|
||||||
|
auto cfg_module = cfg->GetModule();
|
||||||
|
cfg_module->GetMacAddress() = Service::CFG::MacToString(mac);
|
||||||
|
cfg_module->SaveMacAddress();
|
||||||
|
}
|
||||||
|
system.Kernel().GetSharedPageHandler().SetMacAddress(mac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HW::UniqueData::InvalidateSecureData();
|
||||||
|
if (!HW::UniqueData::GetCTCert().IsValid() || !HW::UniqueData::GetMovableSed().IsValid() ||
|
||||||
|
!HW::UniqueData::GetSecureInfoA().IsValid() ||
|
||||||
|
!HW::UniqueData::GetLocalFriendCodeSeedB().IsValid()) {
|
||||||
|
LOG_CRITICAL(Loader, "Some console unique data is invalid, aborting...");
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set deliver arg so that System Settings goes to the update screen directly
|
||||||
|
auto apt = Service::APT::GetModule(system);
|
||||||
|
Service::APT::DeliverArg arg;
|
||||||
|
arg.param.push_back(0x7a);
|
||||||
|
apt->GetAppletManager()->SetDeliverArg(arg);
|
||||||
|
|
||||||
|
// Load NIM
|
||||||
|
auto req = client->NewRequest("System_GetNIM");
|
||||||
|
auto resp = client->Send(req);
|
||||||
|
if (!resp.has_value() || !resp->Succeeded())
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
if (resp->GetMethodResult() != 0)
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
auto resp_buff = resp->GetResponseBuffer(0);
|
||||||
|
if (!resp_buff.has_value() || resp_buff->second != sizeof(ExHeader_Header))
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
ExHeader_Header nim_exheader;
|
||||||
|
memcpy(&nim_exheader, resp_buff->first, resp_buff->second);
|
||||||
|
|
||||||
|
resp_buff = resp->GetResponseBuffer(1);
|
||||||
|
if (!resp_buff.has_value())
|
||||||
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
std::vector<u8> code(resp_buff->second);
|
||||||
|
memcpy(code.data(), resp_buff->first, resp_buff->second);
|
||||||
|
|
||||||
|
std::shared_ptr<Kernel::Process> nim_process;
|
||||||
|
result = LoadExecImpl(nim_process, 0x0004013000002C02, nim_exheader, code);
|
||||||
|
if (ResultStatus::Success != result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Force O3DS mode so that NIM fetches O3DS titles
|
||||||
|
auto am = Service::AM::GetModule(system);
|
||||||
|
if (am) {
|
||||||
|
if (artic_init_mode == ArticInitMode::O3DS) {
|
||||||
|
am->ForceO3DSDeviceID();
|
||||||
|
} else if (artic_init_mode == ArticInitMode::N3DS) {
|
||||||
|
am->ForceN3DSDeviceID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = LoadExec(process); // Load the executable into memory for booting
|
result = LoadExec(process); // Load the executable into memory for booting
|
||||||
if (ResultStatus::Success != result)
|
if (ResultStatus::Success != result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
system.ArchiveManager().RegisterSelfNCCH(*this);
|
system.ArchiveManager().RegisterSelfNCCH(*this);
|
||||||
system.ArchiveManager().RegisterArticSaveDataSource(client);
|
if (!is_initial_setup) {
|
||||||
system.ArchiveManager().RegisterArticExtData(client);
|
system.ArchiveManager().RegisterArticSaveDataSource(client);
|
||||||
system.ArchiveManager().RegisterArticNCCH(client);
|
system.ArchiveManager().RegisterArticExtData(client);
|
||||||
system.ArchiveManager().RegisterArticSystemSaveData(client);
|
system.ArchiveManager().RegisterArticNCCH(client);
|
||||||
|
system.ArchiveManager().RegisterArticSystemSaveData(client);
|
||||||
|
|
||||||
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||||
if (fs_user.get()) {
|
if (fs_user.get()) {
|
||||||
fs_user->RegisterSecureValueBackend(
|
fs_user->RegisterSecureValueBackend(
|
||||||
std::make_shared<FileSys::ArticSecureValueBackend>(client));
|
std::make_shared<FileSys::ArticSecureValueBackend>(client));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
|
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
|
||||||
if (cfg.get()) {
|
if (cfg.get()) {
|
||||||
cfg->UseArticClient(client);
|
cfg->UseArticClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto amnet = system.ServiceManager().GetService<Service::AM::AM_NET>("am:net");
|
auto amnet = system.ServiceManager().GetService<Service::AM::AM_NET>("am:net");
|
||||||
if (amnet.get()) {
|
if (amnet.get()) {
|
||||||
amnet->UseArticClient(client);
|
amnet->UseArticClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto amapp = system.ServiceManager().GetService<Service::AM::AM_APP>("am:app");
|
auto amapp = system.ServiceManager().GetService<Service::AM::AM_APP>("am:app");
|
||||||
if (amapp.get()) {
|
if (amapp.get()) {
|
||||||
amapp->UseArticClient(client);
|
amapp->UseArticClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.use_artic_base_controller.GetValue()) {
|
if (Settings::values.use_artic_base_controller.GetValue()) {
|
||||||
auto hid_user = system.ServiceManager().GetService<Service::HID::User>("hid:USER");
|
auto hid_user = system.ServiceManager().GetService<Service::HID::User>("hid:USER");
|
||||||
if (hid_user.get()) {
|
if (hid_user.get()) {
|
||||||
hid_user->GetModule()->UseArticClient(client);
|
hid_user->GetModule()->UseArticClient(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,8 +566,7 @@ ResultStatus Apploader_Artic::IsExecutable(bool& out_executable) {
|
|||||||
ResultStatus Apploader_Artic::ReadCode(std::vector<u8>& buffer) {
|
ResultStatus Apploader_Artic::ReadCode(std::vector<u8>& buffer) {
|
||||||
// Code is only read once, there is no need to cache it.
|
// Code is only read once, there is no need to cache it.
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
@ -423,8 +606,7 @@ ResultStatus Apploader_Artic::ReadIcon(std::vector<u8>& buffer) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
@ -450,8 +632,7 @@ ResultStatus Apploader_Artic::ReadBanner(std::vector<u8>& buffer) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
@ -477,8 +658,7 @@ ResultStatus Apploader_Artic::ReadLogo(std::vector<u8>& buffer) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
@ -504,8 +684,7 @@ ResultStatus Apploader_Artic::ReadProgramId(u64& out_program_id) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client_connected)
|
EnsureClientConnected();
|
||||||
client_connected = client->Connect();
|
|
||||||
if (!client_connected)
|
if (!client_connected)
|
||||||
return ResultStatus::ErrorArtic;
|
return ResultStatus::ErrorArtic;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -18,22 +18,13 @@ namespace Loader {
|
|||||||
/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
|
/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
|
||||||
class Apploader_Artic final : public AppLoader {
|
class Apploader_Artic final : public AppLoader {
|
||||||
public:
|
public:
|
||||||
Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port)
|
enum class ArticInitMode {
|
||||||
: AppLoader(system_, FileUtil::IOFile()) {
|
NONE,
|
||||||
client = std::make_shared<Network::ArticBase::Client>(server_addr, server_port);
|
O3DS,
|
||||||
client->SetCommunicationErrorCallback([&system_](const std::string& msg) {
|
N3DS,
|
||||||
system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected,
|
};
|
||||||
msg.empty() ? nullptr : msg.c_str());
|
Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port,
|
||||||
});
|
ArticInitMode init_mode);
|
||||||
client->SetArticReportTrafficCallback(
|
|
||||||
[&system_](u32 bytes) { system_.ReportArticTraffic(bytes); });
|
|
||||||
client->SetReportArticEventCallback([&system_](u64 event) {
|
|
||||||
Core::PerfStats::PerfArticEventBits ev =
|
|
||||||
static_cast<Core::PerfStats::PerfArticEventBits>(event & 0xFFFFFFFF);
|
|
||||||
bool set = (event > 32) != 0;
|
|
||||||
system_.ReportPerfArticEvent(ev, set);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
~Apploader_Artic() override;
|
~Apploader_Artic() override;
|
||||||
|
|
||||||
@ -97,7 +88,12 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DoingInitialSetup() override {
|
||||||
|
return is_initial_setup;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr u32 INITIAL_SETUP_APP_VERSION = 0;
|
||||||
/**
|
/**
|
||||||
* Loads .code section into memory for booting
|
* Loads .code section into memory for booting
|
||||||
* @param process The newly created process
|
* @param process The newly created process
|
||||||
@ -105,6 +101,9 @@ private:
|
|||||||
*/
|
*/
|
||||||
ResultStatus LoadExec(std::shared_ptr<Kernel::Process>& process);
|
ResultStatus LoadExec(std::shared_ptr<Kernel::Process>& process);
|
||||||
|
|
||||||
|
ResultStatus LoadExecImpl(std::shared_ptr<Kernel::Process>& process, u64_le program_id,
|
||||||
|
const ExHeader_Header& exheader, std::vector<u8>& code);
|
||||||
|
|
||||||
/// Reads the region lockout info in the SMDH and send it to CFG service
|
/// Reads the region lockout info in the SMDH and send it to CFG service
|
||||||
/// If an SMDH is not present, the program ID is compared against a list
|
/// If an SMDH is not present, the program ID is compared against a list
|
||||||
/// of known system titles to determine the region.
|
/// of known system titles to determine the region.
|
||||||
@ -114,8 +113,12 @@ private:
|
|||||||
|
|
||||||
ResultStatus LoadProductInfo(Service::FS::FS_USER::ProductInfo& out);
|
ResultStatus LoadProductInfo(Service::FS::FS_USER::ProductInfo& out);
|
||||||
|
|
||||||
|
void EnsureClientConnected();
|
||||||
|
|
||||||
ExHeader_Header program_exheader{};
|
ExHeader_Header program_exheader{};
|
||||||
bool program_exheader_loaded = false;
|
bool program_exheader_loaded = false;
|
||||||
|
bool is_initial_setup = false;
|
||||||
|
ArticInitMode artic_init_mode = ArticInitMode::NONE;
|
||||||
|
|
||||||
std::optional<u64> cached_title_id = std::nullopt;
|
std::optional<u64> cached_title_id = std::nullopt;
|
||||||
std::optional<Service::FS::FS_USER::ProductInfo> cached_product_info = std::nullopt;
|
std::optional<Service::FS::FS_USER::ProductInfo> cached_product_info = std::nullopt;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -112,6 +112,12 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileUtil::
|
|||||||
return std::make_unique<AppLoader_NCCH>(system, std::move(file), filepath);
|
return std::make_unique<AppLoader_NCCH>(system, std::move(file), filepath);
|
||||||
|
|
||||||
case FileType::ARTIC: {
|
case FileType::ARTIC: {
|
||||||
|
Apploader_Artic::ArticInitMode mode = Apploader_Artic::ArticInitMode::NONE;
|
||||||
|
if (filename.starts_with("articinio://")) {
|
||||||
|
mode = Apploader_Artic::ArticInitMode::O3DS;
|
||||||
|
} else if (filename.starts_with("articinin://")) {
|
||||||
|
mode = Apploader_Artic::ArticInitMode::N3DS;
|
||||||
|
}
|
||||||
auto strToUInt = [](const std::string& str) -> int {
|
auto strToUInt = [](const std::string& str) -> int {
|
||||||
char* pEnd = NULL;
|
char* pEnd = NULL;
|
||||||
unsigned long ul = ::strtoul(str.c_str(), &pEnd, 10);
|
unsigned long ul = ::strtoul(str.c_str(), &pEnd, 10);
|
||||||
@ -121,7 +127,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileUtil::
|
|||||||
};
|
};
|
||||||
|
|
||||||
u16 port = 5543;
|
u16 port = 5543;
|
||||||
std::string server_addr = filename;
|
std::string server_addr = filename.substr(12);
|
||||||
auto pos = server_addr.find(":");
|
auto pos = server_addr.find(":");
|
||||||
if (pos != server_addr.npos) {
|
if (pos != server_addr.npos) {
|
||||||
int newVal = strToUInt(server_addr.substr(pos + 1));
|
int newVal = strToUInt(server_addr.substr(pos + 1));
|
||||||
@ -130,7 +136,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileUtil::
|
|||||||
server_addr = server_addr.substr(0, pos);
|
server_addr = server_addr.substr(0, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::make_unique<Apploader_Artic>(system, server_addr, port);
|
return std::make_unique<Apploader_Artic>(system, server_addr, port, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -139,9 +145,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileUtil::
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
|
std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
|
||||||
if (filename.starts_with("articbase://")) {
|
if (filename.starts_with("articbase://") || filename.starts_with("articinio://") ||
|
||||||
|
filename.starts_with("articinin://")) {
|
||||||
return GetFileLoader(Core::System::GetInstance(), FileUtil::IOFile(), FileType::ARTIC,
|
return GetFileLoader(Core::System::GetInstance(), FileUtil::IOFile(), FileType::ARTIC,
|
||||||
filename.substr(12), "");
|
filename, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
FileUtil::IOFile file(filename, "rb");
|
FileUtil::IOFile file(filename, "rb");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -275,6 +275,10 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool DoingInitialSetup() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
FileUtil::IOFile file;
|
FileUtil::IOFile file;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -159,9 +159,9 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto now = Clock::now();
|
auto now = Clock::now();
|
||||||
double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
double sleep_scale = Settings::GetFrameLimit() / 100.0;
|
||||||
|
|
||||||
if (Settings::values.frame_limit.GetValue() == 0) {
|
if (Settings::GetFrameLimit() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -1208,4 +1208,82 @@ std::optional<u32> GetSystemTitleRegion(u64 title_id) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<bool, bool> AreSystemTitlesInstalled() {
|
||||||
|
std::array<u32, NUM_SYSTEM_TITLE_REGIONS> o_installed_titles{};
|
||||||
|
std::array<u32, NUM_SYSTEM_TITLE_REGIONS> o_total_titles{};
|
||||||
|
|
||||||
|
std::array<u32, NUM_SYSTEM_TITLE_REGIONS> n_installed_titles{};
|
||||||
|
std::array<u32, NUM_SYSTEM_TITLE_REGIONS> n_total_titles{};
|
||||||
|
|
||||||
|
for (auto categ = system_titles.begin(); categ != system_titles.end(); categ++) {
|
||||||
|
for (auto title = categ->titles.begin(); title != categ->titles.end(); title++) {
|
||||||
|
|
||||||
|
for (u32 i = 0; i < NUM_SYSTEM_TITLE_REGIONS; i++) {
|
||||||
|
if (title->title_id_lows[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u64 program_id =
|
||||||
|
static_cast<u64>(categ->title_id_high) << 32 | title->title_id_lows[i];
|
||||||
|
if (title->sets & SystemTitleSet::Old3ds) {
|
||||||
|
o_total_titles[i]++;
|
||||||
|
}
|
||||||
|
if (title->sets & SystemTitleSet::New3ds) {
|
||||||
|
n_total_titles[i]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(PabloMK7) Switch to a better solution once available, so it's not as slow as
|
||||||
|
// checking everything
|
||||||
|
if (FileUtil::Exists(Service::AM::GetTitleMetadataPath(Service::FS::MediaType::NAND,
|
||||||
|
program_id, false))) {
|
||||||
|
if (title->sets & SystemTitleSet::Old3ds) {
|
||||||
|
o_installed_titles[i]++;
|
||||||
|
}
|
||||||
|
if (title->sets & SystemTitleSet::New3ds) {
|
||||||
|
n_installed_titles[i]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool o_all = false;
|
||||||
|
bool n_all = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < o_installed_titles.size(); i++) {
|
||||||
|
if (o_installed_titles[i] == o_total_titles[i]) {
|
||||||
|
o_all = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < n_installed_titles.size(); i++) {
|
||||||
|
if (n_installed_titles[i] == n_total_titles[i]) {
|
||||||
|
n_all = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(o_all, n_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UninstallSystemFiles(SystemTitleSet set) {
|
||||||
|
for (auto categ = system_titles.begin(); categ != system_titles.end(); categ++) {
|
||||||
|
for (auto title = categ->titles.begin(); title != categ->titles.end(); title++) {
|
||||||
|
if (((set & SystemTitleSet::Old3ds) != 0 &&
|
||||||
|
(title->sets & SystemTitleSet::Old3ds) != 0) ||
|
||||||
|
((set & SystemTitleSet::New3ds) != 0 &&
|
||||||
|
(title->sets & (SystemTitleSet::Old3ds | SystemTitleSet::New3ds)) ==
|
||||||
|
SystemTitleSet::New3ds)) {
|
||||||
|
for (u32 i = 0; i < NUM_SYSTEM_TITLE_REGIONS; i++) {
|
||||||
|
if (title->title_id_lows[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u64 program_id =
|
||||||
|
static_cast<u64>(categ->title_id_high) << 32 | title->title_id_lows[i];
|
||||||
|
Service::AM::UninstallProgram(Service::FS::MediaType::NAND, program_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
@ -26,4 +27,9 @@ std::string GetHomeMenuNcchPath(u32 region);
|
|||||||
/// Returns the region of a system title, if it can be determined.
|
/// Returns the region of a system title, if it can be determined.
|
||||||
std::optional<u32> GetSystemTitleRegion(u64 title_id);
|
std::optional<u32> GetSystemTitleRegion(u64 title_id);
|
||||||
|
|
||||||
|
/// Determines if all system titles are installed for o3ds and n3ds.
|
||||||
|
std::pair<bool, bool> AreSystemTitlesInstalled();
|
||||||
|
|
||||||
|
void UninstallSystemFiles(SystemTitleSet set);
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -43,8 +43,6 @@
|
|||||||
#define closesocket(x) close(x)
|
#define closesocket(x) close(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #define DISABLE_PING_TIMEOUT
|
|
||||||
|
|
||||||
namespace Network::ArticBase {
|
namespace Network::ArticBase {
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
@ -229,7 +227,7 @@ bool Client::Connect() {
|
|||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
hints.ai_family = AF_INET;
|
hints.ai_family = AF_INET;
|
||||||
|
|
||||||
LOG_INFO(Network, "Starting Artic Base Client");
|
LOG_INFO(Network, "Starting Artic Client");
|
||||||
|
|
||||||
if (getaddrinfo(address.data(), NULL, &hints, &addrinfo) != 0) {
|
if (getaddrinfo(address.data(), NULL, &hints, &addrinfo) != 0) {
|
||||||
LOG_ERROR(Network, "Failed to get server address");
|
LOG_ERROR(Network, "Failed to get server address");
|
||||||
@ -273,8 +271,8 @@ bool Client::Connect() {
|
|||||||
shutdown(main_socket, SHUT_RDWR);
|
shutdown(main_socket, SHUT_RDWR);
|
||||||
closesocket(main_socket);
|
closesocket(main_socket);
|
||||||
LOG_ERROR(Network, "Incompatible server version: {}", version_value);
|
LOG_ERROR(Network, "Incompatible server version: {}", version_value);
|
||||||
SignalCommunicationError("\nIncompatible Artic Base Server version.\nCheck for updates "
|
SignalCommunicationError("\nIncompatible Artic Server version.\nCheck for updates "
|
||||||
"to Artic Base Server or Azahar.");
|
"to the Artic Server or Azahar.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -485,15 +483,15 @@ void Client::PingFunction() {
|
|||||||
while (ping_run) {
|
while (ping_run) {
|
||||||
std::chrono::time_point<std::chrono::steady_clock> last = last_sent_request;
|
std::chrono::time_point<std::chrono::steady_clock> last = last_sent_request;
|
||||||
if (std::chrono::steady_clock::now() - last > std::chrono::seconds(7)) {
|
if (std::chrono::steady_clock::now() - last > std::chrono::seconds(7)) {
|
||||||
#ifdef DISABLE_PING_TIMEOUT
|
if (ping_enabled) {
|
||||||
client->last_sent_request = std::chrono::steady_clock::now();
|
auto ping_reply = SendSimpleRequest("PING");
|
||||||
#else
|
if (!ping_reply.has_value()) {
|
||||||
auto ping_reply = SendSimpleRequest("PING");
|
SignalCommunicationError();
|
||||||
if (!ping_reply.has_value()) {
|
break;
|
||||||
SignalCommunicationError();
|
}
|
||||||
break;
|
} else {
|
||||||
|
last_sent_request = std::chrono::steady_clock::now();
|
||||||
}
|
}
|
||||||
#endif // DISABLE_PING_TIMEOUT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock lk(ping_cv_mutex);
|
std::unique_lock lk(ping_cv_mutex);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -161,6 +161,10 @@ public:
|
|||||||
return last_sockaddr_in;
|
return last_sockaddr_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetPingEnabled(bool enable) {
|
||||||
|
ping_enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr const int SERVER_VERSION = 2;
|
static constexpr const int SERVER_VERSION = 2;
|
||||||
|
|
||||||
@ -190,6 +194,7 @@ private:
|
|||||||
std::condition_variable ping_cv;
|
std::condition_variable ping_cv;
|
||||||
std::mutex ping_cv_mutex;
|
std::mutex ping_cv_mutex;
|
||||||
std::atomic<bool> ping_run = true;
|
std::atomic<bool> ping_run = true;
|
||||||
|
bool ping_enabled = true;
|
||||||
|
|
||||||
void StopImpl(bool from_error);
|
void StopImpl(bool from_error);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ void Swapchain::SetPresentMode() {
|
|||||||
}
|
}
|
||||||
// If vsync is enabled attempt to use mailbox mode in case the user wants to speedup/slowdown
|
// If vsync is enabled attempt to use mailbox mode in case the user wants to speedup/slowdown
|
||||||
// the game. If mailbox is not available use immediate and warn about it.
|
// the game. If mailbox is not available use immediate and warn about it.
|
||||||
if (use_vsync && Settings::values.frame_limit.GetValue() > 100) {
|
if (use_vsync && Settings::GetFrameLimit() > 100) {
|
||||||
present_mode = has_mailbox ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate;
|
present_mode = has_mailbox ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate;
|
||||||
if (!has_mailbox) {
|
if (!has_mailbox) {
|
||||||
LOG_WARNING(
|
LOG_WARNING(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user