From 36c27188a4bc05fa93bd6b21b1c62c1d034610c0 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 13 Jun 2023 22:43:11 +1000 Subject: [PATCH] VMManager: Refactor and improve boot process [SAVEVERSION+] VM struct changes. - Serial/title is now linked to disc, instead of running ELF. - Save states can be created during BIOS boot. - Patches now apply based on the executing CRC, and only after the entry point starts executing (fixes multi-game discs). - Add "Fast Forward Boot" option. - Split achievements download and activation, downloads occur on initialization, but are not activated until after the ELF loads. - Prevent HostFS access while in PS1 mode. - Remove multiple sources of truth for ELF/CRC/etc. - Move ELF state from global scope to VMManager. - Prevent game fixes and hw fixes being active while booting game. - Simplify game update. - Flush recompilers after ELF loads. No point keeping boot code around which gets overwritten. --- pcsx2-gsrunner/Main.cpp | 4 +- pcsx2-qt/MainWindow.cpp | 48 +- pcsx2-qt/MainWindow.h | 12 +- pcsx2-qt/QtHost.cpp | 19 +- pcsx2-qt/QtHost.h | 3 +- pcsx2-qt/Settings/BIOSSettingsWidget.cpp | 12 + pcsx2-qt/Settings/BIOSSettingsWidget.h | 3 + pcsx2-qt/Settings/BIOSSettingsWidget.ui | 21 +- pcsx2/Achievements.cpp | 114 ++- pcsx2/Achievements.h | 4 +- pcsx2/CDVD/CDVD.cpp | 248 +++--- pcsx2/CDVD/CDVD.h | 16 +- pcsx2/CDVD/CDVDcommon.cpp | 27 +- pcsx2/CDVD/CDVDcommon.h | 1 + pcsx2/Config.h | 5 +- pcsx2/Elfheader.cpp | 171 ++-- pcsx2/Elfheader.h | 26 +- pcsx2/GS.cpp | 4 +- pcsx2/GS/Renderers/Common/GSRenderer.cpp | 10 +- .../GS/Renderers/HW/GSTextureReplacements.cpp | 4 +- pcsx2/GSDumpReplayer.cpp | 3 - pcsx2/GameDatabase.cpp | 11 +- pcsx2/GameList.cpp | 13 +- pcsx2/Hotkeys.cpp | 39 +- pcsx2/ImGui/FullscreenUI.cpp | 58 +- pcsx2/ImGui/FullscreenUI.h | 2 +- pcsx2/Interpreter.cpp | 63 +- pcsx2/IopBios.cpp | 14 +- pcsx2/MTGS.cpp | 2 +- pcsx2/Memory.cpp | 8 +- pcsx2/PINE.cpp | 8 +- pcsx2/Patch.cpp | 31 +- pcsx2/Patch.h | 3 +- pcsx2/Pcsx2Config.cpp | 6 +- pcsx2/R5900.cpp | 99 +-- pcsx2/R5900.h | 5 - pcsx2/R5900OpcodeImpl.cpp | 4 +- pcsx2/Recording/InputRecording.cpp | 21 +- pcsx2/Recording/InputRecording.h | 5 - pcsx2/SaveState.cpp | 29 +- pcsx2/SaveState.h | 17 +- pcsx2/System.cpp | 31 - pcsx2/System.h | 3 - pcsx2/USB/usb-lightgun/guncon2.cpp | 2 +- pcsx2/VMManager.cpp | 772 +++++++++++------- pcsx2/VMManager.h | 43 +- pcsx2/ps2/BiosTools.cpp | 94 ++- pcsx2/ps2/BiosTools.h | 21 +- pcsx2/x86/ix86-32/iR5900-32.cpp | 16 +- tests/ctest/core/StubHost.cpp | 4 +- 50 files changed, 1195 insertions(+), 984 deletions(-) diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index c6308324a1..8d65f770b1 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -293,8 +293,8 @@ void Host::OnVMResumed() { } -void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial, - const std::string& game_name, u32 game_crc) +void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path, + const std::string& disc_serial, u32 disc_crc, u32 current_crc) { } diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 838c88b11d..2646f2a10d 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -746,9 +746,9 @@ void MainWindow::updateWindowTitle() { QString suffix(QtHost::GetAppConfigSuffix()); QString main_title(QtHost::GetAppNameAndVersion() + suffix); - QString display_title(m_current_game_name + suffix); + QString display_title(m_current_title + suffix); - if (!s_vm_valid || m_current_game_name.isEmpty()) + if (!s_vm_valid || m_current_title.isEmpty()) display_title = main_title; else if (isRenderingToMain()) main_title = display_title; @@ -920,7 +920,7 @@ bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b return true; // If we don't have a crc, we can't save state. - allow_save_to_state &= (m_current_game_crc != 0); + allow_save_to_state &= (m_current_disc_crc != 0); bool save_state = allow_save_to_state && default_save_to_state; // Only confirm on UI thread because we need to display a msgbox. @@ -1213,15 +1213,15 @@ void MainWindow::onChangeDiscMenuAboutToHide() void MainWindow::onLoadStateMenuAboutToShow() { m_ui.menuLoadState->clear(); - updateSaveStateMenusEnableState(!m_current_game_serial.isEmpty()); - populateLoadStateMenu(m_ui.menuLoadState, m_current_disc_path, m_current_game_serial, m_current_game_crc); + updateSaveStateMenusEnableState(!m_current_disc_serial.isEmpty()); + populateLoadStateMenu(m_ui.menuLoadState, m_current_disc_path, m_current_disc_serial, m_current_disc_crc); } void MainWindow::onSaveStateMenuAboutToShow() { m_ui.menuSaveState->clear(); - updateSaveStateMenusEnableState(!m_current_game_serial.isEmpty()); - populateSaveStateMenu(m_ui.menuSaveState, m_current_game_serial, m_current_game_crc); + updateSaveStateMenusEnableState(!m_current_disc_serial.isEmpty()); + populateSaveStateMenu(m_ui.menuSaveState, m_current_disc_serial, m_current_disc_crc); } void MainWindow::onViewToolbarActionToggled(bool checked) @@ -1269,23 +1269,29 @@ void MainWindow::onViewGamePropertiesActionTriggered() return; // prefer to use a game list entry, if we have one, that way the summary is populated - if (!m_current_disc_path.isEmpty() || !m_current_elf_override.isEmpty()) + if (!m_current_disc_path.isEmpty() && m_current_elf_override.isEmpty()) { auto lock = GameList::GetLock(); - const GameList::Entry* entry = m_current_elf_override.isEmpty() ? - GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData()) : - GameList::GetEntryForPath(m_current_elf_override.toUtf8().constData()); + const GameList::Entry* entry = GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData()); if (entry) { - SettingsDialog::openGamePropertiesDialog( - entry, m_current_elf_override.isEmpty() ? std::string_view(entry->serial) : std::string_view(), entry->crc); + SettingsDialog::openGamePropertiesDialog(entry, entry->serial, entry->crc); return; } } // open properties for the current running file (isn't in the game list) - if (m_current_game_crc != 0) - SettingsDialog::openGamePropertiesDialog(nullptr, m_current_game_serial.toStdString(), m_current_game_crc); + if (m_current_disc_crc == 0) + { + QMessageBox::critical(this, tr("Game Properties"), tr("Game properties is unavailable for the current game.")); + return; + } + + // can't use serial for ELFs, because they might have a disc set + if (m_current_elf_override.isEmpty()) + SettingsDialog::openGamePropertiesDialog(nullptr, m_current_disc_serial.toStdString(), m_current_disc_crc); + else + SettingsDialog::openGamePropertiesDialog(nullptr, std::string_view(), m_current_disc_crc); } void MainWindow::onGitHubRepositoryActionTriggered() @@ -1602,13 +1608,15 @@ void MainWindow::onVMStopped() m_game_list_widget->refresh(false); } -void MainWindow::onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc) +void MainWindow::onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path, + const QString& serial, quint32 disc_crc, quint32 crc) { - m_current_disc_path = path; + m_current_title = title; m_current_elf_override = elf_override; - m_current_game_serial = serial; - m_current_game_name = name; - m_current_game_crc = crc; + m_current_disc_path = disc_path; + m_current_disc_serial = serial; + m_current_disc_crc = disc_crc; + m_current_running_crc = crc; updateWindowTitle(); updateSaveStateMenusEnableState(!serial.isEmpty()); } diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 312d4920c0..6eea22df28 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -179,7 +179,8 @@ private Q_SLOTS: void onVMResumed(); void onVMStopped(); - void onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc); + void onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path, + const QString& serial, quint32 disc_crc, quint32 crc); protected: void showEvent(QShowEvent* event) override; @@ -279,11 +280,12 @@ private: QMenu* m_settings_toolbar_menu = nullptr; - QString m_current_disc_path; + QString m_current_title; QString m_current_elf_override; - QString m_current_game_serial; - QString m_current_game_name; - quint32 m_current_game_crc; + QString m_current_disc_path; + QString m_current_disc_serial; + quint32 m_current_disc_crc; + quint32 m_current_running_crc; bool m_display_created = false; bool m_relative_mouse_mode = false; diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index fd2899ea72..16e8d371e8 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -667,14 +667,7 @@ void EmuThread::reloadPatches() return; } - if (!VMManager::HasValidVM()) - return; - - Patch::ReloadPatches(true, false, true, true); - - // Might change widescreen mode. - if (Patch::ReloadPatchAffectingOptions()) - applySettings(); + VMManager::ReloadPatches(true, false, true, true); } void EmuThread::reloadInputSources() @@ -968,11 +961,11 @@ void Host::OnVMResumed() emit g_emu_thread->onVMResumed(); } -void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial, - const std::string& game_name, u32 game_crc) +void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path, + const std::string& disc_serial, u32 disc_crc, u32 current_crc) { - emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(elf_override), - QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc); + emit g_emu_thread->onGameChanged(QString::fromStdString(title), QString::fromStdString(elf_override), + QString::fromStdString(disc_path), QString::fromStdString(disc_serial), disc_crc, current_crc); } void EmuThread::updatePerformanceMetrics(bool force) @@ -1124,7 +1117,7 @@ void Host::VSyncOnCPUThread() void Host::RunOnCPUThread(std::function function, bool block /* = false */) { - if (g_emu_thread->isOnEmuThread()) + if (block && g_emu_thread->isOnEmuThread()) { // probably shouldn't ever happen, but just in case.. function(); diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index 45676e40a6..9d029c62c2 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -137,7 +137,8 @@ Q_SIGNALS: void onVMStopped(); /// Provided by the host; called when the running executable changes. - void onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc); + void onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path, + const QString& serial, quint32 disc_crc, quint32 crc); void onInputDevicesEnumerated(const QList>& devices); void onInputDeviceConnected(const QString& identifier, const QString& device_name); diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp index c9569effcd..67aaaacdda 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp @@ -30,23 +30,29 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent) : QWidget(parent) + , m_dialog(dialog) { SettingsInterface* sif = dialog->getSettingsInterface(); m_ui.setupUi(this); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBootFastForward, "EmuCore", "EnableFastBootFastForward", false); SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory, m_ui.resetSearchDirectory, "Folders", "Bios", Path::Combine(EmuFolders::DataRoot, "bios")); dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Checked"), tr("Patches the BIOS to skip the console's boot animation.")); + dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Forward Boot"), tr("Unchecked"), + tr("Removes emulation speed throttle until the game starts to reduce startup time.")); + refreshList(); connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList); connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList); connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged); + connect(m_ui.fastBoot, &QCheckBox::stateChanged, this, &BIOSSettingsWidget::fastBootChanged); } BIOSSettingsWidget::~BIOSSettingsWidget() @@ -139,6 +145,12 @@ void BIOSSettingsWidget::listItemChanged(const QTreeWidgetItem* current, const Q g_emu_thread->applySettings(); } +void BIOSSettingsWidget::fastBootChanged() +{ + const bool enabled = m_dialog->getEffectiveBoolValue("EmuCore", "EnableFastBoot", true); + m_ui.fastBootFastForward->setEnabled(enabled); +} + BIOSSettingsWidget::RefreshThread::RefreshThread(BIOSSettingsWidget* parent, const QString& directory) : QThread(parent) , m_parent(parent) diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.h b/pcsx2-qt/Settings/BIOSSettingsWidget.h index 0e7c2c2314..c185cef5d7 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.h +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.h @@ -52,8 +52,11 @@ private Q_SLOTS: void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous); void listRefreshed(const QVector& items); + void fastBootChanged(); + private: Ui::BIOSSettingsWidget m_ui; + SettingsDialog* m_dialog; class RefreshThread final : public QThread { diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.ui b/pcsx2-qt/Settings/BIOSSettingsWidget.ui index c5d888419b..b9eeeac01c 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.ui +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.ui @@ -136,11 +136,22 @@ - - - Fast Boot - - + + + + + Fast Boot + + + + + + + Fast Forward Boot + + + + diff --git a/pcsx2/Achievements.cpp b/pcsx2/Achievements.cpp index 713def044e..c7b274282d 100644 --- a/pcsx2/Achievements.cpp +++ b/pcsx2/Achievements.cpp @@ -92,8 +92,7 @@ namespace Achievements static unsigned PeekMemoryBlock(unsigned address, unsigned char* buffer, unsigned num_bytes); static void PokeMemory(unsigned address, unsigned num_bytes, void* ud, unsigned value); static bool IsMastered(); - static void ActivateLockedAchievements(); - static bool ActivateAchievement(Achievement* achievement); + static void ActivateAchievementsAndLeaderboards(); static void DeactivateAchievement(Achievement* achievement); static void SendPing(); static void SendPlaying(); @@ -150,7 +149,8 @@ namespace Achievements static std::string s_username; static std::string s_api_token; - static u32 s_last_game_crc; + static u32 s_last_disc_crc; + static u32 s_current_crc; static std::string s_game_path; static std::string s_game_hash; static std::string s_game_title; @@ -379,7 +379,8 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard while (!s_leaderboards.empty()) { Leaderboard& lb = s_leaderboards.back(); - rc_runtime_deactivate_lboard(&s_rcheevos_runtime, lb.id); + if (lb.active) + rc_runtime_deactivate_lboard(&s_rcheevos_runtime, lb.id); s_leaderboards.pop_back(); } @@ -406,7 +407,7 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard void Achievements::ClearGameHash() { - s_last_game_crc = 0; + s_last_disc_crc = 0; std::string().swap(s_game_hash); } @@ -515,7 +516,7 @@ void Achievements::Initialize() s_logged_in = (!s_username.empty() && !s_api_token.empty()); if (VMManager::HasValidVM()) - GameChanged(VMManager::GetGameCRC()); + GameChanged(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC()); } void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_config) @@ -645,7 +646,13 @@ void Achievements::SetChallengeMode(bool enabled) achievement.locked = true; } for (Leaderboard& leaderboard : s_leaderboards) - rc_runtime_deactivate_lboard(&s_rcheevos_runtime, leaderboard.id); + { + if (leaderboard.active) + { + rc_runtime_deactivate_lboard(&s_rcheevos_runtime, leaderboard.id); + leaderboard.active = false; + } + } } // re-grab unlocks, this will reactivate what's locked in non-hardcore mode later on @@ -767,8 +774,8 @@ void Achievements::LoadState(const u8* state_data, u32 state_data_size) return; // this assumes that the CRC and ELF name has been loaded prior to the cheevos state (it should be). - if (ElfCRC != s_last_game_crc) - GameChanged(ElfCRC); + if (VMManager::GetDiscCRC() != s_last_disc_crc) + GameChanged(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC()); #ifdef ENABLE_RAINTEGRATION if (IsUsingRAIntegration()) @@ -1118,7 +1125,7 @@ void Achievements::GetUserUnlocksCallback(s32 status_code, const std::string& co } // start scanning for locked achievements - ActivateLockedAchievements(); + ActivateAchievementsAndLeaderboards(); DisplayAchievementSummary(); SendPlaying(); UpdateRichPresence(); @@ -1227,16 +1234,10 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten lboard.id = defn.id; lboard.title = defn.title; lboard.description = defn.description; + lboard.memaddr = defn.definition; lboard.format = defn.format; + lboard.active = false; s_leaderboards.push_back(std::move(lboard)); - - // Always track the leaderboard, if we don't have leaderboards enabled we just won't submit it. - // That way if someone activates them later on, current progress will count. - const int err = rc_runtime_activate_lboard(&s_rcheevos_runtime, defn.id, defn.definition, nullptr, 0); - if (err != RC_OK) - Console.Error("Leaderboard %u memaddr parse error: %s", defn.id, rc_error_str(err)); - else - DevCon.WriteLn("Activated leaderboard %s (%u)", defn.title, defn.id); } // Parse rich presence. @@ -1265,7 +1266,7 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten } else { - ActivateLockedAchievements(); + ActivateAchievementsAndLeaderboards(); DisplayAchievementSummary(); Host::OnAchievementsRefreshed(); } @@ -1399,7 +1400,7 @@ std::optional> Achievements::ReadELFFromCurrentDisc(const std::s std::string Achievements::GetGameHash() { - const std::string& elf_path = LastELF; + const std::string elf_path = VMManager::GetDiscELF(); if (elf_path.empty()) return {}; @@ -1457,15 +1458,21 @@ void Achievements::GetGameIdCallback(s32 status_code, const std::string& content GetPatches(game_id); } -void Achievements::GameChanged(u32 crc) +void Achievements::GameChanged(u32 disc_crc, u32 crc) { std::unique_lock lock(s_achievements_mutex); if (!s_active) return; // avoid reading+hashing the executable if the crc hasn't changed - if (s_last_game_crc == crc) + if (s_last_disc_crc == disc_crc) + { + // but we might've just finished booting the game, in which case we need to activate. + if (crc != 0) + ActivateAchievementsAndLeaderboards(); + return; + } std::string game_hash(GetGameHash()); if (s_game_hash == game_hash) @@ -1481,7 +1488,8 @@ void Achievements::GameChanged(u32 crc) ClearGameInfo(); ClearGameHash(); - s_last_game_crc = crc; + s_last_disc_crc = disc_crc; + s_current_crc = crc; s_game_hash = std::move(game_hash); #ifdef ENABLE_RAINTEGRATION @@ -1495,13 +1503,14 @@ void Achievements::GameChanged(u32 crc) if (s_game_hash.empty()) { // when we're booting the bios, or shutting down, this will fail - if (crc != 0) + if (disc_crc != 0) { Host::AddKeyedOSDMessage("retroachievements_disc_read_failed", "Failed to read executable from disc. Achievements disabled.", Host::OSD_CRITICAL_ERROR_DURATION); } - s_last_game_crc = 0; + s_last_disc_crc = 0; + s_current_crc = crc; return; } @@ -1733,31 +1742,48 @@ bool Achievements::IsMastered() return true; } -void Achievements::ActivateLockedAchievements() +void Achievements::ActivateAchievementsAndLeaderboards() { + if (!VMManager::Internal::HasBootedELF()) + { + Console.Warning("Deferring achievement activate until ELF has booted."); + return; + } + for (Achievement& cheevo : s_achievements) { - if (cheevo.locked) - ActivateAchievement(&cheevo); + if (cheevo.active || !cheevo.locked) + continue; + + const int err = + rc_runtime_activate_achievement(&s_rcheevos_runtime, cheevo.id, cheevo.memaddr.c_str(), nullptr, 0); + if (err != RC_OK) + { + Console.Error("Achievement %u memaddr parse error: %s", cheevo.id, rc_error_str(err)); + continue; + } + + DevCon.WriteLn("Activated achievement %s (%u)", cheevo.title.c_str(), cheevo.id); + cheevo.active = true; } -} -bool Achievements::ActivateAchievement(Achievement* achievement) -{ - if (achievement->active) - return true; - - const int err = rc_runtime_activate_achievement(&s_rcheevos_runtime, achievement->id, achievement->memaddr.c_str(), nullptr, 0); - if (err != RC_OK) + for (Leaderboard& lb : s_leaderboards) { - Console.Error("Achievement %u memaddr parse error: %s", achievement->id, rc_error_str(err)); - return false; + // Always track the leaderboard, if we don't have leaderboards enabled we just won't submit it. + // That way if someone activates them later on, current progress will count. + if (lb.active) + continue; + + const int err = rc_runtime_activate_lboard(&s_rcheevos_runtime, lb.id, lb.memaddr.c_str(), nullptr, 0); + if (err != RC_OK) + { + Console.Error("Leaderboard %u memaddr parse error: %s", lb.id, rc_error_str(err)); + continue; + } + + DevCon.WriteLn("Activated leaderboard %s (%u)", lb.title.c_str(), lb.id); + lb.active = true; } - - achievement->active = true; - - DevCon.WriteLn("Activated achievement %s (%u)", achievement->title.c_str(), achievement->id); - return true; } void Achievements::DeactivateAchievement(Achievement* achievement) @@ -2249,7 +2275,7 @@ void Achievements::RAIntegration::RACallbackRebuildMenu() void Achievements::RAIntegration::RACallbackEstimateTitle(char* buf) { - std::string title(fmt::format("{0} ({1}) [{2:08X}]", VMManager::GetGameName(), VMManager::GetGameSerial(), VMManager::GetGameCRC())); + std::string title(fmt::format("{0} ({1}) [{2:08X}]", VMManager::GetTitle(), VMManager::GetDiscSerial(), VMManager::GetDiscCRC())); StringUtil::Strlcpy(buf, title, 256); } diff --git a/pcsx2/Achievements.h b/pcsx2/Achievements.h index c5c8bbc322..7c60fa8804 100644 --- a/pcsx2/Achievements.h +++ b/pcsx2/Achievements.h @@ -62,7 +62,9 @@ namespace Achievements u32 id; std::string title; std::string description; + std::string memaddr; int format; + bool active; }; struct LeaderboardEntry @@ -131,7 +133,7 @@ namespace Achievements bool Login(const char* username, const char* password); void Logout(); - void GameChanged(u32 crc); + void GameChanged(u32 disc_crc, u32 crc); const std::string& GetGameTitle(); const std::string& GetGameIcon(); diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index c4f0ab290b..d432c2faca 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -40,12 +40,6 @@ #include "Recording/InputRecording.h" #include "Host.h" -// This typically reflects the Sony-assigned serial code for the Disc, if one exists. -// (examples: SLUS-2113, etc). -// If the disc is homebrew then it probably won't have a valid serial; in which case -// this string will be empty. -std::string DiscSerial; - cdvdStruct cdvd; s64 PSXCLK = 36864000; @@ -389,13 +383,13 @@ s32 cdvdWriteConfig(const u8* config) } // Sets ElfCRC to the CRC of the game bound to the CDVD source. -static __fi ElfObject* loadElf(std::string filename, bool isPSXElf) +std::unique_ptr cdvdLoadElf(std::string filename, bool isPSXElf) { if (StringUtil::StartsWith(filename, "host:")) { std::string host_filename(filename.substr(5)); s64 host_size = FileSystem::GetPathFileSize(host_filename.c_str()); - return new ElfObject(std::move(host_filename), static_cast(std::max(host_size, 0)), isPSXElf); + return std::make_unique(std::move(host_filename), static_cast(std::max(host_size, 0)), isPSXElf); } // Mimic PS2 behavior! @@ -420,31 +414,13 @@ static __fi ElfObject* loadElf(std::string filename, bool isPSXElf) filename += ";1"; } + // Fix cdrom:path, the iso reader doesn't like it. + if (StringUtil::StartsWith(filename, "cdrom:") && filename[6] != '\\' && filename[6] != '/') + filename.insert(6, 1, '\\'); + IsoFSCDVD isofs; IsoFile file(isofs, filename); - return new ElfObject(std::move(filename), file, isPSXElf); -} - -static __fi void _reloadElfInfo(std::string elfpath) -{ - // Now's a good time to reload the ELF info... - if (elfpath == LastELF) - return; - - std::unique_ptr elfptr(loadElf(elfpath, false)); - elfptr->loadHeaders(); - ElfCRC = elfptr->getCRC(); - ElfEntry = elfptr->header.e_entry; - ElfTextRange = elfptr->getTextRange(); - LastELF = std::move(elfpath); - - Console.WriteLn(Color_StrongBlue, "ELF (%s) Game CRC = 0x%08X, EntryPoint = 0x%08X", LastELF.c_str(), ElfCRC, ElfEntry); - - // Note: Do not load game database info here. This code is generic and called from - // BIOS key encryption as well as eeloadReplaceOSDSYS. The first is actually still executing - // BIOS code, and patches and cheats should not be applied yet. (they are applied when - // eeGameStarting is invoked, which is when the VM starts executing the actual game ELF - // binary). + return std::make_unique(std::move(filename), file, isPSXElf); } u32 cdvdGetElfCRC(const std::string& path) @@ -466,6 +442,84 @@ u32 cdvdGetElfCRC(const std::string& path) } } +// return value: +// 0 - Invalid or unknown disc. +// 1 - PS1 CD +// 2 - PS2 CD +static CDVDDiscType GetPS2ElfName(std::string* name, std::string* version) +{ + CDVDDiscType retype = CDVDDiscType::Other; + name->clear(); + version->clear(); + + try { + IsoFSCDVD isofs; + IsoFile file( isofs, "SYSTEM.CNF;1"); + + int size = file.getLength(); + if( size == 0 ) return CDVDDiscType::Other; + + while( !file.eof() ) + { + const std::string line(file.readLine()); + std::string_view key, value; + if (!StringUtil::ParseAssignmentString(line, &key, &value)) + continue; + + if( value.empty() && file.getLength() != file.getSeekPos() ) + { // Some games have a character on the last line of the file, don't print the error in those cases. + Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" ); + Console.Indent().WriteLn(line); + continue; + } + + if( key == "BOOT2" ) + { + Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PS2 Disc = %.*s", + static_cast(value.size()), value.data()); + *name = value; + retype = CDVDDiscType::PS2Disc; + } + else if( key == "BOOT" ) + { + Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PSX/PSone Disc = %.*s", + static_cast(value.size()), value.data()); + *name = value; + retype = CDVDDiscType::PS1Disc; + } + else if( key == "VMODE" ) + { + Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Disc region type = %.*s", + static_cast(value.size()), value.data()); + } + else if( key == "VER" ) + { + Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Software version = %.*s", + static_cast(value.size()), value.data()); + *version = value; + } + } + + if( retype == CDVDDiscType::Other ) + { + Console.Error("(GetElfName) Disc image is *not* a PlayStation or PS2 game!"); + return CDVDDiscType::Other; + } + } + catch( Exception::FileNotFound& ) + { + //Console.Warning(ex.FormatDiagnosticMessage()); + return CDVDDiscType::Other; // no SYSTEM.CNF, not a PS1/PS2 disc. + } + catch (Exception::BadStream& ex) + { + Console.Error(ex.FormatDiagnosticMessage()); + return CDVDDiscType::Other; // ISO error + } + + return retype; +} + static std::string ExecutablePathToSerial(const std::string& path) { // cdrom:\SCES_123.45;1 @@ -478,7 +532,7 @@ static std::string ExecutablePathToSerial(const std::string& path) else { // cdrom:SCES_123.45;1 - pos = serial.rfind(':'); + pos = path.rfind(':'); if (pos != std::string::npos) serial = path.substr(pos + 1); else @@ -492,8 +546,11 @@ static std::string ExecutablePathToSerial(const std::string& path) // check that it matches our expected format. // this maintains the old behavior of PCSX2. - if (!StringUtil::WildcardMatch(serial.c_str(), "????_???.??*")) + if (!StringUtil::WildcardMatch(serial.c_str(), "????_???.??*") && + !StringUtil::WildcardMatch(serial.c_str(), "????""-???.??*")) // double quote because trigraphs + { serial.clear(); + } // SCES_123.45 -> SCES-12345 for (std::string::size_type pos = 0; pos < serial.size();) @@ -515,60 +572,62 @@ static std::string ExecutablePathToSerial(const std::string& path) return serial; } -void cdvdReloadElfInfo(std::string elfoverride) +void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc, + CDVDDiscType* out_disc_type) { - // called from context of executing VM code (recompilers), so we need to trap exceptions - // and route them through the VM's exception handler. (needed for non-SEH platforms, such - // as Linux/GCC) - DevCon.WriteLn(Color_Green, "Reload ELF"); - try - { - std::string elfpath; - u32 discType = GetPS2ElfName(elfpath); - DiscSerial = ExecutablePathToSerial(elfpath); + std::string elfpath, version; + const CDVDDiscType disc_type = GetPS2ElfName(&elfpath, &version); - // Use the serial from the disc (if any), and the ELF CRC of the override. - if (!elfoverride.empty()) + // Don't bother parsing it if we don't need the CRC. + if (out_crc) + { + u32 crc = 0; + + if (disc_type == CDVDDiscType::PS2Disc || disc_type == CDVDDiscType::PS1Disc) { - _reloadElfInfo(std::move(elfoverride)); - return; + try + { + const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc); + std::unique_ptr elfptr(cdvdLoadElf(elfpath, isPSXElf)); + elfptr->loadHeaders(); + crc = elfptr->getCRC(); + } + catch ([[maybe_unused]] Exception::FileNotFound& e) + { + Console.Error(fmt::format("Failed to load ELF info for {}", elfpath)); + } + catch (Exception::BadStream& ex) + { + Console.Error(ex.FormatDiagnosticMessage()); + } } - if (discType == 1) - { - // PCSX2 currently only recognizes *.elf executables in proper PS2 format. - // To support different PSX titles in the console title and for savestates, this code bypasses all the detection, - // simply using the exe name, stripped of problematic characters. - return; - } - - // Isn't a disc we recognize? - if (discType == 0) - return; - - // Recognized and PS2 (BOOT2). Good job, user. - _reloadElfInfo(std::move(elfpath)); + *out_crc = crc; } - catch ([[maybe_unused]] Exception::FileNotFound& e) + + if (out_serial) { - Console.Error("Failed to load ELF info"); - LastELF.clear(); - DiscSerial.clear(); - ElfCRC = 0; - ElfEntry = 0; - ElfTextRange = {}; - return; + if (disc_type != CDVDDiscType::Other) + *out_serial = ExecutablePathToSerial(elfpath); + else + out_serial->clear(); } + if (out_elf_path) + *out_elf_path = std::move(elfpath); + if (out_version) + *out_version = std::move(version); + if (out_disc_type) + *out_disc_type = disc_type; } void cdvdReadKey(u8, u16, u32 arg2, u8* key) { + const std::string DiscSerial = VMManager::GetDiscSerial(); + s32 numbers = 0, letters = 0; u32 key_0_3; u8 key_4, key_14; - cdvdReloadElfInfo(); - // clear key values memset(key, 0, 16); @@ -697,7 +756,7 @@ s32 cdvdCtrlTrayClose() DevCon.WriteLn(Color_Green, "Close virtual disk tray"); - if (!g_GameStarted && g_SkipBiosHack) + if (VMManager::Internal::IsFastBootInProgress()) { DevCon.WriteLn(Color_Green, "Media already loaded (fast boot)"); cdvdUpdateReady(CDVD_DRIVE_READY); @@ -901,10 +960,6 @@ void cdvdReset() cdvd.RTC.year = (u8)(curtime.tm_year - 100); // offset from 2000 } - g_GameStarted = false; - g_GameLoading = false; - g_SkipBiosHack = EmuConfig.UseBOOT2Injection; - cdvdCtrlTrayClose(); } @@ -930,7 +985,7 @@ void cdvdNewDiskCB() cdvdDetectDisk(); // If not ejected but we've swapped source pretend it got ejected - if ((g_GameStarted || !g_SkipBiosHack) && cdvd.Tray.trayState != CDVD_DISC_EJECT) + if (!VMManager::Internal::IsFastBootInProgress() && cdvd.Tray.trayState != CDVD_DISC_EJECT) { DevCon.WriteLn(Color_Green, "Ejecting media"); cdvdUpdateStatus(CDVD_STATUS_TRAY_OPEN); @@ -1448,12 +1503,6 @@ void cdvdUpdateTrayState() cdvd.Tray.trayState = CDVD_DISC_SEEKING; cdvdUpdateStatus(CDVD_STATUS_SEEK); cdvd.Tray.cdvdActionSeconds = 2; - // If we're swapping disc, reload the elf, patches etc to reflect the new disc. - if (g_GameStarted) - { - cdvdReloadElfInfo(); - VMManager::Internal::SwappingGameOnCPUThread(); - } break; case CDVD_DISC_SEEKING: cdvd.Spinning = true; @@ -2568,22 +2617,25 @@ static void cdvdWrite16(u8 rt) // SCOMMAND // break; case 0x27: // GetPS1BootParam (0:13) - called only by China region PS2 models + { + // Return Disc Serial which is passed to PS1DRV and later used to find matching config. + SetSCMDResultSize(13); - // Return Disc Serial which is passed to PS1DRV and later used to find matching config. - SetSCMDResultSize(13); - cdvd.SCMDResult[0] = 0; - cdvd.SCMDResult[1] = DiscSerial[0]; - cdvd.SCMDResult[2] = DiscSerial[1]; - cdvd.SCMDResult[3] = DiscSerial[2]; - cdvd.SCMDResult[4] = DiscSerial[3]; - cdvd.SCMDResult[5] = DiscSerial[4]; - cdvd.SCMDResult[6] = DiscSerial[5]; - cdvd.SCMDResult[7] = DiscSerial[6]; - cdvd.SCMDResult[8] = DiscSerial[7]; - cdvd.SCMDResult[9] = DiscSerial[9]; // Skipping dot here is required. - cdvd.SCMDResult[10] = DiscSerial[10]; - cdvd.SCMDResult[11] = DiscSerial[11]; - cdvd.SCMDResult[12] = DiscSerial[12]; + const std::string DiscSerial = VMManager::GetDiscSerial(); + cdvd.SCMDResult[0] = 0; + cdvd.SCMDResult[1] = DiscSerial[0]; + cdvd.SCMDResult[2] = DiscSerial[1]; + cdvd.SCMDResult[3] = DiscSerial[2]; + cdvd.SCMDResult[4] = DiscSerial[3]; + cdvd.SCMDResult[5] = DiscSerial[4]; + cdvd.SCMDResult[6] = DiscSerial[5]; + cdvd.SCMDResult[7] = DiscSerial[6]; + cdvd.SCMDResult[8] = DiscSerial[7]; + cdvd.SCMDResult[9] = DiscSerial[9]; // Skipping dot here is required. + cdvd.SCMDResult[10] = DiscSerial[10]; + cdvd.SCMDResult[11] = DiscSerial[11]; + cdvd.SCMDResult[12] = DiscSerial[12]; + } break; // case 0x28: // cdvdman_call150 (1:1) - In V10 Bios diff --git a/pcsx2/CDVD/CDVD.h b/pcsx2/CDVD/CDVD.h index 575b120371..0fe45de973 100644 --- a/pcsx2/CDVD/CDVD.h +++ b/pcsx2/CDVD/CDVD.h @@ -17,9 +17,12 @@ #include "CDVDcommon.h" +#include #include #include +class ElfObject; + #define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */ #define itob(i) ((i) / 10 * 16 + (i) % 10) /* u_char to BCD */ @@ -76,6 +79,13 @@ struct cdvdRTC u8 year; }; +enum class CDVDDiscType : u8 +{ + Other, + PS1Disc, + PS2Disc +}; + enum TrayStates { CDVD_DISC_ENGAGED, @@ -176,9 +186,11 @@ extern void cdvdNewDiskCB(); extern u8 cdvdRead(u8 key); extern void cdvdWrite(u8 key, u8 rt); -extern void cdvdReloadElfInfo(std::string elfoverride = std::string()); +extern void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc, + CDVDDiscType* out_disc_type); extern u32 cdvdGetElfCRC(const std::string& path); +extern std::unique_ptr cdvdLoadElf(std::string filename, bool isPSXElf); + extern s32 cdvdCtrlTrayOpen(); extern s32 cdvdCtrlTrayClose(); -extern std::string DiscSerial; diff --git a/pcsx2/CDVD/CDVDcommon.cpp b/pcsx2/CDVD/CDVDcommon.cpp index 1bb6bd03b9..25a97be80a 100644 --- a/pcsx2/CDVD/CDVDcommon.cpp +++ b/pcsx2/CDVD/CDVDcommon.cpp @@ -346,6 +346,12 @@ CDVD_SourceType CDVDsys_GetSourceType() return m_CurrentSourceType; } +void CDVDsys_ClearFiles() +{ + for (u32 i = 0; i < std::size(m_SourceFilename); i++) + m_SourceFilename[i] = {}; +} + void CDVDsys_ChangeSource(CDVD_SourceType type) { if (CDVD != NULL) @@ -375,14 +381,6 @@ bool DoCDVDopen() CDVD->newDiskCB(cdvdNewDiskCB); - // Win32 Fail: the old CDVD api expects MBCS on Win32 platforms, but generating a MBCS - // from unicode is problematic since we need to know the codepage of the text being - // converted (which isn't really practical knowledge). A 'best guess' would be the - // default codepage of the user's Windows install, but even that will fail and return - // question marks if the filename is another language. - - //TODO_CDVD check if ISO and Disc use UTF8 - auto CurrentSourceType = enum_cast(m_CurrentSourceType); int ret = CDVD->open(!m_SourceFilename[CurrentSourceType].empty() ? m_SourceFilename[CurrentSourceType].c_str() : nullptr); if (ret == -1) @@ -396,19 +394,14 @@ bool DoCDVDopen() return true; } - std::string somepick(Path::StripExtension(FileSystem::GetDisplayNameFromPath(m_SourceFilename[CurrentSourceType]))); - //FWIW Disc serial availability doesn't seem reliable enough, sometimes it's there and sometime it's just null - //Shouldn't the serial be available all time? Potentially need to look into Elfreloadinfo() reliability - //TODO: Add extra fallback case for CRC. - if (somepick.empty() && !DiscSerial.empty()) - somepick = StringUtil::StdStringFromFormat("Untitled-%s", DiscSerial.c_str()); - else if (somepick.empty()) - somepick = "Untitled"; + std::string dump_name(Path::StripExtension(FileSystem::GetDisplayNameFromPath(m_SourceFilename[CurrentSourceType]))); + if (dump_name.empty()) + dump_name = "Untitled"; if (EmuConfig.CurrentBlockdump.empty()) EmuConfig.CurrentBlockdump = FileSystem::GetWorkingDirectory(); - std::string temp(Path::Combine(EmuConfig.CurrentBlockdump, somepick)); + std::string temp(Path::Combine(EmuConfig.CurrentBlockdump, dump_name)); #ifdef ENABLE_TIMESTAMPS std::time_t curtime_t = std::time(nullptr); diff --git a/pcsx2/CDVD/CDVDcommon.h b/pcsx2/CDVD/CDVDcommon.h index de32341197..dc584db96a 100644 --- a/pcsx2/CDVD/CDVDcommon.h +++ b/pcsx2/CDVD/CDVDcommon.h @@ -158,6 +158,7 @@ extern void CDVDsys_ChangeSource(CDVD_SourceType type); extern void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile); extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype); extern CDVD_SourceType CDVDsys_GetSourceType(); +extern void CDVDsys_ClearFiles(); extern bool DoCDVDopen(); extern void DoCDVDclose(); diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 94a8dffbdc..6a6e8969fe 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -1244,14 +1244,15 @@ struct Pcsx2Config EnablePINE : 1, // enables inter-process communication EnableWideScreenPatches : 1, EnableNoInterlacingPatches : 1, + EnableFastBoot : 1, + EnableFastBootFastForward : 1, + EnablePerGameSettings : 1, // TODO - Vaser - where are these settings exposed in the Qt UI? EnableRecordingTools : 1, EnableGameFixes : 1, // enables automatic game fixes SaveStateOnShutdown : 1, // default value for saving state on shutdown EnableDiscordPresence : 1, // enables discord rich presence integration InhibitScreensaver : 1, - // when enabled uses BOOT2 injection, skipping sony bios splashes - UseBOOT2Injection : 1, BackupSavestate : 1, SavestateZstdCompression : 1, // enables simulated ejection of memory cards when loading savestates diff --git a/pcsx2/Elfheader.cpp b/pcsx2/Elfheader.cpp index 3f71c357a2..6908a7bfb0 100644 --- a/pcsx2/Elfheader.cpp +++ b/pcsx2/Elfheader.cpp @@ -22,40 +22,54 @@ #include "Elfheader.h" #include "DebugTools/SymbolMap.h" -u32 ElfCRC; -u32 ElfEntry; -std::pair ElfTextRange; -std::string LastELF; -bool isPSXElf; -std::string ElfVersion; +#pragma pack(push, 1) +struct PSXEXEHeader +{ + char id[8]; // 0x000-0x007 PS-X EXE + char pad1[8]; // 0x008-0x00F + u32 initial_pc; // 0x010 + u32 initial_gp; // 0x014 + u32 load_address; // 0x018 + u32 file_size; // 0x01C excluding 0x800-byte header + u32 unk0; // 0x020 + u32 unk1; // 0x024 + u32 memfill_start; // 0x028 + u32 memfill_size; // 0x02C + u32 initial_sp_base; // 0x030 + u32 initial_sp_offset; // 0x034 + u32 reserved[5]; // 0x038-0x04B + char marker[0x7B4]; // 0x04C-0x7FF +}; +static_assert(sizeof(PSXEXEHeader) == 0x800); +#pragma pack(pop) // All of ElfObjects functions. -ElfObject::ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf) +ElfObject::ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_) : data(isofile.getLength(), "ELF headers") - , filename(std::move(srcfile)) , header(*(ELF_HEADER*)data.GetPtr()) + , filename(std::move(srcfile)) + , isPSXElf(isPSXElf_) { checkElfSize(data.GetSizeInBytes()); readIso(isofile); - initElfHeaders(isPSXElf); + initElfHeaders(); } -ElfObject::ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf) +ElfObject::ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_) : data(hdrsize, "ELF headers") - , filename(std::move(srcfile)) , header(*(ELF_HEADER*)data.GetPtr()) + , filename(std::move(srcfile)) + , isPSXElf(isPSXElf_) { checkElfSize(data.GetSizeInBytes()); readFile(); - initElfHeaders(isPSXElf); + initElfHeaders(); } -void ElfObject::initElfHeaders(bool isPSXElf) +void ElfObject::initElfHeaders() { if (isPSXElf) - { return; - } DevCon.WriteLn("Initializing Elf: %d bytes", data.GetSizeInBytes()); @@ -133,19 +147,57 @@ void ElfObject::initElfHeaders(bool isPSXElf) //applyPatches(); } +bool ElfObject::hasValidPSXHeader() +{ + if (data.GetSizeInBytes() < sizeof(PSXEXEHeader)) + return false; + + const PSXEXEHeader* header = reinterpret_cast(data.GetPtr()); + + static constexpr char expected_id[] = {'P', 'S', '-', 'X', ' ', 'E', 'X', 'E'}; + if (std::memcmp(header->id, expected_id, sizeof(expected_id)) != 0) + return false; + + if ((header->file_size + sizeof(PSXEXEHeader)) > data.GetSizeInBytes()) + { + Console.Warning("Incorrect file size in PS-EXE header: %u bytes should not be greater than %u bytes", + header->file_size, static_cast(data.GetSizeInBytes() - sizeof(PSXEXEHeader))); + } + + return true; +} + bool ElfObject::hasProgramHeaders() { return (proghead != NULL); } bool ElfObject::hasSectionHeaders() { return (secthead != NULL); } bool ElfObject::hasHeaders() { return (hasProgramHeaders() && hasSectionHeaders()); } +u32 ElfObject::getEntryPoint() +{ + if (isPSXElf) + { + if (hasValidPSXHeader()) + return reinterpret_cast(data.GetPtr())->initial_pc; + else + return 0xFFFFFFFFu; + } + else + { + return header.e_entry; + } +} + std::pair ElfObject::getTextRange() { - for (int i = 0; i < header.e_phnum; i++) + if (isPSXElf && hasProgramHeaders()) { - u32 start = proghead[i].p_vaddr; - u32 size = proghead[i].p_memsz; + for (int i = 0; i < header.e_phnum; i++) + { + const u32 start = proghead[i].p_vaddr; + const u32 size = proghead[i].p_memsz; - if (start <= header.e_entry && (start+size) > header.e_entry) - return std::make_pair(start,size); + if (start <= header.e_entry && (start + size) > header.e_entry) + return std::make_pair(start, size); + } } return std::make_pair(0,0); @@ -310,82 +362,9 @@ void ElfObject::loadSectionHeaders() void ElfObject::loadHeaders() { + if (isPSXElf) + return; + loadProgramHeaders(); loadSectionHeaders(); } - -// return value: -// 0 - Invalid or unknown disc. -// 1 - PS1 CD -// 2 - PS2 CD -int GetPS2ElfName( std::string& name ) -{ - int retype = 0; - - try { - IsoFSCDVD isofs; - IsoFile file( isofs, "SYSTEM.CNF;1"); - - int size = file.getLength(); - if( size == 0 ) return 0; - - while( !file.eof() ) - { - const std::string line(file.readLine()); - std::string_view key, value; - if (!StringUtil::ParseAssignmentString(line, &key, &value)) - continue; - - if( value.empty() && file.getLength() != file.getSeekPos() ) - { // Some games have a character on the last line of the file, don't print the error in those cases. - Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" ); - Console.Indent().WriteLn(line); - continue; - } - - if( key == "BOOT2" ) - { - Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PS2 Disc = %.*s", - static_cast(value.size()), value.data()); - name = value; - retype = 2; - } - else if( key == "BOOT" ) - { - Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PSX/PSone Disc = %.*s", - static_cast(value.size()), value.data()); - name = value; - retype = 1; - } - else if( key == "VMODE" ) - { - Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Disc region type = %.*s", - static_cast(value.size()), value.data()); - } - else if( key == "VER" ) - { - Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Software version = %.*s", - static_cast(value.size()), value.data()); - ElfVersion = value; - } - } - - if( retype == 0 ) - { - Console.Error("(GetElfName) Disc image is *not* a PlayStation or PS2 game!"); - return 0; - } - } - catch( Exception::FileNotFound& ) - { - //Console.Warning(ex.FormatDiagnosticMessage()); - return 0; // no SYSTEM.CNF, not a PS1/PS2 disc. - } - catch (Exception::BadStream& ex) - { - Console.Error(ex.FormatDiagnosticMessage()); - return 0; // ISO error - } - - return retype; -} diff --git a/pcsx2/Elfheader.h b/pcsx2/Elfheader.h index 26237372f5..211cece47d 100644 --- a/pcsx2/Elfheader.h +++ b/pcsx2/Elfheader.h @@ -122,24 +122,23 @@ class ElfObject { private: SafeArray data; + ELF_HEADER& header; ELF_PHR* proghead = nullptr; ELF_SHR* secthead = nullptr; std::string filename; + bool isPSXElf; - void initElfHeaders(bool isPSXElf); + void initElfHeaders(); + bool hasValidPSXHeader(); void readIso(IsoFile& file); void readFile(); void checkElfSize(s64 elfsize); public: - ELF_HEADER& header; + ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_); + ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_); - // Destructor! - // C++ does all the cleanup automagically for us. - virtual ~ElfObject() = default; - - ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf); - ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf); + bool IsPSXElf() const { return isPSXElf; } void loadProgramHeaders(); void loadSectionHeaders(); @@ -150,17 +149,8 @@ class ElfObject bool hasHeaders(); std::pair getTextRange(); + u32 getEntryPoint(); u32 getCRC(); }; //------------------- -extern void loadElfFile(const std::string& filename); -extern int GetPS2ElfName( std::string& dest ); - - -extern u32 ElfCRC; -extern u32 ElfEntry; -extern std::pair ElfTextRange; -extern std::string LastELF; -extern bool isPSXElf; -extern std::string ElfVersion; diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 9b8082c104..52a44d157b 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -21,6 +21,7 @@ #include "Gif_Unit.h" #include "Counters.h" #include "Config.h" +#include "VMManager.h" using namespace Threading; using namespace R5900; @@ -45,7 +46,8 @@ void gsReset() void gsUpdateFrequency(Pcsx2Config& config) { - if (config.GS.FrameLimitEnable) + if (config.GS.FrameLimitEnable && + (!config.EnableFastBootFastForward || !VMManager::Internal::IsFastBootInProgress())) { switch (config.LimiterMode) { diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp index 17fdd99d52..94839eb9d0 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp +++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp @@ -667,7 +667,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame) std::string_view compression_str; if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed) { - m_dump = std::unique_ptr(new GSDumpUncompressed(m_snapshot, VMManager::GetGameSerial(), m_crc, + m_dump = std::unique_ptr(new GSDumpUncompressed(m_snapshot, VMManager::GetDiscSerial(), m_crc, screenshot_width, screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs)); @@ -675,7 +675,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame) } else if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::LZMA) { - m_dump = std::unique_ptr(new GSDumpXz(m_snapshot, VMManager::GetGameSerial(), m_crc, + m_dump = std::unique_ptr(new GSDumpXz(m_snapshot, VMManager::GetDiscSerial(), m_crc, screenshot_width, screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs)); @@ -683,7 +683,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame) } else { - m_dump = std::unique_ptr(new GSDumpZst(m_snapshot, VMManager::GetGameSerial(), m_crc, + m_dump = std::unique_ptr(new GSDumpZst(m_snapshot, VMManager::GetDiscSerial(), m_crc, screenshot_width, screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs)); @@ -780,14 +780,14 @@ static std::string GSGetBaseFilename() std::string filename; // append the game serial and title - if (std::string name(VMManager::GetGameName()); !name.empty()) + if (std::string name(VMManager::GetTitle()); !name.empty()) { Path::SanitizeFileName(&name); if (name.length() > 219) name.resize(219); filename += name; } - if (std::string serial(VMManager::GetGameSerial()); !serial.empty()) + if (std::string serial = VMManager::GetDiscSerial(); !serial.empty()) { Path::SanitizeFileName(&serial); filename += '_'; diff --git a/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp b/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp index 675db8806c..15a3522ed4 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp @@ -303,7 +303,7 @@ std::string GSTextureReplacements::GetDumpFilename(const TextureName& name, u32 void GSTextureReplacements::Initialize() { - s_current_serial = VMManager::GetGameSerial(); + s_current_serial = VMManager::GetDiscSerial(); if (GSConfig.DumpReplaceableTextures || GSConfig.LoadTextureReplacements) StartWorkerThread(); @@ -313,7 +313,7 @@ void GSTextureReplacements::Initialize() void GSTextureReplacements::GameChanged() { - std::string new_serial(VMManager::GetGameSerial()); + std::string new_serial = VMManager::GetDiscSerial(); if (s_current_serial == new_serial) return; diff --git a/pcsx2/GSDumpReplayer.cpp b/pcsx2/GSDumpReplayer.cpp index d9fc0b4697..746f5cf5bc 100644 --- a/pcsx2/GSDumpReplayer.cpp +++ b/pcsx2/GSDumpReplayer.cpp @@ -204,9 +204,6 @@ static void GSDumpReplayerLoadInitialState() std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(), std::min(Ps2MemSize::GSregs, static_cast(s_dump_file->GetRegsData().size()))); - // update serial to load hw fixes - VMManager::Internal::GameStartingOnCPUThread(); - // load GS state freezeData fd = {static_cast(s_dump_file->GetStateData().size()), const_cast(s_dump_file->GetStateData().data())}; diff --git a/pcsx2/GameDatabase.cpp b/pcsx2/GameDatabase.cpp index 285f4c86b1..6b940aa2a9 100644 --- a/pcsx2/GameDatabase.cpp +++ b/pcsx2/GameDatabase.cpp @@ -60,22 +60,17 @@ std::string GameDatabaseSchema::GameEntry::memcardFiltersAsString() const const std::string* GameDatabaseSchema::GameEntry::findPatch(u32 crc) const { - Console.WriteLn(fmt::format("[GameDB] Searching for patch with CRC '{:08X}'", crc)); + if (crc == 0) + return nullptr; auto it = patches.find(crc); if (it != patches.end()) - { - Console.WriteLn(fmt::format("[GameDB] Found patch with CRC '{:08X}'", crc)); return &it->second; - } it = patches.find(0); if (it != patches.end()) - { - Console.WriteLn("[GameDB] Found and falling back to default patch"); return &it->second; - } - Console.WriteLn("[GameDB] No CRC-specific patch or default patch found"); + return nullptr; } diff --git a/pcsx2/GameList.cpp b/pcsx2/GameList.cpp index c47e9772ca..06eb3b90fd 100644 --- a/pcsx2/GameList.cpp +++ b/pcsx2/GameList.cpp @@ -173,19 +173,10 @@ bool GameList::GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std:: if (CDVD->open(path.c_str()) != 0) return false; + // TODO: we could include the version in the game list? *disc_type = DoCDVDdetectDiskType(); - cdvdReloadElfInfo(); - - *serial = DiscSerial; - *crc = ElfCRC; - + cdvdGetDiscInfo(serial, nullptr, nullptr, crc, nullptr); DoCDVDclose(); - - // TODO(Stenzek): These globals are **awful**. Clean it up. - DiscSerial.clear(); - ElfCRC = 0; - ElfEntry = -1; - LastELF.clear(); return true; } diff --git a/pcsx2/Hotkeys.cpp b/pcsx2/Hotkeys.cpp index f39c4ea409..d4fbd79693 100644 --- a/pcsx2/Hotkeys.cpp +++ b/pcsx2/Hotkeys.cpp @@ -79,9 +79,9 @@ static void HotkeyCycleSaveSlot(s32 delta) else s_current_save_slot = (s_current_save_slot % CYCLE_SAVE_STATE_SLOTS) + 1; - const u32 crc = VMManager::GetGameCRC(); - const std::string serial(VMManager::GetGameSerial()); - const std::string filename(VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot)); + const u32 crc = VMManager::GetDiscCRC(); + const std::string serial = VMManager::GetDiscSerial(); + const std::string filename = VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot); FILESYSTEM_STAT_DATA sd; if (!filename.empty() && FileSystem::StatFile(filename.c_str(), &sd)) { @@ -109,34 +109,21 @@ static void HotkeyCycleSaveSlot(s32 delta) static void HotkeyLoadStateSlot(s32 slot) { - const u32 crc = VMManager::GetGameCRC(); - if (crc == 0) - { - Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot load state from a slot without a game running.", - Host::OSD_INFO_DURATION); - return; - } + // Can reapply settings and thus binds, therefore must be deferred. + Host::RunOnCPUThread([slot]() { + if (!VMManager::HasSaveStateInSlot(VMManager::GetDiscSerial().c_str(), VMManager::GetDiscCRC(), slot)) + { + Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot), + Host::OSD_INFO_DURATION); + return; + } - const std::string serial(VMManager::GetGameSerial()); - if (!VMManager::HasSaveStateInSlot(serial.c_str(), crc, slot)) - { - Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot), - Host::OSD_INFO_DURATION); - return; - } - - VMManager::LoadStateFromSlot(slot); + VMManager::LoadStateFromSlot(slot); + }); } static void HotkeySaveStateSlot(s32 slot) { - if (VMManager::GetGameCRC() == 0) - { - Host::AddIconOSDMessage("SaveStateToSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot save state to a slot without a game running.", - Host::OSD_INFO_DURATION); - return; - } - VMManager::SaveStateToSlot(slot); } diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index f24b37a6aa..e72d08977c 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -200,7 +200,7 @@ namespace FullscreenUI ////////////////////////////////////////////////////////////////////////// // Main ////////////////////////////////////////////////////////////////////////// - static void UpdateGameDetails(std::string path, std::string serial, std::string title, u32 crc); + static void UpdateGameDetails(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc); static void ToggleTheme(); static void PauseForMenuOpen(); static void ClosePauseMenu(); @@ -229,9 +229,9 @@ namespace FullscreenUI // local copies of the currently-running game static std::string s_current_game_title; static std::string s_current_game_subtitle; - static std::string s_current_game_serial; - static std::string s_current_game_path; - static u32 s_current_game_crc; + static std::string s_current_disc_serial; + static std::string s_current_disc_path; + static u32 s_current_disc_crc; ////////////////////////////////////////////////////////////////////////// // Resources @@ -578,7 +578,8 @@ bool FullscreenUI::Initialize() if (VMManager::HasValidVM()) { - UpdateGameDetails(VMManager::GetDiscPath(), VMManager::GetGameSerial(), VMManager::GetGameName(), VMManager::GetGameCRC()); + UpdateGameDetails(VMManager::GetDiscPath(), VMManager::GetDiscSerial(), VMManager::GetTitle(), + VMManager::GetDiscCRC(), VMManager::GetCurrentCRC()); } else { @@ -651,20 +652,21 @@ void FullscreenUI::OnVMDestroyed() }); } -void FullscreenUI::GameChanged(std::string path, std::string serial, std::string title, u32 crc) +void FullscreenUI::GameChanged(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc) { if (!IsInitialized()) return; - GetMTGS().RunOnGSThread([path = std::move(path), serial = std::move(serial), title = std::move(title), crc]() { - if (!IsInitialized()) - return; + GetMTGS().RunOnGSThread( + [path = std::move(path), serial = std::move(serial), title = std::move(title), disc_crc, crc]() { + if (!IsInitialized()) + return; - UpdateGameDetails(std::move(path), std::move(serial), std::move(title), crc); - }); + UpdateGameDetails(std::move(path), std::move(serial), std::move(title), disc_crc, crc); + }); } -void FullscreenUI::UpdateGameDetails(std::string path, std::string serial, std::string title, u32 crc) +void FullscreenUI::UpdateGameDetails(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc) { if (!serial.empty()) s_current_game_subtitle = fmt::format("{} / {:08X}", serial, crc); @@ -672,9 +674,9 @@ void FullscreenUI::UpdateGameDetails(std::string path, std::string serial, std:: s_current_game_subtitle = {}; s_current_game_title = std::move(title); - s_current_game_serial = std::move(serial); - s_current_game_path = std::move(path); - s_current_game_crc = crc; + s_current_disc_serial = std::move(serial); + s_current_disc_path = std::move(path); + s_current_disc_crc = disc_crc; } void FullscreenUI::ToggleTheme() @@ -744,9 +746,9 @@ void FullscreenUI::Shutdown(bool clear_state) s_graphics_adapter_list_cache = {}; s_current_game_title = {}; s_current_game_subtitle = {}; - s_current_game_serial = {}; - s_current_game_path = {}; - s_current_game_crc = 0; + s_current_disc_serial = {}; + s_current_disc_path = {}; + s_current_disc_crc = 0; s_current_main_window = MainWindowType::None; s_current_pause_submenu = PauseSubMenu::None; @@ -1053,7 +1055,7 @@ void FullscreenUI::DoChangeDiscFromFile() }; OpenFileSelector(ICON_FA_COMPACT_DISC " Select Disc Image", false, std::move(callback), GetDiscImageFilters(), - std::string(Path::GetDirectory(s_current_game_path))); + std::string(Path::GetDirectory(s_current_disc_path))); } void FullscreenUI::DoChangeDisc() @@ -2333,13 +2335,13 @@ void FullscreenUI::SwitchToGameSettings(const std::string_view& serial, u32 crc) void FullscreenUI::SwitchToGameSettings() { - if (s_current_game_serial.empty() || s_current_game_crc == 0) + if (s_current_disc_serial.empty() || s_current_disc_crc == 0) return; auto lock = GameList::GetLock(); - const GameList::Entry* entry = GameList::GetEntryForPath(s_current_game_path.c_str()); + const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str()); if (!entry) - entry = GameList::GetEntryBySerialAndCRC(s_current_game_serial.c_str(), s_current_game_crc); + entry = GameList::GetEntryBySerialAndCRC(s_current_disc_serial.c_str(), s_current_disc_crc); if (entry) SwitchToGameSettings(entry); @@ -4266,7 +4268,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) const float image_width = has_rich_presence ? 60.0f : 50.0f; const float image_height = has_rich_presence ? 90.0f : 75.0f; - const std::string_view path_string(Path::GetFileName(s_current_game_path)); + const std::string_view path_string(Path::GetFileName(s_current_disc_path)); const ImVec2 title_size( g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), -1.0f, s_current_game_title.c_str())); const ImVec2 path_size(path_string.empty() ? @@ -4345,9 +4347,9 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) const ImVec2 time_pos(display_size.x - LayoutScale(10.0f) - time_size.x, LayoutScale(10.0f)); DrawShadowedText(dl, g_large_font, time_pos, IM_COL32(255, 255, 255, 255), buf); - if (!s_current_game_serial.empty()) + if (!s_current_disc_serial.empty()) { - const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(s_current_game_serial); + const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(s_current_disc_serial); const std::time_t session_time = static_cast(VMManager::GetSessionPlayedTime()); const std::string played_time_str(GameList::FormatTimespan(cached_played_time + session_time, true)); const std::string session_time_str(GameList::FormatTimespan(session_time, true)); @@ -4389,7 +4391,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) case PauseSubMenu::None: { // NOTE: Menu close must come first, because otherwise VM destruction options will race. - const bool can_load_or_save_state = s_current_game_crc != 0; + const bool can_load_or_save_state = s_current_disc_crc != 0; if (ActiveButton(ICON_FA_PLAY " Resume Game", false) || WantsToCloseMenu()) ClosePauseMenu(); @@ -4614,7 +4616,7 @@ bool FullscreenUI::OpenSaveStateSelector(bool is_loading) s_save_state_selector_game_path = {}; s_save_state_selector_loading = is_loading; s_save_state_selector_resuming = false; - if (PopulateSaveStateListEntries(s_current_game_title.c_str(), s_current_game_serial.c_str(), s_current_game_crc) > 0) + if (PopulateSaveStateListEntries(s_current_game_title.c_str(), s_current_disc_serial.c_str(), s_current_disc_crc) > 0) { s_save_state_selector_open = true; return true; @@ -5815,7 +5817,7 @@ GSTexture* FullscreenUI::GetCoverForCurrentGame() { auto lock = GameList::GetLock(); - const GameList::Entry* entry = GameList::GetEntryForPath(s_current_game_path.c_str()); + const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str()); if (!entry) return s_fallback_disc_texture.get(); diff --git a/pcsx2/ImGui/FullscreenUI.h b/pcsx2/ImGui/FullscreenUI.h index 4f5578e810..c3ca58384f 100644 --- a/pcsx2/ImGui/FullscreenUI.h +++ b/pcsx2/ImGui/FullscreenUI.h @@ -29,7 +29,7 @@ namespace FullscreenUI void CheckForConfigChanges(const Pcsx2Config& old_config); void OnVMStarted(); void OnVMDestroyed(); - void GameChanged(std::string path, std::string serial, std::string title, u32 crc); + void GameChanged(std::string title, std::string path, std::string serial, u32 disc_crc, u32 crc); void OpenPauseMenu(); void OpenAchievementsWindow(); void OpenLeaderboardsWindow(); diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index d9de915d84..13f33443e9 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -513,30 +513,22 @@ static void intCancelInstruction() static void intExecute() { - enum ExecuteState { - RESET, - GAME_LOADING, - GAME_RUNNING - }; - ExecuteState state = RESET; - // This will come back as zero the first time it runs, or on instruction cancel. // It will come back as nonzero when we exit execution. if (fastjmp_set(&intJmpBuf) != 0) return; - // I hope this doesn't cause issues with the optimizer... infinite loop with a constant expression. for (;;) { - // The execution was splited in three parts so it is easier to - // resume it after a cancelled instruction. - switch (state) { - case RESET: + if (!VMManager::Internal::HasBootedELF()) + { + // Avoid reloading every instruction. + u32 elf_entry_point = VMManager::Internal::GetCurrentELFEntryPoint(); + u32 eeload_main = g_eeloadMain; + + while (true) { - do - { - execI(); - } while (cpuRegs.pc != (g_eeloadMain ? g_eeloadMain : EELOAD_START)); + execI(); if (cpuRegs.pc == EELOAD_START) { @@ -544,11 +536,13 @@ static void intExecute() u32 mainjump = memRead32(EELOAD_START + 0x9c); if (mainjump >> 26 == 3) // JAL g_eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU); + + eeload_main = g_eeloadMain; } else if (cpuRegs.pc == g_eeloadMain) { eeloadHook(); - if (g_SkipBiosHack) + if (VMManager::Internal::IsFastBootInProgress()) { // See comments on this code in iR5900-32.cpp's recRecompile() u32 typeAexecjump = memRead32(EELOAD_START + 0x470); @@ -562,39 +556,24 @@ static void intExecute() else Console.WriteLn("intExecute: Could not enable launch arguments for fast boot mode; unidentified BIOS version! Please report this to the PCSX2 developers."); } + + elf_entry_point = VMManager::Internal::GetCurrentELFEntryPoint(); } else if (cpuRegs.pc == g_eeloadExec) { eeloadHook2(); } - - if (!g_GameLoading) - break; - - state = GAME_LOADING; - [[fallthrough]]; - } - - case GAME_LOADING: - { - if (ElfEntry != 0xFFFFFFFF) + else if (cpuRegs.pc == elf_entry_point) { - do - { - execI(); - } while (cpuRegs.pc != ElfEntry); - eeGameStarting(); + VMManager::Internal::EntryPointCompilingOnCPUThread(); + break; } - state = GAME_RUNNING; - [[fallthrough]]; } - - case GAME_RUNNING: - { - while (true) - execI(); - } - break; + } + else + { + while (true) + execI(); } } } diff --git a/pcsx2/IopBios.cpp b/pcsx2/IopBios.cpp index a24c5e3bf5..750222b06b 100644 --- a/pcsx2/IopBios.cpp +++ b/pcsx2/IopBios.cpp @@ -23,6 +23,7 @@ #include "R5900.h" #include "ps2/BiosTools.h" #include "x86/iR3000A.h" +#include "VMManager.h" #include "common/FileSystem.h" #include "common/Path.h" @@ -170,6 +171,17 @@ namespace R3000A else if (!hostRoot.empty()) // relative paths new_path = Path::Combine(hostRoot, native_path); + // Allow opening the ELF override. + if (new_path == VMManager::Internal::GetELFOverride()) + return new_path; + + // Allow nothing if hostfs isn't enabled. + if (!EmuConfig.HostFs) + { + new_path.clear(); + return new_path; + } + // Double-check that it falls within the directory of the elf. // Not a real sandbox, but emulators shouldn't be treated as such. Don't run untrusted code! std::string canonicalized_path(Path::Canonicalize(new_path)); @@ -562,7 +574,7 @@ namespace R3000A if (not_number_pos == std::string::npos) return false; - return ((!g_GameStarted || EmuConfig.HostFs) && 0 == path.compare(0, 4, "host") && path[not_number_pos] == ':'); + return (path.compare(0, 4, "host") == 0 && path[not_number_pos] == ':'); } int open_HLE() diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 6c0b9c3581..d87ca74aee 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -268,7 +268,7 @@ bool SysMtgsThread::TryOpenGS() if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs)) return false; - GSSetGameCRC(ElfCRC); + GSSetGameCRC(VMManager::GetDiscCRC()); return true; } diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp index eb33501d73..72a407201d 100644 --- a/pcsx2/Memory.cpp +++ b/pcsx2/Memory.cpp @@ -48,8 +48,6 @@ BIOS #include "common/AlignedMalloc.h" -#include "GSDumpReplayer.h" - #ifdef ENABLECACHE #include "Cache.h" #endif @@ -841,11 +839,7 @@ void eeMemoryReserve::Reset() vtlb_VMap(0x00000000,0x00000000,0x20000000); vtlb_VMapUnmap(0x20000000,0x60000000); - const bool needs_bios = !GSDumpReplayer::IsReplayingDump(); - - // TODO(Stenzek): Move BIOS loading out and far away... - if (needs_bios && !LoadBIOS()) - pxFailRel("Failed to load BIOS"); + CopyBIOSToMemory(); } void eeMemoryReserve::Release() diff --git a/pcsx2/PINE.cpp b/pcsx2/PINE.cpp index 44659638f0..2d5f9afd37 100644 --- a/pcsx2/PINE.cpp +++ b/pcsx2/PINE.cpp @@ -437,7 +437,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span buf, std::vector buf, std::vector buf, std::vector buf, std::vector s_override_aspect_ratio; static std::optional s_override_interlace_mode; @@ -520,18 +519,16 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable return count; } -void Patch::ReloadPatches(std::string serial, u32 crc, bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed) +void Patch::ReloadPatches(const std::string& serial, u32 crc, bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed) { - const bool serial_changed = (s_patches_serial != serial); + reload_files |= (s_patches_crc != crc); s_patches_crc = crc; - s_patches_serial = std::move(serial); - // Skip reloading gamedb patches if the serial hasn't changed. - if (serial_changed) + if (reload_files) { s_gamedb_patches.clear(); - const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_patches_serial); + const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(serial); if (game) { const std::string* patches = game->findPatch(crc); @@ -544,18 +541,10 @@ void Patch::ReloadPatches(std::string serial, u32 crc, bool force_reload_files, LoadDynamicPatches(game->dynaPatches); } - } - ReloadPatches(serial_changed, reload_enabled_list, verbose, verbose_if_changed); -} - -void Patch::ReloadPatches(bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed) -{ - if (force_reload_files) - { s_game_patches.clear(); EnumeratePnachFiles( - s_patches_serial, s_patches_crc, false, false, [](const std::string& filename, const std::string& pnach_data) { + serial, s_patches_crc, false, false, [](const std::string& filename, const std::string& pnach_data) { const u32 patch_count = LoadPatchesFromString(&s_game_patches, pnach_data); if (patch_count > 0) Console.WriteLn(Color_Green, fmt::format("Found {} game patches in {}.", patch_count, filename)); @@ -563,7 +552,7 @@ void Patch::ReloadPatches(bool force_reload_files, bool reload_enabled_list, boo s_cheat_patches.clear(); EnumeratePnachFiles( - s_patches_serial, s_patches_crc, true, false, [](const std::string& filename, const std::string& pnach_data) { + serial, s_patches_crc, true, false, [](const std::string& filename, const std::string& pnach_data) { const u32 patch_count = LoadPatchesFromString(&s_cheat_patches, pnach_data); if (patch_count > 0) Console.WriteLn(Color_Green, fmt::format("Found {} cheats in {}.", patch_count, filename)); @@ -584,9 +573,10 @@ void Patch::UpdateActivePatches(bool reload_enabled_list, bool verbose, bool ver s_override_interlace_mode.reset(); std::string message; + u32 gp_count = 0; if (EmuConfig.EnablePatches) { - const u32 gp_count = EnablePatches(s_gamedb_patches, EnablePatchList()); + gp_count = EnablePatches(s_gamedb_patches, EnablePatchList()); if (gp_count > 0) fmt::format_to(std::back_inserter(message), "{} GameDB patches", gp_count); } @@ -600,7 +590,9 @@ void Patch::UpdateActivePatches(bool reload_enabled_list, bool verbose, bool ver fmt::format_to(std::back_inserter(message), "{}{} cheat patches", message.empty() ? "" : ", ", c_count); // Display message on first boot when we load patches. - if (verbose || (verbose_if_changed && prev_count != s_active_patches.size())) + // Except when it's just GameDB. + const bool just_gamedb = (p_count == 0 && c_count == 0 && gp_count > 0); + if (verbose || (verbose_if_changed && prev_count != s_active_patches.size() && !just_gamedb)) { if (!message.empty()) { @@ -673,7 +665,6 @@ void Patch::UnloadPatches() s_override_interlace_mode = {}; s_override_aspect_ratio = {}; s_patches_crc = 0; - s_patches_serial = {}; s_active_patches = {}; s_active_dynamic_patches = {}; s_enabled_patches = {}; diff --git a/pcsx2/Patch.h b/pcsx2/Patch.h index b62ed4388c..0da5614b95 100644 --- a/pcsx2/Patch.h +++ b/pcsx2/Patch.h @@ -96,8 +96,7 @@ namespace Patch extern PatchInfoList GetPatchInfo(const std::string& serial, u32 crc, bool cheats, u32* num_unlabelled_patches); /// Reloads cheats/patches. If verbose is set, the number of patches loaded will be shown in the OSD. - extern void ReloadPatches(std::string serial, u32 crc, bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed); - extern void ReloadPatches(bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed); + extern void ReloadPatches(const std::string& serial, u32 crc, bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed); extern void UpdateActivePatches(bool reload_enabled_list, bool verbose, bool verbose_if_changed); extern void ApplyPatchSettingOverrides(); diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index b9efdde5b1..7ccc48b76e 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1305,6 +1305,8 @@ Pcsx2Config::Pcsx2Config() McdEnableEjection = true; McdFolderAutoManage = true; EnablePatches = true; + EnableFastBoot = true; + EnablePerGameSettings = true; EnableRecordingTools = true; EnableGameFixes = true; InhibitScreensaver = true; @@ -1348,6 +1350,9 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap) SettingsWrapBitBool(EnablePINE); SettingsWrapBitBool(EnableWideScreenPatches); SettingsWrapBitBool(EnableNoInterlacingPatches); + SettingsWrapBitBool(EnableFastBoot); + SettingsWrapBitBool(EnableFastBootFastForward); + SettingsWrapBitBool(EnablePerGameSettings); SettingsWrapBitBool(EnableRecordingTools); SettingsWrapBitBool(EnableGameFixes); SettingsWrapBitBool(SaveStateOnShutdown); @@ -1472,7 +1477,6 @@ bool Pcsx2Config::operator==(const Pcsx2Config& right) const void Pcsx2Config::CopyRuntimeConfig(Pcsx2Config& cfg) { GS.LimitScalar = cfg.GS.LimitScalar; - UseBOOT2Injection = cfg.UseBOOT2Injection; CurrentBlockdump = std::move(cfg.CurrentBlockdump); CurrentIRX = std::move(cfg.CurrentIRX); CurrentGameArgs = std::move(cfg.CurrentGameArgs); diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 80fc466512..b08efda887 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -51,10 +51,6 @@ alignas(16) fpuRegisters fpuRegs; alignas(16) tlbs tlb[48]; R5900cpu *Cpu = NULL; -bool g_SkipBiosHack; // set at boot if the skip bios hack is on, reset before the game has started -bool g_GameStarted; // set when we reach the game's entry point or earlier if the entry point cannot be determined -bool g_GameLoading; // EELOAD has been called to load the game - static const uint eeWaitCycles = 3072; bool eeEventTestIsActive = false; @@ -104,24 +100,12 @@ void cpuReset() extern void Deci2Reset(); // lazy, no good header for it yet. Deci2Reset(); - g_SkipBiosHack = EmuConfig.UseBOOT2Injection; - AllowParams1 = !g_SkipBiosHack; - AllowParams2 = !g_SkipBiosHack; + AllowParams1 = !VMManager::Internal::IsFastBootInProgress(); + AllowParams2 = !VMManager::Internal::IsFastBootInProgress(); - ElfCRC = 0; - DiscSerial.clear(); - ElfEntry = -1; - g_GameStarted = false; - g_GameLoading = false; - - // FIXME: LastELF should be reset on media changes as well as on CPU resets, in - // the very unlikely case that a user swaps to another media source that "looks" - // the same (identical ELF names) but is actually different (devs actually could - // run into this while testing minor binary hacked changes to ISO images, which - // is why I found out about this) --air - LastELF.clear(); - - g_eeloadMain = 0, g_eeloadExec = 0, g_osdsys_str = 0; + g_eeloadMain = 0; + g_eeloadExec = 0; + g_osdsys_str = 0; } __ri void cpuException(u32 code, u32 bd) @@ -279,7 +263,7 @@ static __fi void TESTINT( u8 n, void (*callback)() ) { if( !(cpuRegs.interrupt & (1 << n)) ) return; - if(!g_GameStarted || CHECK_INSTANTDMAHACK || cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) ) + if(CHECK_INSTANTDMAHACK || cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) ) { cpuClearInt( n ); callback(); @@ -416,7 +400,7 @@ __fi void _cpuEventTest_Shared() // where a DMA buffer is overwritten without waiting for the transfer to end, which causes the fonts to get all messed up // so to fix it, we run all the DMA's instantly when in the BIOS. // Only use the lower 17 bits of the cpuRegs.interrupt as the upper bits are for VU0/1 sync which can't be done in a tight loop - if ((!g_GameStarted || CHECK_INSTANTDMAHACK) && dmacRegs.ctrl.DMAE && !(psHu8(DMAC_ENABLER + 2) & 1) && (cpuRegs.interrupt & 0x1FFFF)) + if (CHECK_INSTANTDMAHACK && dmacRegs.ctrl.DMAE && !(psHu8(DMAC_ENABLER + 2) & 1) && (cpuRegs.interrupt & 0x1FFFF)) { while ((cpuRegs.interrupt & 0x1FFFF) && _cpuTestInterrupts()) ; @@ -568,27 +552,6 @@ __fi void CPU_INT( EE_EventType n, s32 ecycle) cpuSetNextEventDelta(cpuRegs.eCycle[n]); } -// Called from recompilers; define is mandatory. -void eeGameStarting() -{ - if (!g_GameStarted) - { - //Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry ); - g_GameStarted = true; - g_GameLoading = false; - - // GameStartingInThread may issue a reset of the cpu and/or recompilers. Check for and - // handle such things here: - VMManager::Internal::GameStartingOnCPUThread(); - if (VMManager::Internal::IsExecutionInterrupted()) - Cpu->ExitExecution(); - } - else - { - Console.WriteLn( Color_Green, "(R5900) Re-executed ELF Entry point (ignored) [addr=0x%08X]", ElfEntry ); - } -} - // Count arguments, save their starting locations, and replace the space separators with null terminators so they're separate strings int ParseArgumentString(u32 arg_block) { @@ -635,16 +598,6 @@ int ParseArgumentString(u32 arg_block) // Called from recompilers; define is mandatory. void eeloadHook() { - const std::string& elf_override(VMManager::Internal::GetElfOverride()); - - if (!elf_override.empty()) - cdvdReloadElfInfo(StringUtil::StdStringFromFormat("host:%s", elf_override.c_str())); - else - cdvdReloadElfInfo(); - - std::string discelf; - int disctype = GetPS2ElfName(discelf); - std::string elfname; int argc = cpuRegs.GPR.n.a0.SD[0]; if (argc) // calls to EELOAD *after* the first one during the startup process will come here @@ -664,7 +617,7 @@ void eeloadHook() // mode). Then EELOAD is called with the argument "rom0:PS2LOGO". At this point, we do not need any additional tricks // because EELOAD is now ready to accept launch arguments. So in full-boot mode, we simply wait for PS2LOGO to be called, // then we add the desired launch arguments. PS2LOGO passes those on to the game itself as it calls EELOAD a third time. - if (!EmuConfig.CurrentGameArgs.empty() && !strcmp(elfname.c_str(), "rom0:PS2LOGO")) + if (!EmuConfig.CurrentGameArgs.empty() && elfname == "rom0:PS2LOGO") { const char *argString = EmuConfig.CurrentGameArgs.c_str(); Console.WriteLn("eeloadHook: Supplying launch argument(s) '%s' to module '%s'...", argString, elfname.c_str()); @@ -711,24 +664,32 @@ void eeloadHook() // If "fast boot" was chosen, then on EELOAD's first call we won't yet know what the game's ELF is. Find the name and write it // into EELOAD's memory. - if (g_SkipBiosHack && elfname.empty()) + if (VMManager::Internal::IsFastBootInProgress() && elfname.empty()) { - std::string elftoload; + const std::string& elf_override = VMManager::Internal::GetELFOverride(); if (!elf_override.empty()) { - elftoload = StringUtil::StdStringFromFormat("host:%s", elf_override.c_str()); + elfname = fmt::format("host:{}", elf_override); } else { - if (disctype == 2) - elftoload = discelf; + CDVDDiscType disc_type; + std::string disc_elf; + cdvdGetDiscInfo(nullptr, &disc_elf, nullptr, nullptr, &disc_type); + if (disc_type == CDVDDiscType::PS2Disc) + { + // only allow fast boot for PS2 games + elfname = std::move(disc_elf); + } else - g_SkipBiosHack = false; // We're not fast booting, so disable it (Fixes some weirdness with the BIOS) + { + Console.Warning(fmt::format("Not allowing fast boot for non-PS2 ELF {}", disc_elf)); + } } // When fast-booting, we insert the game's ELF name into EELOAD so that the game is called instead of the default call of // "rom0:OSDSYS"; any launch arguments supplied by the user will be inserted into EELOAD later by eeloadHook2() - if (!elftoload.empty()) + if (!elfname.empty()) { // Find and save location of default/fallback call "rom0:OSDSYS"; to be used later by eeloadHook2() for (g_osdsys_str = EELOAD_START; g_osdsys_str < EELOAD_START + EELOAD_SIZE; g_osdsys_str += 8) // strings are 64-bit aligned @@ -736,16 +697,20 @@ void eeloadHook() if (!strcmp((char*)PSM(g_osdsys_str), "rom0:OSDSYS")) { // Overwrite OSDSYS with game's ELF name - strcpy((char*)PSM(g_osdsys_str), elftoload.c_str()); - g_GameLoading = true; - return; + strcpy((char*)PSM(g_osdsys_str), elfname.c_str()); } } } + else + { + // Stop fast forwarding if we're doing that for boot. + VMManager::Internal::DisableFastBoot(); + AllowParams1 = true; + AllowParams2 = true; + } } - if (!g_GameStarted && ((disctype == 2 && elfname == discelf) || disctype == 1)) - g_GameLoading = true; + VMManager::Internal::ELFLoadingOnCPUThread(std::move(elfname)); } // Called from recompilers; define is mandatory. diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index c3ef06db6c..7efa63733a 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -26,10 +26,6 @@ class BaseR5900Exception; // them in iR5900.h would mean having to include that into more files than I care to // right now, so we're sticking them here for now until a better solution comes along. -extern bool g_SkipBiosHack; -extern bool g_GameStarted; -extern bool g_GameLoading; - namespace Exception { // Implementation Note: this exception has no meaningful type information and we don't @@ -276,7 +272,6 @@ const u32 EELOAD_START = 0x82000; const u32 EELOAD_SIZE = 0x20000; // overestimate for searching extern u32 g_eeloadMain, g_eeloadExec; -extern void eeGameStarting(); extern void eeloadHook(); extern void eeloadHook2(); diff --git a/pcsx2/R5900OpcodeImpl.cpp b/pcsx2/R5900OpcodeImpl.cpp index 5d14fb9060..eb06a21ae0 100644 --- a/pcsx2/R5900OpcodeImpl.cpp +++ b/pcsx2/R5900OpcodeImpl.cpp @@ -969,7 +969,7 @@ void SYSCALL() AllowParams1 = true; break; case Syscall::GetOsdConfigParam: - if(!NoOSD && g_SkipBiosHack && !AllowParams1) + if(!NoOSD && !AllowParams1) { u32 memaddr = cpuRegs.GPR.n.a0.UL[0]; u8 params[16]; @@ -993,7 +993,7 @@ void SYSCALL() AllowParams2 = true; break; case Syscall::GetOsdConfigParam2: - if (!NoOSD && g_SkipBiosHack && !AllowParams2) + if (!NoOSD && !AllowParams2) { u32 memaddr = cpuRegs.GPR.n.a0.UL[0]; u8 params[16]; diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index e7cd05d6a8..427b6abeed 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -78,7 +78,7 @@ bool InputRecording::create(const std::string& fileName, const bool fromSaveStat m_file.setEmulatorVersion(); m_file.setAuthor(authorName); - m_file.setGameName(resolveGameName()); + m_file.setGameName(VMManager::GetTitle()); m_file.writeHeader(); initializeState(); InputRec::log("Started new input recording"); @@ -129,9 +129,9 @@ bool InputRecording::play(const std::string& filename) initializeState(); InputRec::log("Replaying input recording"); m_file.logRecordingMetadata(); - if (resolveGameName() != m_file.getGameName()) + if (VMManager::GetTitle() != m_file.getGameName()) { - InputRec::consoleLog(fmt::format("Input recording was possibly constructed for a different game. Expected: {}, Actual: {}", m_file.getGameName(), resolveGameName())); + InputRec::consoleLog(fmt::format("Input recording was possibly constructed for a different game. Expected: {}, Actual: {}", m_file.getGameName(), VMManager::GetTitle())); } return true; } @@ -229,21 +229,6 @@ void InputRecording::processRecordQueue() } } -std::string InputRecording::resolveGameName() -{ - std::string gameName; - const std::string gameKey = SysGetDiscID(); - if (!gameKey.empty()) - { - auto game = GameDatabase::findGame(gameKey); - if (game) - { - gameName = fmt::format("{} ({})", game->name, game->region); - } - } - return !gameName.empty() ? gameName : VMManager::GetGameName(); -} - void InputRecording::incFrameCounter() { if (!m_is_active) diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 4bee1523df..2e50a191b1 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -70,11 +70,6 @@ private: void initializeState(); void setStartingFrame(u32 startingFrame); void closeActiveFile(); - -private: - // Resolve the name and region of the game currently loaded using the GameDB - // If the game cannot be found in the DB, the fallback is the ISO filename - std::string resolveGameName(); }; extern InputRecording g_InputRecording; diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index 24581e50d7..c227dde923 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -174,11 +174,11 @@ SaveStateBase& SaveStateBase::FreezeBios() SaveStateBase& SaveStateBase::FreezeInternals() { - const u32 previousCRC = ElfCRC; - // Print this until the MTVU problem in gifPathFreeze is taken care of (rama) if (THREAD_VU1) Console.Warning("MTVU speedhack is enabled, saved states may not be stable"); + vmFreeze(); + // Second Block - Various CPU Registers and States // ----------------------------------------------- FreezeTag( "cpuRegs" ); @@ -188,30 +188,7 @@ SaveStateBase& SaveStateBase::FreezeInternals() Freeze(tlb); // tlbs Freeze(AllowParams1); //OSDConfig written (Fast Boot) Freeze(AllowParams2); - Freeze(g_GameStarted); - Freeze(g_GameLoading); - Freeze(ElfCRC); - - char localDiscSerial[256]; - StringUtil::Strlcpy(localDiscSerial, DiscSerial.c_str(), sizeof(localDiscSerial)); - Freeze(localDiscSerial); - if (IsLoading()) - { - DiscSerial = localDiscSerial; - - if (ElfCRC != previousCRC) - { - // HACK: LastELF isn't in the save state... Load it before we go too far into restoring state. - // When we next bump save states, we should include it. We need this for achievements, because - // we want to load and activate achievements before restoring any of their tracked state. - if (const std::string& elf_override = VMManager::Internal::GetElfOverride(); !elf_override.empty()) - cdvdReloadElfInfo(fmt::format("host:{}", elf_override)); - else - cdvdReloadElfInfo(); - } - } - - + // Third Block - Cycle Timers and Events // ------------------------------------- FreezeTag( "Cycles" ); diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 6db47570f0..4f77b93742 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -36,7 +36,7 @@ enum class FreezeAction // [SAVEVERSION+] // This informs the auto updater that the users savestates will be invalidated. -static const u32 g_SaveVersion = (0x9A36 << 16) | 0x0000; +static const u32 g_SaveVersion = (0x9A37 << 16) | 0x0000; // the freezing data between submodules and core @@ -147,6 +147,18 @@ public: } } + void FreezeString(std::string& s) + { + // overwritten when loading + u32 length = static_cast(s.length()); + Freeze(length); + + if (IsLoading()) + s.resize(length); + + FreezeMem(s.data(), length); + } + uint GetCurrentPos() const { return m_idx; @@ -189,8 +201,7 @@ public: protected: void Init( VmStateBuffer* memblock ); - // Load/Save functions for the various components of our glorious emulator! - + void vmFreeze(); void mtvuFreeze(); void rcntFreeze(); void vuMicroFreeze(); diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 5c0215fd27..6c386104e7 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -346,34 +346,3 @@ void SysClearExecutionCache() dVifReset(1); } } - -// This function returns part of EXTINFO data of the BIOS rom -// This module contains information about Sony build environment at offst 0x10 -// first 15 symbols is build date/time that is unique per rom and can be used as unique serial -// Example for romver 0160EC20010704 -// 20010704-160707,ROMconf,PS20160EC20010704.bin,kuma@rom-server/~/f10k/g/app/rom -// 20010704-160707 can be used as unique ID for Bios -std::string SysGetBiosDiscID() -{ - if (!BiosSerial.empty()) - return BiosSerial; - else - return {}; -} - -// This function always returns a valid DiscID -- using the Sony serial when possible, and -// falling back on the CRC checksum of the ELF binary if the PS2 software being run is -// homebrew or some other serial-less item. -std::string SysGetDiscID() -{ - if (!DiscSerial.empty()) - return DiscSerial; - - if (!ElfCRC) - { - // system is currently running the BIOS - return SysGetBiosDiscID(); - } - - return StringUtil::StdStringFromFormat("%08x", ElfCRC); -} diff --git a/pcsx2/System.h b/pcsx2/System.h index cfb0e4ca86..9d020ce3a7 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -144,9 +144,6 @@ extern SysCpuProviderPack& GetCpuProviders(); extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs. extern void SysClearExecutionCache(); // clears recompiled execution caches! -extern std::string SysGetBiosDiscID(); -extern std::string SysGetDiscID(); - extern SysMainMemory& GetVmMemory(); extern void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR); diff --git a/pcsx2/USB/usb-lightgun/guncon2.cpp b/pcsx2/USB/usb-lightgun/guncon2.cpp index 84943709df..c32328048a 100644 --- a/pcsx2/USB/usb-lightgun/guncon2.cpp +++ b/pcsx2/USB/usb-lightgun/guncon2.cpp @@ -332,7 +332,7 @@ namespace usb_lightgun void GunCon2State::AutoConfigure() { - const std::string serial(VMManager::GetGameSerial()); + const std::string serial = VMManager::GetDiscSerial(); for (const GameConfig& gc : s_game_config) { if (serial != gc.serial) diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 5d979ccd95..243888b3cd 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -86,14 +86,6 @@ namespace VMManager { - enum class UpdateGameReason - { - Resetting, - GameStarting, - LoadingState, - SwappingDisc, - }; - static void ApplyGameFixes(); static bool UpdateGameSettingsLayer(); static void CheckForConfigChanges(const Pcsx2Config& old_config); @@ -107,11 +99,16 @@ namespace VMManager static void EnforceAchievementsChallengeModeSettings(); static void LogUnsafeSettingsToConsole(const std::string& messages); static void WarnAboutUnsafeSettings(); + static void ResetFrameLimiterState(); static bool AutoDetectSource(const std::string& filename); - static bool ApplyBootParameters(VMBootParameters params, std::string* state_to_load); - static bool CheckBIOSAvailability(); - static void UpdateRunningGame(UpdateGameReason reason); + static void UpdateDiscDetails(bool booting); + static void ClearDiscDetails(); + static void HandleELFChange(bool verbose_patches_if_changed); + static void UpdateELFInfo(std::string elf_path); + static void ClearELFInfo(); + static void ReportGameChangeToHost(); + static bool HasBootedELF(); static std::string GetCurrentSaveStateFileName(s32 slot); static bool DoLoadState(const char* filename); @@ -124,7 +121,7 @@ namespace VMManager s32 slot_for_message); static void UpdateInhibitScreensaver(bool allow); - static void SaveSessionTime(); + static void SaveSessionTime(const std::string& prev_serial); static void ReloadPINE(); static void SetTimerResolutionIncreased(bool enabled); @@ -152,16 +149,22 @@ static std::deque s_save_state_threads; static std::mutex s_save_state_threads_mutex; static std::recursive_mutex s_info_mutex; -static std::string s_disc_path; -static u32 s_game_crc; -static u32 s_patches_crc; -static std::string s_game_serial; -static std::string s_game_name; +static std::string s_disc_serial; +static std::string s_disc_elf; +static std::string s_disc_version; +static std::string s_title; +static u32 s_disc_crc; +static u32 s_current_crc; +static u32 s_elf_entry_point = 0xFFFFFFFFu; +static std::string s_elf_path; +static std::pair s_elf_text_range; +static bool s_elf_executed = false; static std::string s_elf_override; static std::string s_input_profile_name; static u32 s_active_game_fixes = 0; static u32 s_frame_advance_count = 0; static u32 s_mxcsr_saved; +static bool s_fast_boot_requested = false; static bool s_gs_open_on_initialize = false; // Used to track play time. We use a monotonic timer here, in case of clock changes. @@ -265,25 +268,43 @@ bool VMManager::HasValidVM() std::string VMManager::GetDiscPath() { std::unique_lock lock(s_info_mutex); - return s_disc_path; + return CDVDsys_GetFile(CDVDsys_GetSourceType()); } -u32 VMManager::GetGameCRC() +std::string VMManager::GetDiscSerial() { std::unique_lock lock(s_info_mutex); - return s_game_crc; + return s_disc_serial; } -std::string VMManager::GetGameSerial() +std::string VMManager::GetDiscELF() { std::unique_lock lock(s_info_mutex); - return s_game_serial; + return s_disc_elf; } -std::string VMManager::GetGameName() +std::string VMManager::GetTitle() { std::unique_lock lock(s_info_mutex); - return s_game_name; + return s_title; +} + +u32 VMManager::GetDiscCRC() +{ + std::unique_lock lock(s_info_mutex); + return s_disc_crc; +} + +std::string VMManager::GetDiscVersion() +{ + std::unique_lock lock(s_info_mutex); + return s_disc_version; +} + +u32 VMManager::GetCurrentCRC() +{ + std::unique_lock lock(s_info_mutex); + return s_current_crc; } bool VMManager::Internal::CPUThreadInitialize() @@ -455,11 +476,17 @@ void VMManager::LoadSettings() void VMManager::ApplyGameFixes() { s_active_game_fixes = 0; + if (!HasBootedELF()) + { + // Instant DMA needs to be on for this BIOS (font rendering is broken without it, possible cache issues). + EmuConfig.Gamefixes.InstantDMAHack = true; - if (s_game_crc == 0) + // Disable user's manual hardware fixes, it might be problematic. + EmuConfig.GS.ManualUserHacks = false; return; + } - const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_game_serial); + const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_disc_serial); if (!game) return; @@ -513,7 +540,7 @@ void VMManager::Internal::UpdateEmuFolders() if (VMManager::HasValidVM()) { if (EmuFolders::Cheats != old_cheats_directory || EmuFolders::Patches != old_patches_directory) - Patch::ReloadPatches(s_game_serial, s_game_crc, true, false, true, true); + Patch::ReloadPatches(s_disc_serial, s_current_crc, true, false, true, true); if (EmuFolders::MemoryCards != old_memcards_directory) { @@ -589,19 +616,19 @@ std::string VMManager::GetSerialForGameSettings() // If we're running an ELF, we don't want to use the serial for any ISO override // for game settings, since the game settings is where we define the override. std::unique_lock lock(s_info_mutex); - return s_elf_override.empty() ? std::string(s_game_serial) : std::string(); + return s_elf_override.empty() ? std::string(s_disc_serial) : std::string(); } bool VMManager::UpdateGameSettingsLayer() { std::unique_ptr new_interface; - if (s_game_crc != 0 && Host::GetBaseBoolSettingValue("EmuCore", "EnablePerGameSettings", true)) + if (s_disc_crc != 0 && EmuConfig.EnablePerGameSettings) { - std::string filename(GetGameSettingsPath(GetSerialForGameSettings(), s_game_crc)); + std::string filename(GetGameSettingsPath(GetSerialForGameSettings(), s_disc_crc)); if (!FileSystem::FileExists(filename.c_str())) { // try the legacy format (crc.ini) - filename = GetGameSettingsPath({}, s_game_crc); + filename = GetGameSettingsPath({}, s_disc_crc); } if (FileSystem::FileExists(filename.c_str())) @@ -673,106 +700,206 @@ bool VMManager::UpdateGameSettingsLayer() return true; } -void VMManager::UpdateRunningGame(UpdateGameReason reason) +void VMManager::UpdateDiscDetails(bool booting) { - // The CRC can be known before the game actually starts (at the bios), so when - // we have the CRC but we're still at the bios and the settings are changed - // (e.g. the user presses TAB to speed up emulation), we don't want to apply the - // settings as if the game is already running (title, loadeding patches, etc). - u32 new_crc; - std::string new_serial; - if (!GSDumpReplayer::IsReplayingDump()) - { - const bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted)); - new_crc = ingame ? ElfCRC : 0; - new_serial = ingame ? SysGetDiscID() : SysGetBiosDiscID(); - } - else - { - new_crc = GSDumpReplayer::GetDumpCRC(); - new_serial = GSDumpReplayer::GetDumpSerial(); - } - - if (reason != UpdateGameReason::Resetting && s_game_crc == new_crc && s_game_serial == new_serial) - return; - + std::string memcardFilters; { + // Only need to protect writes with the mutex. std::unique_lock lock(s_info_mutex); - SaveSessionTime(); - s_game_serial = std::move(new_serial); - s_game_crc = new_crc; - s_game_name.clear(); + const std::string old_serial = std::move(s_disc_serial); + const u32 old_crc = s_disc_crc; + bool serial_is_valid = false; + std::string title; - std::string memcardFilters; - - if (s_game_crc == 0) + if (GSDumpReplayer::IsReplayingDump()) { - s_game_name = "Booting PS2 BIOS..."; + s_disc_serial = GSDumpReplayer::GetDumpSerial(); + s_disc_crc = GSDumpReplayer::GetDumpCRC(); + s_disc_elf = {}; + s_disc_version = {}; + serial_is_valid = !s_disc_serial.empty(); } - else if (const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_game_serial)) + else if (CDVDsys_GetSourceType() != CDVD_SourceType::NoDisc) { - if (!s_elf_override.empty()) - s_game_name = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override)); - else - s_game_name = game->name; - - memcardFilters = game->memcardFiltersAsString(); + cdvdGetDiscInfo(&s_disc_serial, &s_disc_elf, &s_disc_version, &s_disc_crc, nullptr); + serial_is_valid = !s_disc_serial.empty(); + } + else if (!s_elf_override.empty()) + { + s_disc_serial = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override)); + s_disc_version = {}; + s_disc_crc = 0; // set below + title = s_disc_serial; } else { - Console.Warning(fmt::format("Serial '{}' not found in GameDB.", s_game_serial)); + s_disc_serial = BiosSerial; + s_disc_version = {}; + s_disc_crc = 0; + title = fmt::format("PS2 BIOS ({})", BiosZone); } - sioSetGameSerial(memcardFilters.empty() ? s_game_serial : memcardFilters); + // If we're booting an ELF, use its CRC, not the disc (if any). + if (!s_elf_override.empty()) + s_disc_crc = cdvdGetElfCRC(s_elf_override); - // If we don't reset the timer here, when using folder memcards the reindex will cause an eject, - // which a bunch of games don't like since they access the memory card on boot. - if (reason == UpdateGameReason::GameStarting || reason == UpdateGameReason::Resetting) - AutoEject::ClearAll(); + if (!booting && s_disc_serial == old_serial && s_disc_crc == old_crc) + { + Console.WriteLn("Skipping disc details update, no change."); + return; + } + + SaveSessionTime(old_serial); + + if (serial_is_valid) + { + if (const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_disc_serial)) + { + // Append the ELF override if we're using it with a disc. + if (!s_elf_override.empty()) + { + title = fmt::format( + "{} [{}]", game->name, Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override))); + } + else + { + title = game->name; + } + + memcardFilters = game->memcardFiltersAsString(); + } + else + { + Console.Warning(fmt::format("Serial '{}' not found in GameDB.", s_disc_serial)); + + // Use ELF title, otherwise the serial. + if (!s_elf_override.empty()) + { + title = fmt::format("Unknown Game: {} [{}]", s_disc_serial, + Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override))); + } + else + { + title = fmt::format("Unknown Game: {}", s_disc_serial); + } + } + } + else if (title.empty()) + { + title = "Unknown Game"; + } + + s_title = std::move(title); } - Console.WriteLn(Color_StrongGreen, "Game Changed:"); - Console.WriteLn(Color_StrongGreen, fmt::format(" Name: {}", s_game_name)); - Console.WriteLn(Color_StrongGreen, fmt::format(" Serial: {}", s_game_serial)); - Console.WriteLn(Color_StrongGreen, fmt::format(" CRC: {:08X}", s_game_crc)); + Console.WriteLn(Color_StrongGreen, + fmt::format("Disc changed to {}.", Path::GetFileName(CDVDsys_GetFile(CDVDsys_GetSourceType())))); + Console.WriteLn(Color_StrongGreen, fmt::format(" Name: {}", s_title)); + Console.WriteLn(Color_StrongGreen, fmt::format(" Serial: {}", s_disc_serial)); + Console.WriteLn(Color_StrongGreen, fmt::format(" Version: {}", s_disc_version)); + Console.WriteLn(Color_StrongGreen, fmt::format(" CRC: {:08X}", s_disc_crc)); - // When resetting, patches need to get removed here, because there's no entry point being compiled. - if (reason == UpdateGameReason::Resetting || reason == UpdateGameReason::LoadingState) - Patch::ReloadPatches(s_game_serial, s_game_crc, false, false, false, false); + sioSetGameSerial(memcardFilters.empty() ? s_disc_serial : memcardFilters); + + // If we don't reset the timer here, when using folder memcards the reindex will cause an eject, + // which a bunch of games don't like since they access the memory card on boot. + if (booting) + AutoEject::ClearAll(); UpdateGameSettingsLayer(); - - // Must be done before ApplySettings(), so WS/NI configs are picked up. - // Actual patch files get loaded on the entry point compiling. - Patch::UpdateActivePatches(true, false, s_game_crc != 0); - ApplySettings(); - if (reason != UpdateGameReason::LoadingState) - { - // Clear the memory card eject notification again when booting for the first time, or starting. - // Otherwise, games think the card was removed on boot. - if (reason == UpdateGameReason::GameStarting || reason == UpdateGameReason::Resetting) - AutoEject::ClearAll(); - - MIPSAnalyst::ScanForFunctions( - R5900SymbolMap, ElfTextRange.first, ElfTextRange.first + ElfTextRange.second, true); - R5900SymbolMap.UpdateActiveSymbols(); - R3000SymbolMap.UpdateActiveSymbols(); - } + // Patches are game-dependent, thus should get applied after game settings ia loaded. + Patch::ReloadPatches(s_disc_serial, HasBootedELF() ? s_current_crc : 0, true, true, false, false); // Per-game ini enabling of hardcore mode. We need to re-enforce the settings if so. - if (reason == UpdateGameReason::GameStarting && Achievements::ResetChallengeMode()) + if (booting && Achievements::ResetChallengeMode()) ApplySettings(); - GetMTGS().SendGameCRC(new_crc); - - FullscreenUI::GameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc); - Achievements::GameChanged(s_game_crc); + ReportGameChangeToHost(); + Achievements::GameChanged(s_disc_crc, s_current_crc); ReloadPINE(); UpdateDiscordPresence(Achievements::GetRichPresenceString()); +} - Host::OnGameChanged(s_disc_path, s_elf_override, s_game_serial, s_game_name, s_game_crc); +void VMManager::ClearDiscDetails() +{ + std::unique_lock lock(s_info_mutex); + s_disc_crc = 0; + s_title = {}; + s_disc_version = {}; + s_disc_elf = {}; + s_disc_serial = {}; +} + +void VMManager::HandleELFChange(bool verbose_patches_if_changed) +{ + // Classic chicken and egg problem here. We don't want to update the running game + // until the game entry point actually runs, because that can update settings, which + // can flush the JIT, etc. But we need to apply patches for games where the entry + // point is in the patch (e.g. WRC 4). So. Gross, but the only way to handle it really. + const u32 crc_to_report = HasBootedELF() ? s_current_crc : 0; + + ReportGameChangeToHost(); + Achievements::GameChanged(s_disc_crc, crc_to_report); + + Console.WriteLn(Color_StrongOrange, fmt::format("ELF changed, active CRC {:08X} ({})", crc_to_report, s_elf_path)); + Patch::ReloadPatches(s_disc_serial, crc_to_report, false, false, false, verbose_patches_if_changed); + ApplySettings(); + + MIPSAnalyst::ScanForFunctions( + R5900SymbolMap, s_elf_text_range.first, s_elf_text_range.first + s_elf_text_range.second, true); + R5900SymbolMap.UpdateActiveSymbols(); + R3000SymbolMap.UpdateActiveSymbols(); +} + +void VMManager::UpdateELFInfo(std::string elf_path) +{ + try + { + std::unique_ptr elfptr = cdvdLoadElf(elf_path, false); + elfptr->loadHeaders(); + s_current_crc = elfptr->getCRC(); + s_elf_entry_point = elfptr->getEntryPoint(); + s_elf_text_range = elfptr->getTextRange(); + s_elf_path = std::move(elf_path); + return; + } + catch ([[maybe_unused]] Exception::FileNotFound& e) + { + } + catch (Exception::BadStream& ex) + { + Console.Error(ex.FormatDiagnosticMessage()); + } + + Console.Error(fmt::format("Failed to read ELF being loaded: {}", elf_path)); + s_elf_path = {}; + s_elf_text_range = {}; + s_elf_entry_point = 0xFFFFFFFFu; + s_current_crc = 0; +} + +void VMManager::ClearELFInfo() +{ + s_current_crc = 0; + s_elf_executed = false; + s_elf_text_range = {}; + s_elf_path = {}; + s_elf_entry_point = 0xFFFFFFFFu; +} + +void VMManager::ReportGameChangeToHost() +{ + const std::string& disc_path = CDVDsys_GetFile(CDVDsys_GetSourceType()); + const u32 crc_to_report = HasBootedELF() ? s_current_crc : 0; + FullscreenUI::GameChanged(disc_path, s_disc_serial, s_title, s_disc_crc, crc_to_report); + Host::OnGameChanged(s_title, s_elf_override, disc_path, s_disc_serial, s_disc_crc, crc_to_report); +} + +bool VMManager::HasBootedELF() +{ + return s_current_crc != 0 && s_elf_executed; } static LimiterModeType GetInitialLimiterMode() @@ -800,12 +927,11 @@ bool VMManager::AutoDetectSource(const std::string& filename) { // alternative way of booting an elf, change the elf override, and (optionally) use the disc // specified in the game settings. - std::string disc_path(GetDiscOverrideFromGameSettings(filename)); + std::string disc_path = GetDiscOverrideFromGameSettings(filename); if (!disc_path.empty()) { - CDVDsys_SetFile(CDVD_SourceType::Iso, disc_path); + CDVDsys_SetFile(CDVD_SourceType::Iso, std::move(disc_path)); CDVDsys_ChangeSource(CDVD_SourceType::Iso); - s_disc_path = std::move(disc_path); } else { @@ -820,7 +946,6 @@ bool VMManager::AutoDetectSource(const std::string& filename) // TODO: Maybe we should check if it's a valid iso here... CDVDsys_SetFile(CDVD_SourceType::Iso, filename); CDVDsys_ChangeSource(CDVD_SourceType::Iso); - s_disc_path = filename; return true; } } @@ -828,106 +953,11 @@ bool VMManager::AutoDetectSource(const std::string& filename) { // make sure we're not fast booting when we have no filename CDVDsys_ChangeSource(CDVD_SourceType::NoDisc); - EmuConfig.UseBOOT2Injection = false; + s_fast_boot_requested = false; return true; } } -bool VMManager::ApplyBootParameters(VMBootParameters params, std::string* state_to_load) -{ - const bool default_fast_boot = Host::GetBoolSettingValue("EmuCore", "EnableFastBoot", true); - EmuConfig.UseBOOT2Injection = params.fast_boot.value_or(default_fast_boot); - - s_elf_override = std::move(params.elf_override); - s_disc_path.clear(); - if (!params.save_state.empty()) - *state_to_load = std::move(params.save_state); - - // if we're loading an indexed save state, we need to get the serial/crc from the disc. - if (params.state_index.has_value()) - { - if (params.filename.empty()) - { - Host::ReportErrorAsync("Error", "Cannot load an indexed save state without a boot filename."); - return false; - } - - *state_to_load = GetSaveStateFileName(params.filename.c_str(), params.state_index.value()); - if (state_to_load->empty()) - { - Host::ReportErrorAsync("Error", "Could not resolve path indexed save state load."); - return false; - } - } - -#ifdef ENABLE_ACHIEVEMENTS - // Check for resuming with hardcore mode. - Achievements::ResetChallengeMode(); - if (!state_to_load->empty() && Achievements::ChallengeModeActive() && - !Achievements::ConfirmChallengeModeDisable("Resuming state")) - { - return false; - } -#endif - - // resolve source type - if (params.source_type.has_value()) - { - if (params.source_type.value() == CDVD_SourceType::Iso && !FileSystem::FileExists(params.filename.c_str())) - { - Host::ReportErrorAsync("Error", fmt::format("Requested filename '{}' does not exist.", params.filename)); - return false; - } - - // Use specified source type. - s_disc_path = std::move(params.filename); - CDVDsys_SetFile(params.source_type.value(), s_disc_path); - CDVDsys_ChangeSource(params.source_type.value()); - } - else - { - // Automatic type detection of boot parameter based on filename. - if (!AutoDetectSource(params.filename)) - return false; - } - - if (!s_elf_override.empty()) - { - if (!FileSystem::FileExists(s_elf_override.c_str())) - { - Host::ReportErrorAsync("Error", fmt::format("Requested boot ELF '{}' does not exist.", s_elf_override)); - return false; - } - - Hle_SetElfPath(s_elf_override.c_str()); - EmuConfig.UseBOOT2Injection = true; - } - else - { - Hle_ClearElfPath(); - } - - return true; -} - -bool VMManager::CheckBIOSAvailability() -{ - if (IsBIOSAvailable(EmuConfig.FullpathToBios())) - return true; - - // TODO: When we translate core strings, translate this. - - const char* message = "PCSX2 requires a PS2 BIOS in order to run.\n\n" - "For legal reasons, you *must* obtain a BIOS from an actual PS2 unit that you own (borrowing " - "doesn't count).\n\n" - "Once dumped, this BIOS image should be placed in the bios folder within the data directory " - "(Tools Menu -> Open Data Directory).\n\n" - "Please consult the FAQs and Guides for further instructions."; - - Host::ReportErrorAsync("Startup Error", message); - return false; -} - bool VMManager::Initialize(VMBootParameters boot_params) { const Common::Timer init_timer; @@ -947,28 +977,135 @@ bool VMManager::Initialize(VMBootParameters boot_params) if (GSDumpReplayer::IsReplayingDump()) GSDumpReplayer::Shutdown(); - s_vm_thread_handle = {}; + s_elf_override = {}; + ClearELFInfo(); + ClearDiscDetails(); + UpdateGameSettingsLayer(); s_state.store(VMState::Shutdown, std::memory_order_release); Host::OnVMDestroyed(); + ApplySettings(); }; std::string state_to_load; - if (!ApplyBootParameters(std::move(boot_params), &state_to_load)) - return false; - EmuConfig.LimiterMode = GetInitialLimiterMode(); + s_fast_boot_requested = boot_params.fast_boot.value_or(static_cast(EmuConfig.EnableFastBoot)); - // early out if we don't have a bios - if (!GSDumpReplayer::IsReplayingDump() && !CheckBIOSAvailability()) - return false; + s_elf_override = std::move(boot_params.elf_override); + if (!boot_params.save_state.empty()) + state_to_load = std::move(boot_params.save_state); + + // if we're loading an indexed save state, we need to get the serial/crc from the disc. + if (boot_params.state_index.has_value()) + { + if (boot_params.filename.empty()) + { + Host::ReportErrorAsync("Error", "Cannot load an indexed save state without a boot filename."); + return false; + } + + state_to_load = GetSaveStateFileName(boot_params.filename.c_str(), boot_params.state_index.value()); + if (state_to_load.empty()) + { + Host::ReportErrorAsync("Error", "Could not resolve path indexed save state load."); + return false; + } + } + + // resolve source type + if (boot_params.source_type.has_value()) + { + if (boot_params.source_type.value() == CDVD_SourceType::Iso && + !FileSystem::FileExists(boot_params.filename.c_str())) + { + Host::ReportErrorAsync( + "Error", fmt::format("Requested filename '{}' does not exist.", boot_params.filename)); + return false; + } + + // Use specified source type. + CDVDsys_SetFile(boot_params.source_type.value(), std::move(boot_params.filename)); + CDVDsys_ChangeSource(boot_params.source_type.value()); + } + else + { + // Automatic type detection of boot parameter based on filename. + if (!AutoDetectSource(boot_params.filename)) + return false; + } + + ScopedGuard close_cdvd_files = [] { + CDVDsys_ClearFiles(); + if (GSDumpReplayer::IsReplayingDump()) + GSDumpReplayer::Shutdown(); + }; + + // Playing GS dumps don't need a BIOS. + if (!GSDumpReplayer::IsReplayingDump()) + { + Console.WriteLn("Loading BIOS..."); + if (!LoadBIOS()) + { + // TODO: When we translate core strings, translate this. + + const char* message = + "PCSX2 requires a PS2 BIOS in order to run.\n\n" + "For legal reasons, you *must* obtain a BIOS from an actual PS2 unit that you own (borrowing " + "doesn't count).\n\n" + "Once dumped, this BIOS image should be placed in the bios folder within the data directory " + "(Tools Menu -> Open Data Directory).\n\n" + "Please consult the FAQs and Guides for further instructions."; + + Host::ReportErrorAsync("Startup Error", message); + return false; + } + } Console.WriteLn("Opening CDVD..."); if (!DoCDVDopen()) { Host::ReportErrorAsync("Startup Error", "Failed to initialize CDVD."); + CDVDsys_ClearFiles(); return false; } - ScopedGuard close_cdvd = [] { DoCDVDclose(); }; + + // Figure out which game we're running! This also loads game settings. + UpdateDiscDetails(true); + + if (!s_elf_override.empty()) + { + if (!FileSystem::FileExists(s_elf_override.c_str())) + { + Host::ReportErrorAsync("Error", fmt::format("Requested boot ELF '{}' does not exist.", s_elf_override)); + DoCDVDclose(); + CDVDsys_ClearFiles(); + return false; + } + + Hle_SetElfPath(s_elf_override.c_str()); + s_fast_boot_requested = true; + } + else + { + Hle_ClearElfPath(); + } + +#ifdef ENABLE_ACHIEVEMENTS + // Check for resuming with hardcore mode. + Achievements::ResetChallengeMode(); + if (!state_to_load.empty() && Achievements::ChallengeModeActive() && + !Achievements::ConfirmChallengeModeDisable("Resuming state")) + { + DoCDVDclose(); + CDVDsys_ClearFiles(); + return false; + } +#endif + + ScopedGuard close_cdvd = [] { + DoCDVDclose(); + CDVDsys_ClearFiles(); + }; + EmuConfig.LimiterMode = GetInitialLimiterMode(); Console.WriteLn("Opening GS..."); s_gs_open_on_initialize = GetMTGS().IsOpen(); @@ -1040,6 +1177,7 @@ bool VMManager::Initialize(VMBootParameters boot_params) close_spu2.Cancel(); close_gs.Cancel(); close_cdvd.Cancel(); + close_cdvd_files.Cancel(); close_state.Cancel(); #if defined(_M_X86) @@ -1064,8 +1202,6 @@ bool VMManager::Initialize(VMBootParameters boot_params) FullscreenUI::OnVMStarted(); UpdateInhibitScreensaver(EmuConfig.InhibitScreensaver); - UpdateRunningGame(UpdateGameReason::Resetting); - SetEmuThreadAffinities(); PerformanceMetrics::Clear(); @@ -1108,26 +1244,19 @@ void VMManager::Shutdown(bool save_resume_state) } { - LastELF.clear(); - DiscSerial.clear(); - ElfCRC = 0; - ElfEntry = 0; - ElfTextRange = {}; - std::unique_lock lock(s_info_mutex); - SaveSessionTime(); - s_disc_path.clear(); - s_elf_override.clear(); - s_game_crc = 0; - s_patches_crc = 0; - s_game_serial.clear(); - s_game_name.clear(); - Achievements::GameChanged(s_game_crc); - FullscreenUI::GameChanged(s_disc_path, s_game_serial, s_game_name, 0); + SaveSessionTime(s_disc_serial); + s_elf_override = {}; + ClearELFInfo(); + ClearDiscDetails(); + CDVDsys_ClearFiles(); + Achievements::GameChanged(0, 0); + FullscreenUI::GameChanged(s_title, std::string(), s_disc_serial, 0, 0); UpdateDiscordPresence(Achievements::GetRichPresenceString()); - Host::OnGameChanged(s_disc_path, s_elf_override, s_game_serial, s_game_name, 0); + Host::OnGameChanged(s_title, std::string(), std::string(), s_disc_serial, 0, 0); } s_active_game_fixes = 0; + s_fast_boot_requested = false; UpdateGameSettingsLayer(); @@ -1194,9 +1323,10 @@ void VMManager::Reset() return; #endif - const bool game_was_started = g_GameStarted; - - s_active_game_fixes = 0; + const bool elf_was_changed = (s_current_crc != 0); + ClearELFInfo(); + if (elf_was_changed) + HandleELFChange(false); SysClearExecutionCache(); memBindConditionalHandlers(); @@ -1204,10 +1334,6 @@ void VMManager::Reset() frameLimitReset(); cpuReset(); - // gameid change, so apply settings - if (game_was_started) - UpdateRunningGame(UpdateGameReason::Resetting); - if (g_InputRecording.isActive()) { g_InputRecording.handleReset(); @@ -1219,10 +1345,43 @@ void VMManager::Reset() s_state.store(VMState::Running, std::memory_order_release); } +void SaveStateBase::vmFreeze() +{ + const u32 prev_crc = s_current_crc; + const std::string prev_elf = s_elf_path; + const bool prev_elf_executed = s_elf_executed; + Freeze(s_current_crc); + FreezeString(s_elf_path); + Freeze(s_elf_executed); + + // We have to test all the variables here, because we could be loading a state created during ELF load, after the ELF has loaded. + if (IsLoading()) + { + // Might need new ELF info. + if (s_elf_path != prev_elf) + { + if (s_elf_path.empty()) + { + // Shouldn't have executed a non-existant ELF.. unless you load state created from a deleted ELF override I guess. + if (s_elf_executed) + Console.Error("Somehow executed a non-existant ELF"); + VMManager::ClearELFInfo(); + } + else + { + VMManager::UpdateELFInfo(std::move(s_elf_path)); + } + } + + if (s_current_crc != prev_crc || s_elf_path != prev_elf || s_elf_executed != prev_elf_executed) + VMManager::HandleELFChange(true); + } +} + std::string VMManager::GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot) { std::string filename; - if (game_crc != 0) + if (std::strlen(game_serial) > 0) { if (slot < 0) filename = fmt::format("{} ({:08X}).resume.p2s", game_serial, game_crc); @@ -1257,7 +1416,7 @@ bool VMManager::HasSaveStateInSlot(const char* game_serial, u32 game_crc, s32 sl std::string VMManager::GetCurrentSaveStateFileName(s32 slot) { std::unique_lock lock(s_info_mutex); - return GetSaveStateFileName(s_game_serial.c_str(), s_game_crc, slot); + return GetSaveStateFileName(s_disc_serial.c_str(), s_disc_crc, slot); } bool VMManager::DoLoadState(const char* filename) @@ -1269,7 +1428,6 @@ bool VMManager::DoLoadState(const char* filename) { Host::OnSaveStateLoading(filename); SaveState_UnzipFromDisk(filename); - UpdateRunningGame(UpdateGameReason::LoadingState); Host::OnSaveStateLoaded(filename, true); if (g_InputRecording.isActive()) { @@ -1533,6 +1691,7 @@ bool VMManager::ChangeDisc(CDVD_SourceType source, std::string path) } cdvd.Tray.cdvdActionSeconds = 1; cdvd.Tray.trayState = CDVD_DISC_OPEN; + UpdateDiscDetails(false); return result; } @@ -1611,7 +1770,34 @@ VsyncMode Host::GetEffectiveVSyncMode() return EmuConfig.GS.VsyncEnable; } -const std::string& VMManager::Internal::GetElfOverride() +bool VMManager::Internal::IsFastBootInProgress() +{ + return s_fast_boot_requested && !HasBootedELF(); +} + +void VMManager::Internal::DisableFastBoot() +{ + if (!s_fast_boot_requested) + return; + + s_fast_boot_requested = false; + + // Stop fast forwarding boot if enabled. + if (EmuConfig.EnableFastBootFastForward && !s_elf_executed) + ResetFrameLimiterState(); +} + +bool VMManager::Internal::HasBootedELF() +{ + return VMManager::HasBootedELF(); +} + +u32 VMManager::Internal::GetCurrentELFEntryPoint() +{ + return s_elf_entry_point; +} + +const std::string& VMManager::Internal::GetELFOverride() { return s_elf_override; } @@ -1621,27 +1807,46 @@ bool VMManager::Internal::IsExecutionInterrupted() return s_state.load(std::memory_order_relaxed) != VMState::Running || s_cpu_implementation_changed; } +void VMManager::Internal::ELFLoadingOnCPUThread(std::string elf_path) +{ + const bool was_running_bios = (s_current_crc == 0); + + UpdateELFInfo(std::move(elf_path)); + Console.WriteLn(Color_StrongBlue, fmt::format("ELF Loading: {}, Game CRC = {:08X}, EntryPoint = 0x{:08X}", + s_elf_path, s_current_crc, s_elf_entry_point)); + s_elf_executed = false; + + // Remove patches, if we're changing games, we don't want to be applying the patch for the old game while it's loading. + if (!was_running_bios) + { + Patch::ReloadPatches(s_disc_serial, 0, false, false, false, true); + ApplySettings(); + } +} + void VMManager::Internal::EntryPointCompilingOnCPUThread() { - // Classic chicken and egg problem here. We don't want to update the running game - // until the game entry point actually runs, because that can update settings, which - // can flush the JIT, etc. But we need to apply patches for games where the entry - // point is in the patch (e.g. WRC 4). So. Gross, but the only way to handle it really. - Patch::ReloadPatches(SysGetDiscID(), ElfCRC, false, false, false, true); - Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD); -} + if (s_elf_executed) + return; -void VMManager::Internal::GameStartingOnCPUThread() -{ - // See note above. - UpdateRunningGame(UpdateGameReason::GameStarting); - Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD); - Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1); -} + const bool reset_speed_limiter = (EmuConfig.EnableFastBootFastForward && IsFastBootInProgress()); -void VMManager::Internal::SwappingGameOnCPUThread() -{ - UpdateRunningGame(UpdateGameReason::SwappingDisc); + Console.WriteLn( + Color_StrongGreen, fmt::format("ELF {} with entry point at 0x{} is executing.", s_elf_path, s_elf_entry_point)); + s_elf_executed = true; + + if (reset_speed_limiter) + { + ResetFrameLimiterState(); + PerformanceMetrics::Reset(); + } + + HandleELFChange(true); + + Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD); + + // Toss all the recs, we're going to be executing new code. + SysClearExecutionCache(); } void VMManager::Internal::VSyncOnCPUThread() @@ -1731,9 +1936,7 @@ void VMManager::CheckForGSConfigChanges(const Pcsx2Config& old_config) if (EmuConfig.GS.FrameLimitEnable != old_config.GS.FrameLimitEnable) EmuConfig.LimiterMode = GetInitialLimiterMode(); - gsUpdateFrequency(EmuConfig); - UpdateVSyncRate(true); - frameLimitReset(); + ResetFrameLimiterState(); GetMTGS().ApplySettings(); } @@ -1818,16 +2021,22 @@ void VMManager::CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config) std::string sioSerial; { std::unique_lock lock(s_info_mutex); - if (const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_game_serial)) + if (const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_disc_serial)) sioSerial = game->memcardFiltersAsString(); if (sioSerial.empty()) - sioSerial = s_game_serial; + sioSerial = s_disc_serial; } sioSetGameSerial(sioSerial); } void VMManager::CheckForMiscConfigChanges(const Pcsx2Config& old_config) { + if (EmuConfig.EnableFastBootFastForward && !old_config.EnableFastBootFastForward && + VMManager::Internal::IsFastBootInProgress()) + { + ResetFrameLimiterState(); + } + if (EmuConfig.InhibitScreensaver != old_config.InhibitScreensaver) UpdateInhibitScreensaver(EmuConfig.InhibitScreensaver && VMManager::GetState() == VMState::Running); @@ -1868,6 +2077,13 @@ void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config) Host::CheckForSettingsChanges(old_config); } +void VMManager::ResetFrameLimiterState() +{ + gsUpdateFrequency(EmuConfig); + UpdateVSyncRate(true); + frameLimitReset(); +} + void VMManager::ApplySettings() { Console.WriteLn("Applying settings..."); @@ -1901,6 +2117,18 @@ bool VMManager::ReloadGameSettings() return true; } +void VMManager::ReloadPatches(bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed) +{ + if (!HasValidVM()) + return; + + Patch::ReloadPatches(s_disc_serial, HasBootedELF() ? s_current_crc : 0, reload_files, reload_enabled_list, verbose, verbose_if_changed); + + // Might change widescreen mode. + if (Patch::ReloadPatchAffectingOptions()) + ApplySettings(); +} + void VMManager::EnforceAchievementsChallengeModeSettings() { if (!Achievements::ChallengeModeActive()) @@ -2080,16 +2308,20 @@ void VMManager::UpdateInhibitScreensaver(bool inhibit) Console.Warning("Failed to inhibit screen saver."); } -void VMManager::SaveSessionTime() +void VMManager::SaveSessionTime(const std::string& prev_serial) { + // Don't save time when running dumps, just messes up your list. + if (GSDumpReplayer::IsReplayingDump()) + return; + const u64 ctime = Common::Timer::GetCurrentValue(); - if (!s_game_serial.empty() && s_game_crc != 0) + if (!prev_serial.empty()) { // round up to seconds const std::time_t etime = static_cast(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time))); const std::time_t wtime = std::time(nullptr); - GameList::AddPlayedTimeForSerial(s_game_serial, wtime, etime); + GameList::AddPlayedTimeForSerial(prev_serial, wtime, etime); } s_session_start_time = ctime; @@ -2428,13 +2660,7 @@ void VMManager::UpdateDiscordPresence(const std::string& rich_presence) rp.largeImageKey = "4k-pcsx2"; rp.largeImageText = "PCSX2 Emulator"; rp.startTimestamp = std::time(nullptr); - - std::string details_string; - if (VMManager::HasValidVM()) - details_string = VMManager::GetGameName(); - else - details_string = "No Game Running"; - rp.details = details_string.c_str(); + rp.details = s_title.empty() ? "No Game Running" : s_title.c_str(); // Trim to 128 bytes as per Discord-RPC requirements std::string state_string; diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index a291d8c6ad..98fe1ff697 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -74,14 +74,23 @@ namespace VMManager /// Returns the path of the disc currently running. std::string GetDiscPath(); - /// Returns the crc of the executable currently running. - u32 GetGameCRC(); + /// Returns the serial of the disc currently running. + std::string GetDiscSerial(); - /// Returns the serial of the disc/executable currently running. - std::string GetGameSerial(); + /// Returns the path of the main ELF of the disc currently running. + std::string GetDiscELF(); /// Returns the name of the disc/executable currently running. - std::string GetGameName(); + std::string GetTitle(); + + /// Returns the CRC for the main ELF of the disc currently running. + u32 GetDiscCRC(); + + /// Returns the version of the disc currently running. + std::string GetDiscVersion(); + + /// Returns the crc of the executable currently running. + u32 GetCurrentCRC(); /// Loads global settings (i.e. EmuConfig). void LoadSettings(); @@ -107,6 +116,9 @@ namespace VMManager /// Reloads game specific settings, and applys any changes present. bool ReloadGameSettings(); + /// Reloads game patches. + void ReloadPatches(bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed); + /// Returns the save state filename for the given game serial/crc. std::string GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot); @@ -215,11 +227,22 @@ namespace VMManager /// Updates the variables in the EmuFolders namespace, reloading subsystems if needed. void UpdateEmuFolders(); - const std::string& GetElfOverride(); + /// Returns true if fast booting is active (requested but ELF not started). + bool IsFastBootInProgress(); + + /// Disables fast boot if it was requested, and found to be incompatible. + void DisableFastBoot(); + + /// Returns true if the current ELF has started executing. + bool HasBootedELF(); + + /// Returns the PC of the currently-executing ELF's entry point. + u32 GetCurrentELFEntryPoint(); + + const std::string& GetELFOverride(); bool IsExecutionInterrupted(); + void ELFLoadingOnCPUThread(std::string elf_path); void EntryPointCompilingOnCPUThread(); - void GameStartingOnCPUThread(); - void SwappingGameOnCPUThread(); void VSyncOnCPUThread(); } // namespace Internal } // namespace VMManager @@ -262,8 +285,8 @@ namespace Host void OnSaveStateSaved(const std::string_view& filename); /// Provided by the host; called when the running executable changes. - void OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial, - const std::string& game_name, u32 game_crc); + void OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path, + const std::string& disc_serial, u32 disc_crc, u32 current_crc); /// Provided by the host; called once per frame at guest vsync. void VSyncOnCPUThread(); diff --git a/pcsx2/ps2/BiosTools.cpp b/pcsx2/ps2/BiosTools.cpp index d3766ba507..40dfeea420 100644 --- a/pcsx2/ps2/BiosTools.cpp +++ b/pcsx2/ps2/BiosTools.cpp @@ -54,9 +54,11 @@ bool NoOSD; bool AllowParams1; bool AllowParams2; std::string BiosDescription; +std::string BiosZone; std::string BiosSerial; std::string BiosPath; BiosDebugInformation CurrentBiosInformation; +std::vector BiosRom; static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& description, u32& region, std::string& zone, std::string& serial) { @@ -177,12 +179,12 @@ static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& descriptio return true; } -template -void ChecksumIt(u32& result, const u8 (&srcdata)[_size]) +static void ChecksumIt(u32& result, u32 offset, u32 size) { - pxAssume((_size & 3) == 0); - for (size_t i = 0; i < _size / 4; ++i) - result ^= ((u32*)srcdata)[i]; + const u8* srcdata = &BiosRom[offset]; + pxAssume((size & 3) == 0); + for (size_t i = 0; i < size / 4; ++i) + result ^= reinterpret_cast(srcdata)[i]; } // Attempts to load a BIOS rom sub-component, by trying multiple combinations of base @@ -192,8 +194,7 @@ void ChecksumIt(u32& result, const u8 (&srcdata)[_size]) // Parameters: // ext - extension of the sub-component to load. Valid options are rom1 and rom2. // -template -static void LoadExtraRom(const char* ext, u8 (&dest)[_size]) +static void LoadExtraRom(const char* ext, u32 offset, u32 size) { // Try first a basic extension concatenation (normally results in something like name.bin.rom1) std::string Bios1(StringUtil::StdStringFromFormat("%s.%s", BiosPath.c_str(), ext)); @@ -210,8 +211,10 @@ static void LoadExtraRom(const char* ext, u8 (&dest)[_size]) } } + BiosRom.resize(offset + size); + auto fp = FileSystem::OpenManagedCFile(Bios1.c_str(), "rb"); - if (!fp || std::fread(dest, static_cast(std::min(_size, filesize)), 1, fp.get()) != 1) + if (!fp || std::fread(&BiosRom[offset], static_cast(std::min(size, filesize)), 1, fp.get()) != 1) { Console.Warning("BIOS Warning: %s could not be read (permission denied?)", ext); return; @@ -261,6 +264,29 @@ static std::string FindBiosImage() return std::string(); } +bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone) +{ + std::string serial; + const auto fp = FileSystem::OpenManagedCFile(filename, "rb"); + if (!fp) + return false; + + // FPS2BIOS is smaller and of variable size + //if (inway.Length() < 512*1024) return false; + return LoadBiosVersion(fp.get(), version, description, region, zone, serial); +} + +bool IsBIOSAvailable(const std::string& full_path) +{ + // We can't use EmuConfig here since it may not be loaded yet. + if (!full_path.empty() && FileSystem::FileExists(full_path.c_str())) + return true; + + // No bios configured or the configured name is missing, check for one in the BIOS directory. + const std::string auto_path(FindBiosImage()); + return !auto_path.empty() && FileSystem::FileExists(auto_path.c_str()); +} + // Loads the configured bios rom file into PS2 memory. PS2 memory must be allocated prior to // this method being called. // @@ -297,11 +323,12 @@ bool LoadBIOS() if (filesize <= 0) return false; - std::string zone; - LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, zone, BiosSerial); + LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, BiosZone, BiosSerial); + + BiosRom.resize(Ps2MemSize::Rom); if (FileSystem::FSeek64(fp.get(), 0, SEEK_SET) || - std::fread(eeMem->ROM, static_cast(std::min(Ps2MemSize::Rom, filesize)), 1, fp.get()) != 1) + std::fread(BiosRom.data(), static_cast(std::min(Ps2MemSize::Rom, filesize)), 1, fp.get()) != 1) { return false; } @@ -314,40 +341,31 @@ bool LoadBIOS() NoOSD = false; BiosChecksum = 0; - ChecksumIt(BiosChecksum, eeMem->ROM); + ChecksumIt(BiosChecksum, 0, Ps2MemSize::Rom); BiosPath = std::move(path); //injectIRX("host.irx"); //not fully tested; still buggy - LoadExtraRom("rom1", eeMem->ROM1); - LoadExtraRom("rom2", eeMem->ROM2); + LoadExtraRom("rom1", Ps2MemSize::Rom, Ps2MemSize::Rom1); + LoadExtraRom("rom2", Ps2MemSize::Rom + Ps2MemSize::Rom1, Ps2MemSize::Rom2); + return true; +} + +void CopyBIOSToMemory() +{ + if (BiosRom.size() >= Ps2MemSize::Rom) + { + std::memcpy(eeMem->ROM, BiosRom.data(), sizeof(eeMem->ROM)); + if (BiosRom.size() >= (Ps2MemSize::Rom + Ps2MemSize::Rom1)) + { + std::memcpy(eeMem->ROM1, BiosRom.data() + Ps2MemSize::Rom, sizeof(eeMem->ROM1)); + if (BiosRom.size() >= (Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2)) + std::memcpy(eeMem->ROM2, BiosRom.data() + Ps2MemSize::Rom + Ps2MemSize::Rom1, sizeof(eeMem->ROM2)); + } + } if (EmuConfig.CurrentIRX.length() > 3) LoadIrx(EmuConfig.CurrentIRX, &eeMem->ROM[0x3C0000], sizeof(eeMem->ROM) - 0x3C0000); CurrentBiosInformation.eeThreadListAddr = 0; - return true; -} - -bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone) -{ - std::string serial; - const auto fp = FileSystem::OpenManagedCFile(filename, "rb"); - if (!fp) - return false; - - // FPS2BIOS is smaller and of variable size - //if (inway.Length() < 512*1024) return false; - return LoadBiosVersion(fp.get(), version, description, region, zone, serial); -} - -bool IsBIOSAvailable(const std::string& full_path) -{ - // We can't use EmuConfig here since it may not be loaded yet. - if (!full_path.empty() && FileSystem::FileExists(full_path.c_str())) - return true; - - // No bios configured or the configured name is missing, check for one in the BIOS directory. - const std::string auto_path(FindBiosImage()); - return !auto_path.empty() && FileSystem::FileExists(auto_path.c_str()); } diff --git a/pcsx2/ps2/BiosTools.h b/pcsx2/ps2/BiosTools.h index 82d8f81c83..7242d49642 100644 --- a/pcsx2/ps2/BiosTools.h +++ b/pcsx2/ps2/BiosTools.h @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -14,7 +14,9 @@ */ #pragma once + #include +#include const u32 ThreadListInstructions[3] = { @@ -30,6 +32,7 @@ struct BiosDebugInformation u32 iopModListAddr; }; +// TODO: namespace this extern BiosDebugInformation CurrentBiosInformation; extern u32 BiosVersion; // Used by CDVD extern u32 BiosRegion; // Used by CDVD @@ -38,9 +41,23 @@ extern bool AllowParams1; extern bool AllowParams2; extern u32 BiosChecksum; extern std::string BiosDescription; +extern std::string BiosZone; + +// This function returns part of EXTINFO data of the BIOS rom +// This module contains information about Sony build environment at offst 0x10 +// first 15 symbols is build date/time that is unique per rom and can be used as unique serial +// Example for romver 0160EC20010704 +// 20010704-160707,ROMconf,PS20160EC20010704.bin,kuma@rom-server/~/f10k/g/app/rom +// 20010704-160707 can be used as unique ID for Bios extern std::string BiosSerial; extern std::string BiosPath; -extern bool LoadBIOS(); + +// Copies of the BIOS ROM. Because the EE can write to the ROM area, we need to copy it on reset. +// If we ever support read-only physical mappings, we can remove this. +extern std::vector BiosRom; + extern bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone); extern bool IsBIOSAvailable(const std::string& full_path); +extern bool LoadBIOS(); +extern void CopyBIOSToMemory(); diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index ffe71fa2ef..d59cf3c023 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -2220,6 +2220,9 @@ static void recRecompile(const u32 startpc) if (recPtr >= (recMem->GetPtrEnd() - _64kb)) eeRecNeedsReset = true; + if (HWADDR(startpc) == VMManager::Internal::GetCurrentELFEntryPoint()) + VMManager::Internal::EntryPointCompilingOnCPUThread(); + if (eeRecNeedsReset) { eeRecNeedsReset = false; @@ -2229,9 +2232,6 @@ static void recRecompile(const u32 startpc) xSetPtr(recPtr); recPtr = xGetAlignedCallTarget(); - if (0x8000d618 == startpc) - DbgCon.WriteLn("Compiling block @ 0x%08x", startpc); - s_pCurBlock = PC_GETBLOCK(startpc); pxAssert(s_pCurBlock->GetFnptr() == (uptr)JITCompile || s_pCurBlock->GetFnptr() == (uptr)JITCompileInBlock); @@ -2254,7 +2254,7 @@ static void recRecompile(const u32 startpc) if (g_eeloadMain && HWADDR(startpc) == HWADDR(g_eeloadMain)) { xFastCall((void*)eeloadHook); - if (g_SkipBiosHack) + if (VMManager::Internal::IsFastBootInProgress()) { // There are four known versions of EELOAD, identifiable by the location of the 'jal' to the EELOAD function which // calls ExecPS2(). The function itself is at the same address in all BIOSs after v1.00-v1.10. @@ -2274,14 +2274,6 @@ static void recRecompile(const u32 startpc) if (g_eeloadExec && HWADDR(startpc) == HWADDR(g_eeloadExec)) xFastCall((void*)eeloadHook2); - // this is the only way patches get applied, doesn't depend on a hack - if (g_GameLoading && HWADDR(startpc) == ElfEntry) - { - Console.WriteLn("Elf entry point @ 0x%08x about to get recompiled. Load patches first.", startpc); - xFastCall((void*)eeGameStarting); - VMManager::Internal::EntryPointCompilingOnCPUThread(); - } - g_branch = 0; // reset recomp state variables diff --git a/tests/ctest/core/StubHost.cpp b/tests/ctest/core/StubHost.cpp index cf4e24b308..62a9565138 100644 --- a/tests/ctest/core/StubHost.cpp +++ b/tests/ctest/core/StubHost.cpp @@ -137,8 +137,8 @@ void Host::OnVMResumed() { } -void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial, - const std::string& game_name, u32 game_crc) +void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path, + const std::string& disc_serial, u32 disc_crc, u32 current_crc) { }