Major revamps to match game loading decisions.

- Allow downloading titles from eshop and system settings
- Remove encrypted game support
This commit is contained in:
PabloMK7
2025-02-27 14:13:17 +01:00
parent 7a5e1de441
commit dc1ebb63cb
62 changed files with 4860 additions and 1115 deletions

View File

@@ -507,8 +507,8 @@ void GMainWindow::InitializeWidgets() {
emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% "
"indicate emulation is running faster or slower than a 3DS."));
game_fps_label = new QLabel();
game_fps_label->setToolTip(tr("How many frames per second the game is currently displaying. "
"This will vary from game to game and scene to scene."));
game_fps_label->setToolTip(tr("How many frames per second the app is currently displaying. "
"This will vary from app to app and scene to scene."));
emu_frametime_label = new QLabel();
emu_frametime_label->setToolTip(
tr("Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For "
@@ -756,7 +756,7 @@ void GMainWindow::InitializeHotkeys() {
link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot"));
link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot"));
link_action_shortcut(ui->action_View_Lobby,
QStringLiteral("Multiplayer Browse Public Game Lobby"));
QStringLiteral("Multiplayer Browse Public Application Lobby"));
link_action_shortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
link_action_shortcut(ui->action_Connect_To_Room,
QStringLiteral("Multiplayer Direct Connect to Room"));
@@ -779,7 +779,7 @@ void GMainWindow::InitializeHotkeys() {
ToggleFullscreen();
}
});
connect_shortcut(QStringLiteral("Toggle Per-Game Speed"), [&] {
connect_shortcut(QStringLiteral("Toggle Per-Application Speed"), [&] {
Settings::values.frame_limit.SetGlobal(!Settings::values.frame_limit.UsingGlobal());
UpdateStatusBar();
});
@@ -1164,7 +1164,7 @@ void GMainWindow::OnUpdateFound(bool found, bool error) {
}
if (emulation_running && !explicit_update_check) {
LOG_INFO(Frontend, "Update found, deferring as game is running");
LOG_INFO(Frontend, "Update found, deferring as application is running");
defer_update_prompt = true;
return;
}
@@ -1223,7 +1223,7 @@ static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
//: TRANSLATORS: This string is shown to the user to explain why Citra needs to prevent the
//: computer from sleeping
options.insert(QString::fromLatin1("reason"),
QCoreApplication::translate("GMainWindow", "Azahar is running a game"));
QCoreApplication::translate("GMainWindow", "Azahar is running an application"));
// 0x4: Suspend lock; 0x8: Idle lock
QDBusReply<QDBusObjectPath> reply =
xdp.call(QString::fromLatin1("Inhibit"),
@@ -1295,8 +1295,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString());
QMessageBox::critical(
this, tr("Invalid ROM Format"),
tr("Your ROM format is not supported.<br/>Please follow the guides to redump your "
this, tr("Invalid App Format"),
tr("Your app format is not supported.<br/>Please follow the guides to redump your "
"<a "
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
"dumping-game-cartridges/'>game "
@@ -1308,10 +1308,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
break;
case Core::System::ResultStatus::ErrorSystemMode:
LOG_CRITICAL(Frontend, "Failed to load ROM!");
LOG_CRITICAL(Frontend, "Failed to load App!");
QMessageBox::critical(
this, tr("ROM Corrupted"),
tr("Your ROM is corrupted. <br/>Please follow the guides to redump your "
this, tr("App Corrupted"),
tr("Your app is corrupted. <br/>Please follow the guides to redump your "
"<a "
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
"dumping-game-cartridges/'>game "
@@ -1323,23 +1323,17 @@ bool GMainWindow::LoadROM(const QString& filename) {
break;
case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: {
QMessageBox::critical(
this, tr("ROM Encrypted"),
tr("Your ROM is encrypted. <br/>Please follow the guides to redump your "
"<a "
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
"dumping-game-cartridges/'>game "
"cartridges</a> or "
"<a "
"href='https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/"
"dumping-installed-titles/'>installed "
"titles</a>."));
QMessageBox::critical(this, tr("App Encrypted"),
tr("Your app is encrypted. <br/>"
"<a "
"href='https://azahar-emu.org/blog/game-loading-changes/'>"
"Please check our blog for more info.</a>"));
break;
}
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
QMessageBox::critical(
this, tr("Invalid ROM Format"),
tr("Your ROM format is not supported.<br/>Please follow the guides to redump your "
this, tr("Invalid App Format"),
tr("Your app format is not supported.<br/>Please follow the guides to redump your "
"<a "
"href='https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/"
"dumping-game-cartridges/'>game "
@@ -1351,8 +1345,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
break;
case Core::System::ResultStatus::ErrorLoader_ErrorGbaTitle:
QMessageBox::critical(this, tr("Unsupported ROM"),
tr("GBA Virtual Console ROMs are not supported by Azahar."));
QMessageBox::critical(this, tr("Unsupported App"),
tr("GBA Virtual Console is not supported by Azahar."));
break;
case Core::System::ResultStatus::ErrorArticDisconnected:
@@ -1365,7 +1359,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
break;
default:
QMessageBox::critical(
this, tr("Error while loading ROM!"),
this, tr("Error while loading App!"),
tr("An unknown error occurred. Please see the log for more details."));
break;
}
@@ -1430,7 +1424,7 @@ void GMainWindow::BootGame(const QString& filename) {
const std::string name{is_artic ? "" : FileUtil::GetFilename(filename.toStdString())};
const std::string config_file_name =
title_id == 0 ? name : fmt::format("{:016X}", title_id);
LOG_INFO(Frontend, "Loading per game config file for title {}", config_file_name);
LOG_INFO(Frontend, "Loading per application config file for title {}", config_file_name);
QtConfig per_game_config(config_file_name, QtConfig::ConfigType::PerGameConfig);
}
@@ -1932,9 +1926,9 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int message,
switch (message) {
case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_PROMPT:
buttons = QMessageBox::Yes | QMessageBox::No;
result =
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Do you want to launch the game in fullscreen?"), buttons);
result = QMessageBox::information(
parent, tr("Create Shortcut"),
tr("Do you want to launch the application in fullscreen?"), buttons);
return result == QMessageBox::Yes;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
QMessageBox::information(parent, tr("Create Shortcut"),
@@ -2147,7 +2141,7 @@ void GMainWindow::OnGameListAddDirectory() {
UISettings::values.game_dirs.append(game_dir);
game_list->PopulateAsync(UISettings::values.game_dirs);
} else {
LOG_WARNING(Frontend, "Selected directory is already in the game list");
LOG_WARNING(Frontend, "Selected directory is already in the application list");
}
}
@@ -2164,7 +2158,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const QString& file) {
u64 title_id{};
if (!loader || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
QMessageBox::information(this, tr("Properties"),
tr("The game properties could not be loaded."));
tr("The application properties could not be loaded."));
return;
}
@@ -2260,10 +2254,11 @@ void GMainWindow::OnCIAInstallReport(Service::AM::InstallStatus status, QString
QMessageBox::critical(this, tr("Invalid File"), tr("%1 is not a valid CIA").arg(filename));
break;
case Service::AM::InstallStatus::ErrorEncrypted:
QMessageBox::critical(this, tr("Encrypted File"),
tr("%1 must be decrypted "
"before being used with Azahar. A real 3DS is required.")
.arg(filename));
QMessageBox::critical(this, tr("CIA Encrypted"),
tr("Your CIA file is encrypted.<br/>"
"<a "
"href='https://azahar-emu.org/blog/game-loading-changes/'>"
"Please check our blog for more info.</a>"));
break;
case Service::AM::InstallStatus::ErrorFileNotFound:
QMessageBox::critical(this, tr("Unable to find File"),
@@ -2625,9 +2620,10 @@ void GMainWindow::OnLoadState() {
ASSERT(action);
if (UISettings::values.save_state_warning) {
QMessageBox::warning(this, tr("Savestates"),
tr("Warning: Savestates are NOT a replacement for in-game saves, "
"and are not meant to be reliable.\n\nUse at your own risk!"));
QMessageBox::warning(
this, tr("Savestates"),
tr("Warning: Savestates are NOT a replacement for in-application saves, "
"and are not meant to be reliable.\n\nUse at your own risk!"));
UISettings::values.save_state_warning = false;
config->Save();
}
@@ -2709,7 +2705,7 @@ void GMainWindow::OnLoadAmiibo() {
if (!nfc->IsSearchingForAmiibos()) {
QMessageBox::warning(this, tr("Error opening amiibo data file"),
tr("Game is not looking for amiibos."));
tr("Application is not looking for amiibos."));
return;
}
@@ -2859,11 +2855,11 @@ void GMainWindow::OnCaptureScreenshot() {
const bool was_running = emu_thread->IsRunning();
if (was_running ||
(QMessageBox::question(
this, tr("Game will unpause"),
tr("The game will be unpaused, and the next frame will be captured. Is this okay?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)) {
if (was_running || (QMessageBox::question(this, tr("Application will unpause"),
tr("The application will be unpaused, and the next "
"frame will be captured. Is this okay?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) == QMessageBox::Yes)) {
if (was_running) {
OnPauseGame();
}
@@ -3136,7 +3132,7 @@ void GMainWindow::UpdateStatusBar() {
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
.arg(Settings::values.frame_limit.GetValue()));
}
game_fps_label->setText(tr("Game: %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));
if (show_artic_label) {
@@ -3340,7 +3336,8 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
if (can_continue) {
message_box.addButton(tr("Continue"), QMessageBox::RejectRole);
}
QPushButton* abort_button = message_box.addButton(tr("Quit Game"), QMessageBox::AcceptRole);
QPushButton* abort_button =
message_box.addButton(tr("Quit Application"), QMessageBox::AcceptRole);
if (result != Core::System::ResultStatus::ShutdownRequested)
message_box.exec();
@@ -3470,7 +3467,7 @@ bool GMainWindow::ConfirmChangeGame() {
}
auto answer = QMessageBox::question(
this, tr("Azahar"), tr("The game is still running. Would you like to stop emulation?"),
this, tr("Azahar"), tr("The application is still running. Would you like to stop emulation?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
return answer != QMessageBox::No;
}
@@ -3675,8 +3672,8 @@ void GMainWindow::RetranslateStatusBar() {
emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% "
"indicate emulation is running faster or slower than a 3DS."));
game_fps_label->setToolTip(tr("How many frames per second the game is currently displaying. "
"This will vary from game to game and scene to scene."));
game_fps_label->setToolTip(tr("How many frames per second the app is currently displaying. "
"This will vary from app to app and scene to scene."));
emu_frametime_label->setToolTip(
tr("Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For "
"full-speed emulation this should be at most 16.67 ms."));

View File

@@ -17,6 +17,7 @@
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/ptm/ptm.h"
#include "core/hw/aes/key.h"
#include "core/hw/unique_data.h"
#include "core/system_titles.h"
#include "ui_configure_system.h"
@@ -245,7 +246,7 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
this, tr("Select SecureInfo_A/B"), QString(),
tr("SecureInfo_A/B (SecureInfo_A SecureInfo_B);;All Files (*.*)"));
ui->button_secure_info->setEnabled(true);
InstallSecureData(file_path_qtstr.toStdString(), cfg->GetSecureInfoAPath());
InstallSecureData(file_path_qtstr.toStdString(), HW::UniqueData::GetSecureInfoAPath());
});
connect(ui->button_friend_code_seed, &QPushButton::clicked, this, [this] {
ui->button_friend_code_seed->setEnabled(false);
@@ -254,14 +255,23 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
tr("LocalFriendCodeSeed_A/B (LocalFriendCodeSeed_A "
"LocalFriendCodeSeed_B);;All Files (*.*)"));
ui->button_friend_code_seed->setEnabled(true);
InstallSecureData(file_path_qtstr.toStdString(), cfg->GetLocalFriendCodeSeedBPath());
InstallSecureData(file_path_qtstr.toStdString(),
HW::UniqueData::GetLocalFriendCodeSeedBPath());
});
connect(ui->button_ct_cert, &QPushButton::clicked, this, [this] {
ui->button_ct_cert->setEnabled(false);
connect(ui->button_otp, &QPushButton::clicked, this, [this] {
ui->button_otp->setEnabled(false);
const QString file_path_qtstr =
QFileDialog::getOpenFileName(this, tr("Select encrypted OTP file"), QString(),
tr("Binary file (*.bin);;All Files (*.*)"));
ui->button_otp->setEnabled(true);
InstallSecureData(file_path_qtstr.toStdString(), HW::UniqueData::GetOTPPath());
});
connect(ui->button_movable, &QPushButton::clicked, this, [this] {
ui->button_movable->setEnabled(false);
const QString file_path_qtstr = QFileDialog::getOpenFileName(
this, tr("Select CTCert"), QString(), tr("CTCert.bin (*.bin);;All Files (*.*)"));
ui->button_ct_cert->setEnabled(true);
InstallCTCert(file_path_qtstr.toStdString());
this, tr("Select movable.sed"), QString(), tr("Sed file (*.sed);;All Files (*.*)"));
ui->button_movable->setEnabled(true);
InstallSecureData(file_path_qtstr.toStdString(), HW::UniqueData::GetMovablePath());
});
for (u8 i = 0; i < country_names.size(); i++) {
@@ -562,50 +572,39 @@ void ConfigureSystem::InstallSecureData(const std::string& from_path, const std:
if (from.empty() || from == to) {
return;
}
FileUtil::CreateFullPath(to_path);
FileUtil::Copy(from, to);
cfg->InvalidateSecureData();
RefreshSecureDataStatus();
}
void ConfigureSystem::InstallCTCert(const std::string& from_path) {
std::string from =
FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault);
std::string to = FileUtil::SanitizePath(Service::AM::Module::GetCTCertPath(),
FileUtil::DirectorySeparator::PlatformDefault);
if (from.empty() || from == to) {
return;
}
FileUtil::CreateFullPath(to);
FileUtil::Copy(from, to);
HW::UniqueData::InvalidateSecureData();
RefreshSecureDataStatus();
}
void ConfigureSystem::RefreshSecureDataStatus() {
auto status_to_str = [](Service::CFG::SecureDataLoadStatus status) {
auto status_to_str = [](HW::UniqueData::SecureDataLoadStatus status) {
switch (status) {
case Service::CFG::SecureDataLoadStatus::Loaded:
case HW::UniqueData::SecureDataLoadStatus::Loaded:
return "Loaded";
case Service::CFG::SecureDataLoadStatus::NotFound:
case HW::UniqueData::SecureDataLoadStatus::InvalidSignature:
return "Loaded (Invalid Signature)";
case HW::UniqueData::SecureDataLoadStatus::NotFound:
return "Not Found";
case Service::CFG::SecureDataLoadStatus::Invalid:
case HW::UniqueData::SecureDataLoadStatus::Invalid:
return "Invalid";
case Service::CFG::SecureDataLoadStatus::IOError:
case HW::UniqueData::SecureDataLoadStatus::IOError:
return "IO Error";
default:
return "";
}
};
Service::AM::CTCert ct_cert;
ui->label_secure_info_status->setText(
tr((std::string("Status: ") + status_to_str(cfg->LoadSecureInfoAFile())).c_str()));
tr((std::string("Status: ") + status_to_str(HW::UniqueData::LoadSecureInfoA())).c_str()));
ui->label_friend_code_seed_status->setText(
tr((std::string("Status: ") + status_to_str(cfg->LoadLocalFriendCodeSeedBFile())).c_str()));
ui->label_ct_cert_status->setText(
tr((std::string("Status: ") + status_to_str(static_cast<Service::CFG::SecureDataLoadStatus>(
Service::AM::Module::LoadCTCertFile(ct_cert))))
tr((std::string("Status: ") + status_to_str(HW::UniqueData::LoadLocalFriendCodeSeedB()))
.c_str()));
ui->label_otp_status->setText(
tr((std::string("Status: ") + status_to_str(HW::UniqueData::LoadOTP())).c_str()));
ui->label_movable_status->setText(
tr((std::string("Status: ") + status_to_str(HW::UniqueData::LoadMovable())).c_str()));
}
void ConfigureSystem::RetranslateUI() {
@@ -669,40 +668,7 @@ void ConfigureSystem::SetupPerGameUI() {
void ConfigureSystem::DownloadFromNUS() {
ui->button_start_download->setEnabled(false);
const auto mode =
static_cast<Core::SystemTitleSet>(1 << ui->combo_download_set->currentIndex());
const auto region = static_cast<u32>(ui->combo_download_region->currentIndex());
const std::vector<u64> titles = Core::GetSystemTitleIds(mode, region);
QProgressDialog progress(tr("Downloading files..."), tr("Cancel"), 0,
static_cast<int>(titles.size()), this);
progress.setWindowModality(Qt::WindowModal);
QFutureWatcher<void> future_watcher;
QObject::connect(&future_watcher, &QFutureWatcher<void>::finished, &progress,
&QProgressDialog::reset);
QObject::connect(&progress, &QProgressDialog::canceled, &future_watcher,
&QFutureWatcher<void>::cancel);
QObject::connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &progress,
&QProgressDialog::setValue);
auto failed = false;
const auto download_title = [&future_watcher, &failed](const u64& title_id) {
if (Service::AM::InstallFromNus(title_id) != Service::AM::InstallStatus::Success) {
failed = true;
future_watcher.cancel();
}
};
future_watcher.setFuture(QtConcurrent::map(titles, download_title));
progress.exec();
future_watcher.waitForFinished();
if (failed) {
QMessageBox::critical(this, tr("Azahar"), tr("Downloading system files failed."));
} else if (!future_watcher.isCanceled()) {
QMessageBox::information(this, tr("Azahar"), tr("Successfully downloaded system files."));
}
QMessageBox::critical(this, tr("Azahar"), tr("Downloading from NUS has been deprecated."));
ui->button_start_download->setEnabled(true);
}

View File

@@ -53,7 +53,6 @@ private:
void RefreshConsoleID();
void InstallSecureData(const std::string& from_path, const std::string& to_path);
void InstallCTCert(const std::string& from_path);
void RefreshSecureDataStatus();
void SetupPerGameUI();

View File

@@ -629,24 +629,60 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_ct_cert">
<widget class="QLabel" name="label_otp">
<property name="text">
<string>CTCert</string>
<string>OTP</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QWidget" name="ct_cert">
<layout class="QHBoxLayout" name="horizontalLayout_ct_cert">
<widget class="QWidget" name="otp">
<layout class="QHBoxLayout" name="horizontalLayout_otp">
<item>
<widget class="QLabel" name="label_ct_cert_status">
<widget class="QLabel" name="label_otp_status">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_ct_cert">
<widget class="QPushButton" name="button_otp">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Choose</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_movable">
<property name="text">
<string>movable.sed</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QWidget" name="otp">
<layout class="QHBoxLayout" name="horizontalLayout_movable">
<item>
<widget class="QLabel" name="label_movable_status">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_movable">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>

View File

@@ -596,7 +596,7 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
QMenu* uninstall_menu = context_menu.addMenu(tr("Uninstall"));
QAction* uninstall_all = uninstall_menu->addAction(tr("Everything"));
uninstall_menu->addSeparator();
QAction* uninstall_game = uninstall_menu->addAction(tr("Game"));
QAction* uninstall_game = uninstall_menu->addAction(tr("Application"));
QAction* uninstall_update = uninstall_menu->addAction(tr("Update"));
QAction* uninstall_dlc = uninstall_menu->addAction(tr("DLC"));
@@ -736,7 +736,7 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
QMessageBox::StandardButton answer = QMessageBox::question(
this, tr("Azahar"),
tr("Are you sure you want to completely uninstall '%1'?\n\nThis will "
"delete the game if installed, as well as any installed updates or DLC.")
"delete the application if installed, as well as any installed updates or DLC.")
.arg(name),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (answer == QMessageBox::Yes) {
@@ -805,7 +805,7 @@ void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
UISettings::values.game_dirs[selected.data(GameListDir::GameDirRole).toInt()];
QAction* deep_scan = context_menu.addAction(tr("Scan Subfolders"));
QAction* delete_dir = context_menu.addAction(tr("Remove Game Directory"));
QAction* delete_dir = context_menu.addAction(tr("Remove Application Directory"));
deep_scan->setCheckable(true);
deep_scan->setChecked(game_dir.deep_scan);
@@ -885,18 +885,18 @@ void GameList::LoadCompatibilityList() {
QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")};
if (!compat_list.open(QFile::ReadOnly | QFile::Text)) {
LOG_ERROR(Frontend, "Unable to open game compatibility list");
LOG_ERROR(Frontend, "Unable to open application compatibility list");
return;
}
if (compat_list.size() == 0) {
LOG_WARNING(Frontend, "Game compatibility list is empty");
LOG_WARNING(Frontend, "Application compatibility list is empty");
return;
}
const QByteArray content = compat_list.readAll();
if (content.isEmpty()) {
LOG_ERROR(Frontend, "Unable to completely read game compatibility list");
LOG_ERROR(Frontend, "Unable to completely read application compatibility list");
return;
}
@@ -1004,12 +1004,12 @@ void GameList::LoadInterfaceLayout() {
}
const QStringList GameList::supported_file_extensions = {
QStringLiteral("3ds"), QStringLiteral("3dsx"), QStringLiteral("elf"), QStringLiteral("axf"),
QStringLiteral("cci"), QStringLiteral("cxi"), QStringLiteral("app")};
QStringLiteral("3dsx"), QStringLiteral("elf"), QStringLiteral("axf"),
QStringLiteral("cci"), QStringLiteral("cxi"), QStringLiteral("app")};
void GameList::RefreshGameDirectory() {
if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
LOG_INFO(Frontend, "Change detected in the applications directory. Reloading game list.");
PopulateAsync(UISettings::values.game_dirs);
}
}
@@ -1094,7 +1094,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent}
layout->setAlignment(Qt::AlignCenter);
image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200));
text->setText(tr("Double-click to add a new folder to the game list"));
text->setText(tr("Double-click to add a new folder to the application list"));
QFont font = text->font();
font.setPointSize(20);
text->setFont(font);

View File

@@ -163,7 +163,7 @@ public:
GameListItemPath() = default;
GameListItemPath(const QString& game_path, std::span<const u8> smdh_data, u64 program_id,
u64 extdata_id, Service::FS::MediaType media_type) {
u64 extdata_id, Service::FS::MediaType media_type, bool is_encrypted) {
setData(type(), TypeRole);
setData(game_path, FullPathRole);
setData(qulonglong(program_id), ProgramIdRole);
@@ -184,6 +184,9 @@ public:
if (UISettings::values.game_list_icon_size.GetValue() !=
UISettings::GameListIconSize::NoIcon)
setData(GetDefaultIcon(large), Qt::DecorationRole);
if (is_encrypted) {
setData(QObject::tr("Unsupported encrypted application"), TitleRole);
}
return;
}
@@ -262,13 +265,13 @@ public:
};
// clang-format off
static const std::map<QString, CompatStatus> status_data = {
{QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}},
{QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}},
{QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}},
{QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}},
{QStringLiteral("4"), {QStringLiteral("#ff0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}},
{QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
{QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
{QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("App functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}},
{QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("App functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}},
{QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("App functions with major graphical or audio glitches, but app is playable from start to finish with\nworkarounds.")}},
{QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("App functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}},
{QStringLiteral("4"), {QStringLiteral("#ff0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("App is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}},
{QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The app crashes when attempting to startup.")}},
{QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The app has not yet been tested.")}}};
// clang-format on
auto iterator = status_data.find(compatibility);
@@ -445,7 +448,7 @@ public:
int icon_size = IconSizes.at(UISettings::values.game_list_icon_size.GetValue());
setData(QIcon::fromTheme(QStringLiteral("plus")).pixmap(icon_size), Qt::DecorationRole);
setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole);
setData(QObject::tr("Add New Application Directory"), Qt::DisplayRole);
}
int type() const override {

View File

@@ -108,7 +108,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
emit EntryReady(
{
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id,
extdata_id, media_type),
extdata_id, media_type,
res == Loader::ResultStatus::ErrorEncrypted),
new GameListItemCompat(compatibility),
new GameListItemRegion(smdh),
new GameListItem(

View File

@@ -446,7 +446,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Browse Public Game Lobby</string>
<string>Browse Public Application Lobby</string>
</property>
</action>
<action name="action_Start_Room">
@@ -688,7 +688,7 @@
<bool>false</bool>
</property>
<property name="text">
<string>Configure Current Game...</string>
<string>Configure Current Application...</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>