mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 18:53:32 +00:00
refactor: Improve BootGame error handling and thread initialization
- Add early validation for loader and title ID with critical error logging - Move LoadROM call outside conditional block to always execute after validation - Move emu_thread->start() to after all signal connections are established - Improve code flow and organization for better maintainability Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
@@ -2068,19 +2068,30 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
last_filename_booted = filename;
|
last_filename_booted = filename;
|
||||||
|
|
||||||
ConfigureFilesystemProvider(filename.toStdString());
|
ConfigureFilesystemProvider(filename.toStdString());
|
||||||
|
|
||||||
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
|
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
|
||||||
|
|
||||||
const auto loader = Loader::GetLoader(*system, v_file, params.program_id, params.program_index);
|
const auto loader = Loader::GetLoader(*system, v_file, params.program_id, params.program_index);
|
||||||
|
|
||||||
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
|
if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||||
type == StartGameType::Normal) {
|
// If we can't get a loader or read the title ID, we cannot proceed.
|
||||||
// Load per game settings
|
LOG_CRITICAL(Frontend, "Failed to load game: Could not determine title ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == StartGameType::Normal) {
|
||||||
|
// Load per game settings if it is a normal boot
|
||||||
const auto file_path =
|
const auto file_path =
|
||||||
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
|
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
|
||||||
|
|
||||||
const auto config_file_name = title_id == 0
|
const auto config_file_name = title_id == 0
|
||||||
? Common::FS::PathToUTF8String(file_path.filename())
|
? Common::FS::PathToUTF8String(file_path.filename())
|
||||||
: fmt::format("{:016X}", title_id);
|
: fmt::format("{:016X}", title_id);
|
||||||
|
|
||||||
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
||||||
|
|
||||||
system->HIDCore().ReloadInputDevices();
|
system->HIDCore().ReloadInputDevices();
|
||||||
|
|
||||||
system->ApplySettings();
|
system->ApplySettings();
|
||||||
|
|
||||||
// Final Fantasy Tactics requires single-core mode to boot properly
|
// Final Fantasy Tactics requires single-core mode to boot properly
|
||||||
@@ -2089,6 +2100,8 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
Settings::values.use_multi_core.SetValue(false);
|
Settings::values.use_multi_core.SetValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Settings::LogSettings();
|
Settings::LogSettings();
|
||||||
|
|
||||||
if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) {
|
if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) {
|
||||||
@@ -2098,30 +2111,30 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
.display_options = {},
|
.display_options = {},
|
||||||
.purpose = Service::AM::Frontend::UserSelectionPurpose::General,
|
.purpose = Service::AM::Frontend::UserSelectionPurpose::General,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (SelectAndSetCurrentUser(parameters) == false) {
|
if (SelectAndSetCurrentUser(parameters) == false) {
|
||||||
return;
|
return; // User cancelled profile selection
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user specifies -u (successfully) on the cmd line, don't prompt for a user on first
|
|
||||||
// game startup only. If the user stops emulation and starts a new one, go back to the expected
|
|
||||||
// behavior of asking.
|
|
||||||
user_flag_cmd_line = false;
|
user_flag_cmd_line = false;
|
||||||
|
|
||||||
|
// The core ROM loading logic. If this fails, we must not proceed.
|
||||||
if (!LoadROM(filename, params)) {
|
if (!LoadROM(filename, params)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// This block is only reached if LoadROM returns true.
|
||||||
|
|
||||||
system->SetShuttingDown(false);
|
system->SetShuttingDown(false);
|
||||||
game_list->setDisabled(true);
|
game_list->setDisabled(true);
|
||||||
|
|
||||||
// Create and start the emulation thread
|
// Create and start the emulation thread
|
||||||
emu_thread = std::make_unique<EmuThread>(*system);
|
emu_thread = std::make_unique<EmuThread>(*system);
|
||||||
emit EmulationStarting(emu_thread.get());
|
|
||||||
emu_thread->start();
|
|
||||||
|
|
||||||
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
emit EmulationStarting(emu_thread.get());
|
||||||
|
|
||||||
system->RegisterExecuteProgramCallback(
|
system->RegisterExecuteProgramCallback(
|
||||||
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
|
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
|
||||||
|
|
||||||
@@ -2132,8 +2145,7 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
|
|
||||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||||
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
|
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
|
||||||
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
|
||||||
// before the CPU continues
|
|
||||||
connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
|
connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
|
||||||
&WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
|
&WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
|
||||||
connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget,
|
connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget,
|
||||||
@@ -2142,6 +2154,9 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
|
connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
|
||||||
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
|
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
// Start the thread AFTER all connections are set up
|
||||||
|
emu_thread->start();
|
||||||
|
|
||||||
// Update the GUI
|
// Update the GUI
|
||||||
UpdateStatusButtons();
|
UpdateStatusButtons();
|
||||||
if (ui->action_Single_Window_Mode->isChecked()) {
|
if (ui->action_Single_Window_Mode->isChecked()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user