Added system for handling core errors in citra-qt.

This commit is contained in:
TheKoopaKingdom 2017-03-08 16:28:30 -05:00
parent e523c76cc8
commit 1ecb322daa
9 changed files with 121 additions and 26 deletions

View file

@ -37,7 +37,11 @@ void EmuThread::run() {
if (!was_active) if (!was_active)
emit DebugModeLeft(); emit DebugModeLeft();
Core::System::GetInstance().RunLoop(); Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
if (result != Core::System::ResultStatus::Success) {
emit ErrorThrown(result);
break;
}
was_active = running || exec_step; was_active = running || exec_step;
if (!was_active && !stop_run) if (!was_active && !stop_run)

View file

@ -10,6 +10,7 @@
#include <QGLWidget> #include <QGLWidget>
#include <QThread> #include <QThread>
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/motion_emu.h" #include "core/frontend/motion_emu.h"
@ -97,6 +98,8 @@ signals:
* Qt::BlockingQueuedConnection (additionally block source thread until slot returns) * Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/ */
void DebugModeLeft(); void DebugModeLeft();
void ErrorThrown(Core::System::ResultStatus);
}; };
class GRenderWindow : public QWidget, public EmuWindow { class GRenderWindow : public QWidget, public EmuWindow {

View file

@ -301,8 +301,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while starting Citra!"), QMessageBox::critical(this, tr("Error while starting Citra!"),
tr("Failed to initialize the video core!\n\n" tr("Your GPU may not support OpenGL 3.3, or you do not"
"Please ensure that your GPU supports OpenGL 3.3 and that you "
"have the latest graphics driver.")); "have the latest graphics driver."));
return false; return false;
} }
@ -327,18 +326,17 @@ bool GMainWindow::LoadROM(const QString& filename) {
break; break;
case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: { case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: {
// Build the MessageBox ourselves to have clickable link QMessageBox::critical(
QMessageBox popup_error; this, tr("Error while loading ROM!"),
popup_error.setTextFormat(Qt::RichText);
popup_error.setWindowTitle(tr("Error while loading ROM!"));
popup_error.setText(
tr("The game that you are trying to load must be decrypted before being used with " tr("The game that you are trying to load must be decrypted before being used with "
"Citra.<br/><br/>" "Citra.<br/><br/>"
"For more information on dumping and decrypting games, please see: <a " "For more information on dumping and decrypting games, please see the following "
"href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://" "wiki pages: <ul>"
"citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); "<li><a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges/'>Dumping Game "
popup_error.setIcon(QMessageBox::Critical); "Cartridges</a></li>"
popup_error.exec(); "<li><a href='https://citra-emu.org/wiki/Dumping-Installed-Titles/'>Dumping "
"Installed Titles</a></li>"
"</ul>"));
break; break;
} }
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
@ -346,8 +344,16 @@ bool GMainWindow::LoadROM(const QString& filename) {
tr("The ROM format is not supported.")); tr("The ROM format is not supported."));
break; break;
case Core::System::ResultStatus::ErrorOpenGL:
QMessageBox::critical(this, tr("Error while loading OpenGL!"),
tr("Your GPU may not support OpenGL 3.3, or you do not "
"have the latest graphics driver."));
break;
default: default:
QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!")); QMessageBox::critical(
this, tr("Error while loading ROM!"),
tr("An unknown error occured. Please see the log for more details."));
break; break;
} }
return false; return false;
@ -530,6 +536,9 @@ void GMainWindow::OnMenuRecentFile() {
void GMainWindow::OnStartGame() { void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true); emu_thread->SetRunning(true);
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
connect(emu_thread.get(), SIGNAL(ErrorThrown(Core::System::ResultStatus)), this,
SLOT(OnCoreError(Core::System::ResultStatus)));
ui.action_Start->setEnabled(false); ui.action_Start->setEnabled(false);
ui.action_Start->setText(tr("Continue")); ui.action_Start->setText(tr("Continue"));
@ -622,14 +631,57 @@ void GMainWindow::UpdateStatusBar() {
emu_frametime_label->setVisible(true); emu_frametime_label->setVisible(true);
} }
void GMainWindow::OnCoreError(Core::System::ResultStatus result) {
// Waiting for the dialog to be closed before shutting down causes a segfault, maybe because of
// the profiler
ShutdownGame();
switch (result) {
case Core::System::ResultStatus::ErrorSystemFiles:
QMessageBox::critical(
this, "System Archive Not Found",
"Citra was unable to locate the 3DS system archive.<br/><br/>"
"The game you are trying to load requires additional files from your 3DS to be dumped "
"before playing.<br/><br/>"
"For more information on dumping these files, please see the following wiki page: "
"<a "
"href='https://citra-emu.org/wiki/"
"Dumping-System-Archives-and-the-Shared-Fonts-from-a-3DS-Console/'>Dumping System "
"Archives and the Shared Fonts from a 3DS Console</a>"
".");
break;
case Core::System::ResultStatus::ErrorSharedFont:
QMessageBox::critical(
this, "Shared Fonts Not Found",
"Citra was unable to locate the 3DS shared fonts.<br/><br/>"
"The game you are trying to load requires additional files from your 3DS to be dumped "
"before playing.<br/><br/>"
"For more information on dumping these files, please see the following wiki page: "
"<a "
"href='https://citra-emu.org/wiki/"
"Dumping-System-Archives-and-the-Shared-Fonts-from-a-3DS-Console/'>Dumping System "
"Archives and the Shared Fonts from a 3DS Console</a>"
".");
break;
case Core::System::ResultStatus::ErrorUnknown:
QMessageBox::critical(
this, "Fatal Error",
"Citra has encountered a fatal error, please see the log for more details.");
break;
default:
break;
}
}
bool GMainWindow::ConfirmClose() { bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;
auto answer = return QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"),
QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"), QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No); QMessageBox::No) != QMessageBox::No;
return answer != QMessageBox::No;
} }
void GMainWindow::closeEvent(QCloseEvent* event) { void GMainWindow::closeEvent(QCloseEvent* event) {

View file

@ -125,6 +125,7 @@ private slots:
void OnDisplayTitleBars(bool); void OnDisplayTitleBars(bool);
void ToggleWindowMode(); void ToggleWindowMode();
void OnCreateGraphicsSurfaceViewer(); void OnCreateGraphicsSurfaceViewer();
void OnCoreError(Core::System::ResultStatus);
private: private:
void UpdateStatusBar(); void UpdateStatusBar();

View file

@ -59,7 +59,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
HW::Update(); HW::Update();
Reschedule(); Reschedule();
return ResultStatus::Success; return GetStatus();
} }
System::ResultStatus System::SingleStep() { System::ResultStatus System::SingleStep() {
@ -73,12 +73,22 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str()); LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str());
return ResultStatus::ErrorGetLoader; return ResultStatus::ErrorGetLoader;
} }
boost::optional<u32> system_mode = boost::none;
boost::optional<u32> system_mode{app_loader->LoadKernelSystemMode()}; Loader::ResultStatus load_result{app_loader->LoadKernelSystemMode(system_mode)};
if (!system_mode) { if (!system_mode) {
LOG_CRITICAL(Core, "Failed to determine system mode!"); LOG_CRITICAL(Core, "Failed to determine system mode (Error %i)!", load_result);
System::Shutdown();
switch (load_result) {
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
default:
return ResultStatus::ErrorSystemMode; return ResultStatus::ErrorSystemMode;
} }
}
ResultStatus init_result{Init(emu_window, system_mode.get())}; ResultStatus init_result{Init(emu_window, system_mode.get())};
if (init_result != ResultStatus::Success) { if (init_result != ResultStatus::Success) {
@ -87,7 +97,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
return init_result; return init_result;
} }
const Loader::ResultStatus load_result{app_loader->Load()}; load_result = app_loader->Load();
if (Loader::ResultStatus::Success != load_result) { if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
System::Shutdown(); System::Shutdown();
@ -101,6 +111,8 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
return ResultStatus::ErrorLoader; return ResultStatus::ErrorLoader;
} }
} }
// this->status will be used for errors while actually running the game
status = ResultStatus::Success;
return ResultStatus::Success; return ResultStatus::Success;
} }
@ -142,7 +154,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
GDBStub::Init(); GDBStub::Init();
if (!VideoCore::Init(emu_window)) { if (!VideoCore::Init(emu_window)) {
return ResultStatus::ErrorVideoCore; return ResultStatus::ErrorOpenGL;
} }
LOG_DEBUG(Core, "Initialized OK"); LOG_DEBUG(Core, "Initialized OK");

