diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 91149245a..53ca8dd05 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1015,6 +1015,11 @@ bool GMainWindow::LoadROM(const QString& filename) { "titles.")); break; + case Core::System::ResultStatus::ErrorLoader_ErrorGbaTitle: + QMessageBox::critical(this, tr("Unsupported ROM"), + tr("GBA Virtual Console ROMs are not supported by Citra.")); + break; + case Core::System::ResultStatus::ErrorVideoCore: QMessageBox::critical( this, tr("Video Core Error"), diff --git a/src/core/core.cpp b/src/core/core.cpp index c99f17a7f..c6eebade7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -268,6 +268,8 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st return ResultStatus::ErrorLoader_ErrorEncrypted; case Loader::ResultStatus::ErrorInvalidFormat: return ResultStatus::ErrorLoader_ErrorInvalidFormat; + case Loader::ResultStatus::ErrorGbaTitle: + return ResultStatus::ErrorLoader_ErrorGbaTitle; default: return ResultStatus::ErrorSystemMode; } @@ -292,7 +294,6 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st telemetry_session->AddInitialInfo(*app_loader); std::shared_ptr process; const Loader::ResultStatus load_result{app_loader->Load(process)}; - kernel->SetCurrentProcess(process); if (Loader::ResultStatus::Success != load_result) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result); System::Shutdown(); @@ -302,10 +303,13 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st return ResultStatus::ErrorLoader_ErrorEncrypted; case Loader::ResultStatus::ErrorInvalidFormat: return ResultStatus::ErrorLoader_ErrorInvalidFormat; + case Loader::ResultStatus::ErrorGbaTitle: + return ResultStatus::ErrorLoader_ErrorGbaTitle; default: return ResultStatus::ErrorLoader; } } + kernel->SetCurrentProcess(process); cheat_engine = std::make_unique(*this); title_id = 0; if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { @@ -539,7 +543,8 @@ void System::Shutdown(bool is_deserializing) { perf_results.emulation_speed * 100.0); telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps); telemetry_session->AddField(performance, "Shutdown_Frametime", perf_results.frametime * 1000.0); - telemetry_session->AddField(performance, "Mean_Frametime_MS", perf_stats->GetMeanFrametime()); + telemetry_session->AddField(performance, "Mean_Frametime_MS", + perf_stats ? perf_stats->GetMeanFrametime() : 0); // Shutdown emulation session VideoCore::Shutdown(); diff --git a/src/core/core.h b/src/core/core.h index 79cbc3f85..c4e06e2aa 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -82,10 +82,12 @@ public: ErrorSystemMode, ///< Error determining the system mode ErrorLoader, ///< Error loading the specified application ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption - ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an - /// invalid format - ErrorSystemFiles, ///< Error in finding system files - ErrorVideoCore, ///< Error in the video core + ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an + /// invalid format + ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual + ///< Console + ErrorSystemFiles, ///< Error in finding system files + ErrorVideoCore, ///< Error in the video core ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having /// generic drivers installed ErrorVideoCore_ErrorBelowGL43, ///< Error in the video core due to the user not having diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index d42aab3a1..9f50c79d1 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -75,6 +75,7 @@ enum class ResultStatus { ErrorAlreadyLoaded, ErrorMemoryAllocationFailed, ErrorEncrypted, + ErrorGbaTitle, }; constexpr u32 MakeMagic(char a, char b, char c, char d) { diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index e376120a7..d233aef26 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -85,6 +85,11 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr& process) u64_le program_id; if (ResultStatus::Success == ReadCode(code) && ResultStatus::Success == ReadProgramId(program_id)) { + if (IsGbaVirtualConsole(code)) { + LOG_ERROR(Loader, "Encountered unsupported GBA Virtual Console code section."); + return ResultStatus::ErrorGbaTitle; + } + std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( (const char*)overlay_ncch->exheader_header.codeset_info.name, 8); @@ -177,6 +182,12 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() { } } +bool AppLoader_NCCH::IsGbaVirtualConsole(const std::vector& code) { + const u32* gbaVcHeader = reinterpret_cast(code.data() + code.size() - 0x10); + return code.size() >= 0x10 && gbaVcHeader[0] == MakeMagic('.', 'C', 'A', 'A') && + gbaVcHeader[1] == 1; +} + ResultStatus AppLoader_NCCH::Load(std::shared_ptr& process) { u64_le ncch_program_id; diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 6f680b063..76ce61446 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -78,6 +78,9 @@ private: /// Reads the region lockout info in the SMDH and send it to CFG service void ParseRegionLockoutInfo(); + /// Detects whether the NCCH contains GBA Virtual Console. + bool IsGbaVirtualConsole(const std::vector& code); + FileSys::NCCHContainer base_ncch; FileSys::NCCHContainer update_ncch; FileSys::NCCHContainer* overlay_ncch;