// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include "citra_qt/configuration/configure_system.h" #include "citra_qt/uisettings.h" #include "core/core.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/ptm/ptm.h" #include "core/settings.h" #include "ui_configure_system.h" static const std::array days_in_month = {{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, }}; static const std::array country_names = { "", QT_TRANSLATE_NOOP("ConfigureSystem", "Japan"), "", "", "", "", "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "Anguilla"), QT_TRANSLATE_NOOP("ConfigureSystem", "Antigua and Barbuda"), // 0-9 QT_TRANSLATE_NOOP("ConfigureSystem", "Argentina"), QT_TRANSLATE_NOOP("ConfigureSystem", "Aruba"), QT_TRANSLATE_NOOP("ConfigureSystem", "Bahamas"), QT_TRANSLATE_NOOP("ConfigureSystem", "Barbados"), QT_TRANSLATE_NOOP("ConfigureSystem", "Belize"), QT_TRANSLATE_NOOP("ConfigureSystem", "Bolivia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Brazil"), QT_TRANSLATE_NOOP("ConfigureSystem", "British Virgin Islands"), QT_TRANSLATE_NOOP("ConfigureSystem", "Canada"), QT_TRANSLATE_NOOP("ConfigureSystem", "Cayman Islands"), // 10-19 QT_TRANSLATE_NOOP("ConfigureSystem", "Chile"), QT_TRANSLATE_NOOP("ConfigureSystem", "Colombia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Costa Rica"), QT_TRANSLATE_NOOP("ConfigureSystem", "Dominica"), QT_TRANSLATE_NOOP("ConfigureSystem", "Dominican Republic"), QT_TRANSLATE_NOOP("ConfigureSystem", "Ecuador"), QT_TRANSLATE_NOOP("ConfigureSystem", "El Salvador"), QT_TRANSLATE_NOOP("ConfigureSystem", "French Guiana"), QT_TRANSLATE_NOOP("ConfigureSystem", "Grenada"), QT_TRANSLATE_NOOP("ConfigureSystem", "Guadeloupe"), // 20-29 QT_TRANSLATE_NOOP("ConfigureSystem", "Guatemala"), QT_TRANSLATE_NOOP("ConfigureSystem", "Guyana"), QT_TRANSLATE_NOOP("ConfigureSystem", "Haiti"), QT_TRANSLATE_NOOP("ConfigureSystem", "Honduras"), QT_TRANSLATE_NOOP("ConfigureSystem", "Jamaica"), QT_TRANSLATE_NOOP("ConfigureSystem", "Martinique"), QT_TRANSLATE_NOOP("ConfigureSystem", "Mexico"), QT_TRANSLATE_NOOP("ConfigureSystem", "Montserrat"), QT_TRANSLATE_NOOP("ConfigureSystem", "Netherlands Antilles"), QT_TRANSLATE_NOOP("ConfigureSystem", "Nicaragua"), // 30-39 QT_TRANSLATE_NOOP("ConfigureSystem", "Panama"), QT_TRANSLATE_NOOP("ConfigureSystem", "Paraguay"), QT_TRANSLATE_NOOP("ConfigureSystem", "Peru"), QT_TRANSLATE_NOOP("ConfigureSystem", "Saint Kitts and Nevis"), QT_TRANSLATE_NOOP("ConfigureSystem", "Saint Lucia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Saint Vincent and the Grenadines"), QT_TRANSLATE_NOOP("ConfigureSystem", "Suriname"), QT_TRANSLATE_NOOP("ConfigureSystem", "Trinidad and Tobago"), QT_TRANSLATE_NOOP("ConfigureSystem", "Turks and Caicos Islands"), QT_TRANSLATE_NOOP("ConfigureSystem", "United States"), // 40-49 QT_TRANSLATE_NOOP("ConfigureSystem", "Uruguay"), QT_TRANSLATE_NOOP("ConfigureSystem", "US Virgin Islands"), QT_TRANSLATE_NOOP("ConfigureSystem", "Venezuela"), "", "", "", "", "", "", "", // 50-59 "", "", "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "Albania"), QT_TRANSLATE_NOOP("ConfigureSystem", "Australia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Austria"), QT_TRANSLATE_NOOP("ConfigureSystem", "Belgium"), QT_TRANSLATE_NOOP("ConfigureSystem", "Bosnia and Herzegovina"), QT_TRANSLATE_NOOP("ConfigureSystem", "Botswana"), // 60-69 QT_TRANSLATE_NOOP("ConfigureSystem", "Bulgaria"), QT_TRANSLATE_NOOP("ConfigureSystem", "Croatia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Cyprus"), QT_TRANSLATE_NOOP("ConfigureSystem", "Czech Republic"), QT_TRANSLATE_NOOP("ConfigureSystem", "Denmark"), QT_TRANSLATE_NOOP("ConfigureSystem", "Estonia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Finland"), QT_TRANSLATE_NOOP("ConfigureSystem", "France"), QT_TRANSLATE_NOOP("ConfigureSystem", "Germany"), QT_TRANSLATE_NOOP("ConfigureSystem", "Greece"), // 70-79 QT_TRANSLATE_NOOP("ConfigureSystem", "Hungary"), QT_TRANSLATE_NOOP("ConfigureSystem", "Iceland"), QT_TRANSLATE_NOOP("ConfigureSystem", "Ireland"), QT_TRANSLATE_NOOP("ConfigureSystem", "Italy"), QT_TRANSLATE_NOOP("ConfigureSystem", "Latvia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Lesotho"), QT_TRANSLATE_NOOP("ConfigureSystem", "Liechtenstein"), QT_TRANSLATE_NOOP("ConfigureSystem", "Lithuania"), QT_TRANSLATE_NOOP("ConfigureSystem", "Luxembourg"), QT_TRANSLATE_NOOP("ConfigureSystem", "Macedonia"), // 80-89 QT_TRANSLATE_NOOP("ConfigureSystem", "Malta"), QT_TRANSLATE_NOOP("ConfigureSystem", "Montenegro"), QT_TRANSLATE_NOOP("ConfigureSystem", "Mozambique"), QT_TRANSLATE_NOOP("ConfigureSystem", "Namibia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Netherlands"), QT_TRANSLATE_NOOP("ConfigureSystem", "New Zealand"), QT_TRANSLATE_NOOP("ConfigureSystem", "Norway"), QT_TRANSLATE_NOOP("ConfigureSystem", "Poland"), QT_TRANSLATE_NOOP("ConfigureSystem", "Portugal"), QT_TRANSLATE_NOOP("ConfigureSystem", "Romania"), // 90-99 QT_TRANSLATE_NOOP("ConfigureSystem", "Russia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Serbia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Slovakia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Slovenia"), QT_TRANSLATE_NOOP("ConfigureSystem", "South Africa"), QT_TRANSLATE_NOOP("ConfigureSystem", "Spain"), QT_TRANSLATE_NOOP("ConfigureSystem", "Swaziland"), QT_TRANSLATE_NOOP("ConfigureSystem", "Sweden"), QT_TRANSLATE_NOOP("ConfigureSystem", "Switzerland"), QT_TRANSLATE_NOOP("ConfigureSystem", "Turkey"), // 100-109 QT_TRANSLATE_NOOP("ConfigureSystem", "United Kingdom"), QT_TRANSLATE_NOOP("ConfigureSystem", "Zambia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Zimbabwe"), QT_TRANSLATE_NOOP("ConfigureSystem", "Azerbaijan"), QT_TRANSLATE_NOOP("ConfigureSystem", "Mauritania"), QT_TRANSLATE_NOOP("ConfigureSystem", "Mali"), QT_TRANSLATE_NOOP("ConfigureSystem", "Niger"), QT_TRANSLATE_NOOP("ConfigureSystem", "Chad"), QT_TRANSLATE_NOOP("ConfigureSystem", "Sudan"), QT_TRANSLATE_NOOP("ConfigureSystem", "Eritrea"), // 110-119 QT_TRANSLATE_NOOP("ConfigureSystem", "Djibouti"), QT_TRANSLATE_NOOP("ConfigureSystem", "Somalia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Andorra"), QT_TRANSLATE_NOOP("ConfigureSystem", "Gibraltar"), QT_TRANSLATE_NOOP("ConfigureSystem", "Guernsey"), QT_TRANSLATE_NOOP("ConfigureSystem", "Isle of Man"), QT_TRANSLATE_NOOP("ConfigureSystem", "Jersey"), QT_TRANSLATE_NOOP("ConfigureSystem", "Monaco"), QT_TRANSLATE_NOOP("ConfigureSystem", "Taiwan"), "", // 120-129 "", "", "", "", "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "South Korea"), "", "", "", // 130-139 "", "", "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "Hong Kong"), QT_TRANSLATE_NOOP("ConfigureSystem", "Macau"), "", "", "", "", // 140-149 "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "Indonesia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Singapore"), QT_TRANSLATE_NOOP("ConfigureSystem", "Thailand"), QT_TRANSLATE_NOOP("ConfigureSystem", "Philippines"), QT_TRANSLATE_NOOP("ConfigureSystem", "Malaysia"), "", "", "", // 150-159 QT_TRANSLATE_NOOP("ConfigureSystem", "China"), "", "", "", "", "", "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "United Arab Emirates"), QT_TRANSLATE_NOOP("ConfigureSystem", "India"), // 160-169 QT_TRANSLATE_NOOP("ConfigureSystem", "Egypt"), QT_TRANSLATE_NOOP("ConfigureSystem", "Oman"), QT_TRANSLATE_NOOP("ConfigureSystem", "Qatar"), QT_TRANSLATE_NOOP("ConfigureSystem", "Kuwait"), QT_TRANSLATE_NOOP("ConfigureSystem", "Saudi Arabia"), QT_TRANSLATE_NOOP("ConfigureSystem", "Syria"), QT_TRANSLATE_NOOP("ConfigureSystem", "Bahrain"), QT_TRANSLATE_NOOP("ConfigureSystem", "Jordan"), "", "", // 170-179 "", "", "", "", QT_TRANSLATE_NOOP("ConfigureSystem", "San Marino"), QT_TRANSLATE_NOOP("ConfigureSystem", "Vatican City"), QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 }; // The QSlider doesn't have an easy way to set a custom step amount, // so we can just convert from the sliders range (0 - 79) to the expected // settings range (5 - 400) with simple math. static constexpr int SliderToSettings(int value) { return 5 * value + 5; } static constexpr int SettingsToSlider(int value) { return (value - 5) / 5; } ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); connect(ui->combo_birthmonth, static_cast(&QComboBox::currentIndexChanged), this, &ConfigureSystem::UpdateBirthdayComboBox); connect(ui->combo_init_clock, static_cast(&QComboBox::currentIndexChanged), this, &ConfigureSystem::UpdateInitTime); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, &ConfigureSystem::RefreshConsoleID); for (u8 i = 0; i < country_names.size(); i++) { if (std::strcmp(country_names.at(i), "") != 0) { ui->combo_country->addItem(tr(country_names.at(i)), i); } } // Set a minimum width for the label to prevent the slider from changing size. // This scales across DPIs. (This value should be enough for "xxx%") ui->clock_display_label->setMinimumWidth(40); connect(ui->slider_clock_speed, &QSlider::valueChanged, [&](int value) { ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value))); }); ConfigureTime(); } ConfigureSystem::~ConfigureSystem() = default; void ConfigureSystem::SetConfiguration() { enabled = !Core::System::GetInstance().IsPoweredOn(); ui->combo_init_clock->setCurrentIndex(static_cast(Settings::values.init_clock)); QDateTime date_time; date_time.setTime_t(Settings::values.init_time); ui->edit_init_time->setDateTime(date_time); long long init_time_offset = Settings::values.init_time_offset; long long days_offset = init_time_offset / 86400; ui->edit_init_time_offset_days->setValue(days_offset); unsigned long long time_offset = std::abs(init_time_offset) - std::abs(days_offset * 86400); QTime time = QTime::fromMSecsSinceStartOfDay(time_offset * 1000); ui->edit_init_time_offset_time->setTime(time); if (!enabled) { cfg = Service::CFG::GetModule(Core::System::GetInstance()); ASSERT_MSG(cfg, "CFG Module missing!"); ReadSystemSettings(); ui->group_system_settings->setEnabled(false); } else { // This tab is enabled only when game is not running (i.e. all service are not initialized). cfg = std::make_shared(); ReadSystemSettings(); ui->label_disable_info->hide(); } ui->slider_clock_speed->setValue(SettingsToSlider(Settings::values.cpu_clock_percentage)); ui->clock_display_label->setText( QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage)); ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds); } void ConfigureSystem::ReadSystemSettings() { // set username username = cfg->GetUsername(); // TODO(wwylele): Use this when we move to Qt 5.5 // ui->edit_username->setText(QString::fromStdU16String(username)); ui->edit_username->setText( QString::fromUtf16(reinterpret_cast(username.data()))); // set birthday std::tie(birthmonth, birthday) = cfg->GetBirthday(); ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); UpdateBirthdayComboBox( birthmonth - 1); // explicitly update it because the signal from setCurrentIndex is not reliable ui->combo_birthday->setCurrentIndex(birthday - 1); // set system language language_index = cfg->GetSystemLanguage(); ui->combo_language->setCurrentIndex(language_index); // set sound output mode sound_index = cfg->GetSoundOutputMode(); ui->combo_sound->setCurrentIndex(sound_index); // set the country code country_code = cfg->GetCountryCode(); ui->combo_country->setCurrentIndex(ui->combo_country->findData(country_code)); // set the console id u64 console_id = cfg->GetConsoleUniqueId(); ui->label_console_id->setText( tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); // set play coin play_coin = Service::PTM::Module::GetPlayCoins(); ui->spinBox_play_coins->setValue(play_coin); } void ConfigureSystem::ApplyConfiguration() { if (enabled) { bool modified = false; // apply username // TODO(wwylele): Use this when we move to Qt 5.5 // std::u16string new_username = ui->edit_username->text().toStdU16String(); std::u16string new_username( reinterpret_cast(ui->edit_username->text().utf16())); if (new_username != username) { cfg->SetUsername(new_username); modified = true; } // apply birthday int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1; int new_birthday = ui->combo_birthday->currentIndex() + 1; if (birthmonth != new_birthmonth || birthday != new_birthday) { cfg->SetBirthday(new_birthmonth, new_birthday); modified = true; } // apply language int new_language = ui->combo_language->currentIndex(); if (language_index != new_language) { cfg->SetSystemLanguage(static_cast(new_language)); modified = true; } // apply sound int new_sound = ui->combo_sound->currentIndex(); if (sound_index != new_sound) { cfg->SetSoundOutputMode(static_cast(new_sound)); modified = true; } // apply country u8 new_country = static_cast(ui->combo_country->currentData().toInt()); if (country_code != new_country) { cfg->SetCountryCode(new_country); modified = true; } // apply play coin u16 new_play_coin = static_cast(ui->spinBox_play_coins->value()); if (play_coin != new_play_coin) { Service::PTM::Module::SetPlayCoins(new_play_coin); } // update the config savegame if any item is modified. if (modified) { cfg->UpdateConfigNANDSavegame(); } Settings::values.init_clock = static_cast(ui->combo_init_clock->currentIndex()); Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t(); s64 time_offset_time = ui->edit_init_time_offset_time->time().msecsSinceStartOfDay() / 1000; s64 time_offset_days = ui->edit_init_time_offset_days->value() * 86400; if (time_offset_days < 0) { time_offset_time = -time_offset_time; } Settings::values.init_time_offset = time_offset_days + time_offset_time; Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked(); } Settings::values.cpu_clock_percentage = SliderToSettings(ui->slider_clock_speed->value()); Settings::Apply(); } void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { if (birthmonth_index < 0 || birthmonth_index >= 12) return; // store current day selection int birthday_index = ui->combo_birthday->currentIndex(); // get number of days in the new selected month int days = days_in_month[birthmonth_index]; // if the selected day is out of range, // reset it to 1st if (birthday_index < 0 || birthday_index >= days) birthday_index = 0; // update the day combo box ui->combo_birthday->clear(); for (int i = 1; i <= days; ++i) { ui->combo_birthday->addItem(QString::number(i)); } // restore the day selection ui->combo_birthday->setCurrentIndex(birthday_index); } void ConfigureSystem::ConfigureTime() { QDateTime dt; dt.fromString(QStringLiteral("2000-01-01 00:00:01"), QStringLiteral("yyyy-MM-dd hh:mm:ss")); ui->edit_init_time->setMinimumDateTime(dt); ui->edit_init_time->setCalendarPopup(true); SetConfiguration(); UpdateInitTime(ui->combo_init_clock->currentIndex()); } void ConfigureSystem::UpdateInitTime(int init_clock) { const bool is_fixed_time = static_cast(init_clock) == Settings::InitClock::FixedTime; ui->label_init_time->setVisible(is_fixed_time); ui->edit_init_time->setVisible(is_fixed_time); ui->label_init_time_offset->setVisible(!is_fixed_time); ui->edit_init_time_offset_days->setVisible(!is_fixed_time); ui->edit_init_time_offset_time->setVisible(!is_fixed_time); } void ConfigureSystem::RefreshConsoleID() { QMessageBox::StandardButton reply; QString warning_text = tr("This will replace your current virtual 3DS with a new one. " "Your current virtual 3DS will not be recoverable. " "This might have unexpected effects in games. This might fail, " "if you use an outdated config savegame. Continue?"); reply = QMessageBox::critical(this, tr("Warning"), warning_text, QMessageBox::No | QMessageBox::Yes); if (reply == QMessageBox::No) { return; } const auto [random_number, console_id] = cfg->GenerateConsoleUniqueId(); cfg->SetConsoleUniqueId(random_number, console_id); cfg->UpdateConfigNANDSavegame(); ui->label_console_id->setText( tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); } void ConfigureSystem::RetranslateUI() { ui->retranslateUi(this); }