View file

@ -40,7 +40,11 @@ public:
ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
/// invalid format /// invalid format
ErrorSystemFiles, ///< Error in finding system files
ErrorSharedFont, ///< Error in finding shared font
ErrorVideoCore, ///< Error in the video core ErrorVideoCore, ///< Error in the video core
ErrorOpenGL, ///< Error when initializing OpenGL
ErrorUnknown ///< Any other error
}; };
/** /**
@ -105,6 +109,14 @@ public:
PerfStats perf_stats; PerfStats perf_stats;
FrameLimiter frame_limiter; FrameLimiter frame_limiter;
ResultStatus GetStatus() {
return status;
}
void SetStatus(ResultStatus newStatus) {
status = newStatus;
}
private: private:
/** /**
* Initialize the emulated system. * Initialize the emulated system.
@ -130,6 +142,7 @@ private:
std::unique_ptr<Core::TelemetrySession> telemetry_session; std::unique_ptr<Core::TelemetrySession> telemetry_session;
static System s_instance; static System s_instance;
ResultStatus status;
}; };
inline ARM_Interface& CPU() { inline ARM_Interface& CPU() {

View file

@ -5,6 +5,7 @@
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/applets/applet.h" #include "core/hle/applets/applet.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
@ -74,6 +75,7 @@ void GetSharedFont(Service::Interface* self) {
LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
rb.Push<u32>(-1); // TODO: Find the right error code rb.Push<u32>(-1); // TODO: Find the right error code
rb.Skip(1 + 2, true); rb.Skip(1 + 2, true);
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
return; return;
} }
@ -279,7 +281,8 @@ void CancelParameter(Service::Interface* self) {
rb.Push(RESULT_SUCCESS); // No error rb.Push(RESULT_SUCCESS); // No error
rb.Push(true); // Set to Success rb.Push(true); // Set to Success
LOG_WARNING(Service_APT, "(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, " LOG_WARNING(Service_APT,
"(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, "
"check_receiver=0x%08X, receiver_appid=0x%08X", "check_receiver=0x%08X, receiver_appid=0x%08X",
check_sender, sender_appid, check_receiver, receiver_appid); check_sender, sender_appid, check_receiver, receiver_appid);
} }

View file

@ -10,6 +10,7 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/err_f.h" #include "core/hle/service/err_f.h"
@ -172,6 +173,7 @@ static void ThrowFatalError(Interface* self) {
const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]);
LOG_CRITICAL(Service_ERR, "Fatal error type: %s", LOG_CRITICAL(Service_ERR, "Fatal error type: %s",
GetErrType(errinfo->errinfo_common.specifier).c_str()); GetErrType(errinfo->errinfo_common.specifier).c_str());
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorUnknown);
// Generic Info // Generic Info
LogGenericInfo(errinfo->errinfo_common); LogGenericInfo(errinfo->errinfo_common);

View file

@ -8,6 +8,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/errors.h" #include "core/file_sys/errors.h"
#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/client_session.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -132,6 +133,10 @@ static void OpenFileDirectly(Service::Interface* self) {
LOG_ERROR(Service_FS, LOG_ERROR(Service_FS,
"failed to get a handle for archive archive_id=0x%08X archive_path=%s", "failed to get a handle for archive archive_id=0x%08X archive_path=%s",
static_cast<u32>(archive_id), archive_path.DebugStr().c_str()); static_cast<u32>(archive_id), archive_path.DebugStr().c_str());
if (static_cast<u32>(archive_id) == 0x2345678A) {
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles);
return;
}
cmd_buff[1] = archive_handle.Code().raw; cmd_buff[1] = archive_handle.Code().raw;
cmd_buff[3] = 0; cmd_buff[3] = 0;
return; return;