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.
This commit is contained in:
Stenzek 2023-06-13 22:43:11 +10:00 committed by Connor McLaughlin
parent 0cf4cb6e4f
commit 36c27188a4
50 changed files with 1195 additions and 984 deletions

View File

@ -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, void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
const std::string& game_name, u32 game_crc) const std::string& disc_serial, u32 disc_crc, u32 current_crc)
{ {
} }

View File

@ -746,9 +746,9 @@ void MainWindow::updateWindowTitle()
{ {
QString suffix(QtHost::GetAppConfigSuffix()); QString suffix(QtHost::GetAppConfigSuffix());
QString main_title(QtHost::GetAppNameAndVersion() + suffix); 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; display_title = main_title;
else if (isRenderingToMain()) else if (isRenderingToMain())
main_title = display_title; main_title = display_title;
@ -920,7 +920,7 @@ bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b
return true; return true;
// If we don't have a crc, we can't save state. // 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; bool save_state = allow_save_to_state && default_save_to_state;
// Only confirm on UI thread because we need to display a msgbox. // Only confirm on UI thread because we need to display a msgbox.
@ -1213,15 +1213,15 @@ void MainWindow::onChangeDiscMenuAboutToHide()
void MainWindow::onLoadStateMenuAboutToShow() void MainWindow::onLoadStateMenuAboutToShow()
{ {
m_ui.menuLoadState->clear(); m_ui.menuLoadState->clear();
updateSaveStateMenusEnableState(!m_current_game_serial.isEmpty()); updateSaveStateMenusEnableState(!m_current_disc_serial.isEmpty());
populateLoadStateMenu(m_ui.menuLoadState, m_current_disc_path, m_current_game_serial, m_current_game_crc); populateLoadStateMenu(m_ui.menuLoadState, m_current_disc_path, m_current_disc_serial, m_current_disc_crc);
} }
void MainWindow::onSaveStateMenuAboutToShow() void MainWindow::onSaveStateMenuAboutToShow()
{ {
m_ui.menuSaveState->clear(); m_ui.menuSaveState->clear();
updateSaveStateMenusEnableState(!m_current_game_serial.isEmpty()); updateSaveStateMenusEnableState(!m_current_disc_serial.isEmpty());
populateSaveStateMenu(m_ui.menuSaveState, m_current_game_serial, m_current_game_crc); populateSaveStateMenu(m_ui.menuSaveState, m_current_disc_serial, m_current_disc_crc);
} }
void MainWindow::onViewToolbarActionToggled(bool checked) void MainWindow::onViewToolbarActionToggled(bool checked)
@ -1269,23 +1269,29 @@ void MainWindow::onViewGamePropertiesActionTriggered()
return; return;
// prefer to use a game list entry, if we have one, that way the summary is populated // 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(); auto lock = GameList::GetLock();
const GameList::Entry* entry = m_current_elf_override.isEmpty() ? const GameList::Entry* entry = GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData());
GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData()) :
GameList::GetEntryForPath(m_current_elf_override.toUtf8().constData());
if (entry) if (entry)
{ {
SettingsDialog::openGamePropertiesDialog( SettingsDialog::openGamePropertiesDialog(entry, entry->serial, entry->crc);
entry, m_current_elf_override.isEmpty() ? std::string_view(entry->serial) : std::string_view(), entry->crc);
return; return;
} }
} }
// open properties for the current running file (isn't in the game list) // open properties for the current running file (isn't in the game list)
if (m_current_game_crc != 0) if (m_current_disc_crc == 0)
SettingsDialog::openGamePropertiesDialog(nullptr, m_current_game_serial.toStdString(), m_current_game_crc); {
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() void MainWindow::onGitHubRepositoryActionTriggered()
@ -1602,13 +1608,15 @@ void MainWindow::onVMStopped()
m_game_list_widget->refresh(false); 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_elf_override = elf_override;
m_current_game_serial = serial; m_current_disc_path = disc_path;
m_current_game_name = name; m_current_disc_serial = serial;
m_current_game_crc = crc; m_current_disc_crc = disc_crc;
m_current_running_crc = crc;
updateWindowTitle(); updateWindowTitle();
updateSaveStateMenusEnableState(!serial.isEmpty()); updateSaveStateMenusEnableState(!serial.isEmpty());
} }

View File

@ -179,7 +179,8 @@ private Q_SLOTS:
void onVMResumed(); void onVMResumed();
void onVMStopped(); 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: protected:
void showEvent(QShowEvent* event) override; void showEvent(QShowEvent* event) override;
@ -279,11 +280,12 @@ private:
QMenu* m_settings_toolbar_menu = nullptr; QMenu* m_settings_toolbar_menu = nullptr;
QString m_current_disc_path; QString m_current_title;
QString m_current_elf_override; QString m_current_elf_override;
QString m_current_game_serial; QString m_current_disc_path;
QString m_current_game_name; QString m_current_disc_serial;
quint32 m_current_game_crc; quint32 m_current_disc_crc;
quint32 m_current_running_crc;
bool m_display_created = false; bool m_display_created = false;
bool m_relative_mouse_mode = false; bool m_relative_mouse_mode = false;

View File

@ -667,14 +667,7 @@ void EmuThread::reloadPatches()
return; return;
} }
if (!VMManager::HasValidVM()) VMManager::ReloadPatches(true, false, true, true);
return;
Patch::ReloadPatches(true, false, true, true);
// Might change widescreen mode.
if (Patch::ReloadPatchAffectingOptions())
applySettings();
} }
void EmuThread::reloadInputSources() void EmuThread::reloadInputSources()
@ -968,11 +961,11 @@ void Host::OnVMResumed()
emit g_emu_thread->onVMResumed(); emit g_emu_thread->onVMResumed();
} }
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial, void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
const std::string& game_name, u32 game_crc) const std::string& disc_serial, u32 disc_crc, u32 current_crc)
{ {
emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(elf_override), emit g_emu_thread->onGameChanged(QString::fromStdString(title), QString::fromStdString(elf_override),
QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc); QString::fromStdString(disc_path), QString::fromStdString(disc_serial), disc_crc, current_crc);
} }
void EmuThread::updatePerformanceMetrics(bool force) void EmuThread::updatePerformanceMetrics(bool force)
@ -1124,7 +1117,7 @@ void Host::VSyncOnCPUThread()
void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */) void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */)
{ {
if (g_emu_thread->isOnEmuThread()) if (block && g_emu_thread->isOnEmuThread())
{ {
// probably shouldn't ever happen, but just in case.. // probably shouldn't ever happen, but just in case..
function(); function();

View File

@ -137,7 +137,8 @@ Q_SIGNALS:
void onVMStopped(); void onVMStopped();
/// Provided by the host; called when the running executable changes. /// 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<QPair<QString, QString>>& devices); void onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices);
void onInputDeviceConnected(const QString& identifier, const QString& device_name); void onInputDeviceConnected(const QString& identifier, const QString& device_name);

View File

@ -30,23 +30,29 @@
BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent) BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent) : QWidget(parent)
, m_dialog(dialog)
{ {
SettingsInterface* sif = dialog->getSettingsInterface(); SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this); m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true); 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, SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory,
m_ui.resetSearchDirectory, "Folders", "Bios", Path::Combine(EmuFolders::DataRoot, "bios")); m_ui.resetSearchDirectory, "Folders", "Bios", Path::Combine(EmuFolders::DataRoot, "bios"));
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Checked"), dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Checked"),
tr("Patches the BIOS to skip the console's boot animation.")); 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(); refreshList();
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList); connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList); connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged); connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
connect(m_ui.fastBoot, &QCheckBox::stateChanged, this, &BIOSSettingsWidget::fastBootChanged);
} }
BIOSSettingsWidget::~BIOSSettingsWidget() BIOSSettingsWidget::~BIOSSettingsWidget()
@ -139,6 +145,12 @@ void BIOSSettingsWidget::listItemChanged(const QTreeWidgetItem* current, const Q
g_emu_thread->applySettings(); 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) BIOSSettingsWidget::RefreshThread::RefreshThread(BIOSSettingsWidget* parent, const QString& directory)
: QThread(parent) : QThread(parent)
, m_parent(parent) , m_parent(parent)

View File

@ -52,8 +52,11 @@ private Q_SLOTS:
void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous); void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous);
void listRefreshed(const QVector<BIOSInfo>& items); void listRefreshed(const QVector<BIOSInfo>& items);
void fastBootChanged();
private: private:
Ui::BIOSSettingsWidget m_ui; Ui::BIOSSettingsWidget m_ui;
SettingsDialog* m_dialog;
class RefreshThread final : public QThread class RefreshThread final : public QThread
{ {

View File

@ -136,11 +136,22 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QCheckBox" name="fastBoot"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="text"> <item>
<string>Fast Boot</string> <widget class="QCheckBox" name="fastBoot">
</property> <property name="text">
</widget> <string>Fast Boot</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fastBootFastForward">
<property name="text">
<string>Fast Forward Boot</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@ -92,8 +92,7 @@ namespace Achievements
static unsigned PeekMemoryBlock(unsigned address, unsigned char* buffer, unsigned num_bytes); static unsigned PeekMemoryBlock(unsigned address, unsigned char* buffer, unsigned num_bytes);
static void PokeMemory(unsigned address, unsigned num_bytes, void* ud, unsigned value); static void PokeMemory(unsigned address, unsigned num_bytes, void* ud, unsigned value);
static bool IsMastered(); static bool IsMastered();
static void ActivateLockedAchievements(); static void ActivateAchievementsAndLeaderboards();
static bool ActivateAchievement(Achievement* achievement);
static void DeactivateAchievement(Achievement* achievement); static void DeactivateAchievement(Achievement* achievement);
static void SendPing(); static void SendPing();
static void SendPlaying(); static void SendPlaying();
@ -150,7 +149,8 @@ namespace Achievements
static std::string s_username; static std::string s_username;
static std::string s_api_token; 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_path;
static std::string s_game_hash; static std::string s_game_hash;
static std::string s_game_title; static std::string s_game_title;
@ -379,7 +379,8 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard
while (!s_leaderboards.empty()) while (!s_leaderboards.empty())
{ {
Leaderboard& lb = s_leaderboards.back(); 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(); s_leaderboards.pop_back();
} }
@ -406,7 +407,7 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard
void Achievements::ClearGameHash() void Achievements::ClearGameHash()
{ {
s_last_game_crc = 0; s_last_disc_crc = 0;
std::string().swap(s_game_hash); std::string().swap(s_game_hash);
} }
@ -515,7 +516,7 @@ void Achievements::Initialize()
s_logged_in = (!s_username.empty() && !s_api_token.empty()); s_logged_in = (!s_username.empty() && !s_api_token.empty());
if (VMManager::HasValidVM()) if (VMManager::HasValidVM())
GameChanged(VMManager::GetGameCRC()); GameChanged(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
} }
void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_config) void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_config)
@ -645,7 +646,13 @@ void Achievements::SetChallengeMode(bool enabled)
achievement.locked = true; achievement.locked = true;
} }
for (Leaderboard& leaderboard : s_leaderboards) 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 // 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; return;
// this assumes that the CRC and ELF name has been loaded prior to the cheevos state (it should be). // 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) if (VMManager::GetDiscCRC() != s_last_disc_crc)
GameChanged(ElfCRC); GameChanged(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
@ -1118,7 +1125,7 @@ void Achievements::GetUserUnlocksCallback(s32 status_code, const std::string& co
} }
// start scanning for locked achievements // start scanning for locked achievements
ActivateLockedAchievements(); ActivateAchievementsAndLeaderboards();
DisplayAchievementSummary(); DisplayAchievementSummary();
SendPlaying(); SendPlaying();
UpdateRichPresence(); UpdateRichPresence();
@ -1227,16 +1234,10 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten
lboard.id = defn.id; lboard.id = defn.id;
lboard.title = defn.title; lboard.title = defn.title;
lboard.description = defn.description; lboard.description = defn.description;
lboard.memaddr = defn.definition;
lboard.format = defn.format; lboard.format = defn.format;
lboard.active = false;
s_leaderboards.push_back(std::move(lboard)); 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. // Parse rich presence.
@ -1265,7 +1266,7 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten
} }
else else
{ {
ActivateLockedAchievements(); ActivateAchievementsAndLeaderboards();
DisplayAchievementSummary(); DisplayAchievementSummary();
Host::OnAchievementsRefreshed(); Host::OnAchievementsRefreshed();
} }
@ -1399,7 +1400,7 @@ std::optional<std::vector<u8>> Achievements::ReadELFFromCurrentDisc(const std::s
std::string Achievements::GetGameHash() std::string Achievements::GetGameHash()
{ {
const std::string& elf_path = LastELF; const std::string elf_path = VMManager::GetDiscELF();
if (elf_path.empty()) if (elf_path.empty())
return {}; return {};
@ -1457,15 +1458,21 @@ void Achievements::GetGameIdCallback(s32 status_code, const std::string& content
GetPatches(game_id); GetPatches(game_id);
} }
void Achievements::GameChanged(u32 crc) void Achievements::GameChanged(u32 disc_crc, u32 crc)
{ {
std::unique_lock lock(s_achievements_mutex); std::unique_lock lock(s_achievements_mutex);
if (!s_active) if (!s_active)
return; return;
// avoid reading+hashing the executable if the crc hasn't changed // 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; return;
}
std::string game_hash(GetGameHash()); std::string game_hash(GetGameHash());
if (s_game_hash == game_hash) if (s_game_hash == game_hash)
@ -1481,7 +1488,8 @@ void Achievements::GameChanged(u32 crc)
ClearGameInfo(); ClearGameInfo();
ClearGameHash(); ClearGameHash();
s_last_game_crc = crc; s_last_disc_crc = disc_crc;
s_current_crc = crc;
s_game_hash = std::move(game_hash); s_game_hash = std::move(game_hash);
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
@ -1495,13 +1503,14 @@ void Achievements::GameChanged(u32 crc)
if (s_game_hash.empty()) if (s_game_hash.empty())
{ {
// when we're booting the bios, or shutting down, this will fail // 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::AddKeyedOSDMessage("retroachievements_disc_read_failed", "Failed to read executable from disc. Achievements disabled.",
Host::OSD_CRITICAL_ERROR_DURATION); Host::OSD_CRITICAL_ERROR_DURATION);
} }
s_last_game_crc = 0; s_last_disc_crc = 0;
s_current_crc = crc;
return; return;
} }
@ -1733,31 +1742,48 @@ bool Achievements::IsMastered()
return true; 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) for (Achievement& cheevo : s_achievements)
{ {
if (cheevo.locked) if (cheevo.active || !cheevo.locked)
ActivateAchievement(&cheevo); 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) for (Leaderboard& lb : s_leaderboards)
{
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)
{ {
Console.Error("Achievement %u memaddr parse error: %s", achievement->id, rc_error_str(err)); // Always track the leaderboard, if we don't have leaderboards enabled we just won't submit it.
return false; // 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) void Achievements::DeactivateAchievement(Achievement* achievement)
@ -2249,7 +2275,7 @@ void Achievements::RAIntegration::RACallbackRebuildMenu()
void Achievements::RAIntegration::RACallbackEstimateTitle(char* buf) 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); StringUtil::Strlcpy(buf, title, 256);
} }

View File

@ -62,7 +62,9 @@ namespace Achievements
u32 id; u32 id;
std::string title; std::string title;
std::string description; std::string description;
std::string memaddr;
int format; int format;
bool active;
}; };
struct LeaderboardEntry struct LeaderboardEntry
@ -131,7 +133,7 @@ namespace Achievements
bool Login(const char* username, const char* password); bool Login(const char* username, const char* password);
void Logout(); void Logout();
void GameChanged(u32 crc); void GameChanged(u32 disc_crc, u32 crc);
const std::string& GetGameTitle(); const std::string& GetGameTitle();
const std::string& GetGameIcon(); const std::string& GetGameIcon();

View File

@ -40,12 +40,6 @@
#include "Recording/InputRecording.h" #include "Recording/InputRecording.h"
#include "Host.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; cdvdStruct cdvd;
s64 PSXCLK = 36864000; 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. // 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<ElfObject> cdvdLoadElf(std::string filename, bool isPSXElf)
{ {
if (StringUtil::StartsWith(filename, "host:")) if (StringUtil::StartsWith(filename, "host:"))
{ {
std::string host_filename(filename.substr(5)); std::string host_filename(filename.substr(5));
s64 host_size = FileSystem::GetPathFileSize(host_filename.c_str()); s64 host_size = FileSystem::GetPathFileSize(host_filename.c_str());
return new ElfObject(std::move(host_filename), static_cast<u32>(std::max<s64>(host_size, 0)), isPSXElf); return std::make_unique<ElfObject>(std::move(host_filename), static_cast<u32>(std::max<s64>(host_size, 0)), isPSXElf);
} }
// Mimic PS2 behavior! // Mimic PS2 behavior!
@ -420,31 +414,13 @@ static __fi ElfObject* loadElf(std::string filename, bool isPSXElf)
filename += ";1"; 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; IsoFSCDVD isofs;
IsoFile file(isofs, filename); IsoFile file(isofs, filename);
return new ElfObject(std::move(filename), file, isPSXElf); return std::make_unique<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<ElfObject> 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).
} }
u32 cdvdGetElfCRC(const std::string& path) 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<int>(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<int>(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<int>(value.size()), value.data());
}
else if( key == "VER" )
{
Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Software version = %.*s",
static_cast<int>(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) static std::string ExecutablePathToSerial(const std::string& path)
{ {
// cdrom:\SCES_123.45;1 // cdrom:\SCES_123.45;1
@ -478,7 +532,7 @@ static std::string ExecutablePathToSerial(const std::string& path)
else else
{ {
// cdrom:SCES_123.45;1 // cdrom:SCES_123.45;1
pos = serial.rfind(':'); pos = path.rfind(':');
if (pos != std::string::npos) if (pos != std::string::npos)
serial = path.substr(pos + 1); serial = path.substr(pos + 1);
else else
@ -492,8 +546,11 @@ static std::string ExecutablePathToSerial(const std::string& path)
// check that it matches our expected format. // check that it matches our expected format.
// this maintains the old behavior of PCSX2. // 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(); serial.clear();
}
// SCES_123.45 -> SCES-12345 // SCES_123.45 -> SCES-12345
for (std::string::size_type pos = 0; pos < serial.size();) for (std::string::size_type pos = 0; pos < serial.size();)
@ -515,60 +572,62 @@ static std::string ExecutablePathToSerial(const std::string& path)
return serial; 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 std::string elfpath, version;
// and route them through the VM's exception handler. (needed for non-SEH platforms, such const CDVDDiscType disc_type = GetPS2ElfName(&elfpath, &version);
// as Linux/GCC)
DevCon.WriteLn(Color_Green, "Reload ELF");
try
{
std::string elfpath;
u32 discType = GetPS2ElfName(elfpath);
DiscSerial = ExecutablePathToSerial(elfpath);
// Use the serial from the disc (if any), and the ELF CRC of the override. // Don't bother parsing it if we don't need the CRC.
if (!elfoverride.empty()) if (out_crc)
{
u32 crc = 0;
if (disc_type == CDVDDiscType::PS2Disc || disc_type == CDVDDiscType::PS1Disc)
{ {
_reloadElfInfo(std::move(elfoverride)); try
return; {
const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc);
std::unique_ptr<ElfObject> 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) *out_crc = crc;
{
// 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));
} }
catch ([[maybe_unused]] Exception::FileNotFound& e)
if (out_serial)
{ {
Console.Error("Failed to load ELF info"); if (disc_type != CDVDDiscType::Other)
LastELF.clear(); *out_serial = ExecutablePathToSerial(elfpath);
DiscSerial.clear(); else
ElfCRC = 0; out_serial->clear();
ElfEntry = 0;
ElfTextRange = {};
return;
} }
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) void cdvdReadKey(u8, u16, u32 arg2, u8* key)
{ {
const std::string DiscSerial = VMManager::GetDiscSerial();
s32 numbers = 0, letters = 0; s32 numbers = 0, letters = 0;
u32 key_0_3; u32 key_0_3;
u8 key_4, key_14; u8 key_4, key_14;
cdvdReloadElfInfo();
// clear key values // clear key values
memset(key, 0, 16); memset(key, 0, 16);
@ -697,7 +756,7 @@ s32 cdvdCtrlTrayClose()
DevCon.WriteLn(Color_Green, "Close virtual disk tray"); 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)"); DevCon.WriteLn(Color_Green, "Media already loaded (fast boot)");
cdvdUpdateReady(CDVD_DRIVE_READY); cdvdUpdateReady(CDVD_DRIVE_READY);
@ -901,10 +960,6 @@ void cdvdReset()
cdvd.RTC.year = (u8)(curtime.tm_year - 100); // offset from 2000 cdvd.RTC.year = (u8)(curtime.tm_year - 100); // offset from 2000
} }
g_GameStarted = false;
g_GameLoading = false;
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
cdvdCtrlTrayClose(); cdvdCtrlTrayClose();
} }
@ -930,7 +985,7 @@ void cdvdNewDiskCB()
cdvdDetectDisk(); cdvdDetectDisk();
// If not ejected but we've swapped source pretend it got ejected // 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"); DevCon.WriteLn(Color_Green, "Ejecting media");
cdvdUpdateStatus(CDVD_STATUS_TRAY_OPEN); cdvdUpdateStatus(CDVD_STATUS_TRAY_OPEN);
@ -1448,12 +1503,6 @@ void cdvdUpdateTrayState()
cdvd.Tray.trayState = CDVD_DISC_SEEKING; cdvd.Tray.trayState = CDVD_DISC_SEEKING;
cdvdUpdateStatus(CDVD_STATUS_SEEK); cdvdUpdateStatus(CDVD_STATUS_SEEK);
cdvd.Tray.cdvdActionSeconds = 2; 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; break;
case CDVD_DISC_SEEKING: case CDVD_DISC_SEEKING:
cdvd.Spinning = true; cdvd.Spinning = true;
@ -2568,22 +2617,25 @@ static void cdvdWrite16(u8 rt) // SCOMMAND
// break; // break;
case 0x27: // GetPS1BootParam (0:13) - called only by China region PS2 models 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. const std::string DiscSerial = VMManager::GetDiscSerial();
SetSCMDResultSize(13); cdvd.SCMDResult[0] = 0;
cdvd.SCMDResult[0] = 0; cdvd.SCMDResult[1] = DiscSerial[0];
cdvd.SCMDResult[1] = DiscSerial[0]; cdvd.SCMDResult[2] = DiscSerial[1];
cdvd.SCMDResult[2] = DiscSerial[1]; cdvd.SCMDResult[3] = DiscSerial[2];
cdvd.SCMDResult[3] = DiscSerial[2]; cdvd.SCMDResult[4] = DiscSerial[3];
cdvd.SCMDResult[4] = DiscSerial[3]; cdvd.SCMDResult[5] = DiscSerial[4];
cdvd.SCMDResult[5] = DiscSerial[4]; cdvd.SCMDResult[6] = DiscSerial[5];
cdvd.SCMDResult[6] = DiscSerial[5]; cdvd.SCMDResult[7] = DiscSerial[6];
cdvd.SCMDResult[7] = DiscSerial[6]; cdvd.SCMDResult[8] = DiscSerial[7];
cdvd.SCMDResult[8] = DiscSerial[7]; cdvd.SCMDResult[9] = DiscSerial[9]; // Skipping dot here is required.
cdvd.SCMDResult[9] = DiscSerial[9]; // Skipping dot here is required. cdvd.SCMDResult[10] = DiscSerial[10];
cdvd.SCMDResult[10] = DiscSerial[10]; cdvd.SCMDResult[11] = DiscSerial[11];
cdvd.SCMDResult[11] = DiscSerial[11]; cdvd.SCMDResult[12] = DiscSerial[12];
cdvd.SCMDResult[12] = DiscSerial[12]; }
break; break;
// case 0x28: // cdvdman_call150 (1:1) - In V10 Bios // case 0x28: // cdvdman_call150 (1:1) - In V10 Bios

View File

@ -17,9 +17,12 @@
#include "CDVDcommon.h" #include "CDVDcommon.h"
#include <memory>
#include <string> #include <string>
#include <string_view> #include <string_view>
class ElfObject;
#define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */ #define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
#define itob(i) ((i) / 10 * 16 + (i) % 10) /* u_char to BCD */ #define itob(i) ((i) / 10 * 16 + (i) % 10) /* u_char to BCD */
@ -76,6 +79,13 @@ struct cdvdRTC
u8 year; u8 year;
}; };
enum class CDVDDiscType : u8
{
Other,
PS1Disc,
PS2Disc
};
enum TrayStates enum TrayStates
{ {
CDVD_DISC_ENGAGED, CDVD_DISC_ENGAGED,
@ -176,9 +186,11 @@ extern void cdvdNewDiskCB();
extern u8 cdvdRead(u8 key); extern u8 cdvdRead(u8 key);
extern void cdvdWrite(u8 key, u8 rt); 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 u32 cdvdGetElfCRC(const std::string& path);
extern std::unique_ptr<ElfObject> cdvdLoadElf(std::string filename, bool isPSXElf);
extern s32 cdvdCtrlTrayOpen(); extern s32 cdvdCtrlTrayOpen();
extern s32 cdvdCtrlTrayClose(); extern s32 cdvdCtrlTrayClose();
extern std::string DiscSerial;

View File

@ -346,6 +346,12 @@ CDVD_SourceType CDVDsys_GetSourceType()
return m_CurrentSourceType; 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) void CDVDsys_ChangeSource(CDVD_SourceType type)
{ {
if (CDVD != NULL) if (CDVD != NULL)
@ -375,14 +381,6 @@ bool DoCDVDopen()
CDVD->newDiskCB(cdvdNewDiskCB); 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); auto CurrentSourceType = enum_cast(m_CurrentSourceType);
int ret = CDVD->open(!m_SourceFilename[CurrentSourceType].empty() ? m_SourceFilename[CurrentSourceType].c_str() : nullptr); int ret = CDVD->open(!m_SourceFilename[CurrentSourceType].empty() ? m_SourceFilename[CurrentSourceType].c_str() : nullptr);
if (ret == -1) if (ret == -1)
@ -396,19 +394,14 @@ bool DoCDVDopen()
return true; return true;
} }
std::string somepick(Path::StripExtension(FileSystem::GetDisplayNameFromPath(m_SourceFilename[CurrentSourceType]))); std::string dump_name(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 if (dump_name.empty())
//Shouldn't the serial be available all time? Potentially need to look into Elfreloadinfo() reliability dump_name = "Untitled";
//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";
if (EmuConfig.CurrentBlockdump.empty()) if (EmuConfig.CurrentBlockdump.empty())
EmuConfig.CurrentBlockdump = FileSystem::GetWorkingDirectory(); EmuConfig.CurrentBlockdump = FileSystem::GetWorkingDirectory();
std::string temp(Path::Combine(EmuConfig.CurrentBlockdump, somepick)); std::string temp(Path::Combine(EmuConfig.CurrentBlockdump, dump_name));
#ifdef ENABLE_TIMESTAMPS #ifdef ENABLE_TIMESTAMPS
std::time_t curtime_t = std::time(nullptr); std::time_t curtime_t = std::time(nullptr);

View File

@ -158,6 +158,7 @@ extern void CDVDsys_ChangeSource(CDVD_SourceType type);
extern void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile); extern void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile);
extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype); extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype);
extern CDVD_SourceType CDVDsys_GetSourceType(); extern CDVD_SourceType CDVDsys_GetSourceType();
extern void CDVDsys_ClearFiles();
extern bool DoCDVDopen(); extern bool DoCDVDopen();
extern void DoCDVDclose(); extern void DoCDVDclose();

View File

@ -1244,14 +1244,15 @@ struct Pcsx2Config
EnablePINE : 1, // enables inter-process communication EnablePINE : 1, // enables inter-process communication
EnableWideScreenPatches : 1, EnableWideScreenPatches : 1,
EnableNoInterlacingPatches : 1, EnableNoInterlacingPatches : 1,
EnableFastBoot : 1,
EnableFastBootFastForward : 1,
EnablePerGameSettings : 1,
// TODO - Vaser - where are these settings exposed in the Qt UI? // TODO - Vaser - where are these settings exposed in the Qt UI?
EnableRecordingTools : 1, EnableRecordingTools : 1,
EnableGameFixes : 1, // enables automatic game fixes EnableGameFixes : 1, // enables automatic game fixes
SaveStateOnShutdown : 1, // default value for saving state on shutdown SaveStateOnShutdown : 1, // default value for saving state on shutdown
EnableDiscordPresence : 1, // enables discord rich presence integration EnableDiscordPresence : 1, // enables discord rich presence integration
InhibitScreensaver : 1, InhibitScreensaver : 1,
// when enabled uses BOOT2 injection, skipping sony bios splashes
UseBOOT2Injection : 1,
BackupSavestate : 1, BackupSavestate : 1,
SavestateZstdCompression : 1, SavestateZstdCompression : 1,
// enables simulated ejection of memory cards when loading savestates // enables simulated ejection of memory cards when loading savestates

View File

@ -22,40 +22,54 @@
#include "Elfheader.h" #include "Elfheader.h"
#include "DebugTools/SymbolMap.h" #include "DebugTools/SymbolMap.h"
u32 ElfCRC; #pragma pack(push, 1)
u32 ElfEntry; struct PSXEXEHeader
std::pair<u32,u32> ElfTextRange; {
std::string LastELF; char id[8]; // 0x000-0x007 PS-X EXE
bool isPSXElf; char pad1[8]; // 0x008-0x00F
std::string ElfVersion; 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. // 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") : data(isofile.getLength(), "ELF headers")
, filename(std::move(srcfile))
, header(*(ELF_HEADER*)data.GetPtr()) , header(*(ELF_HEADER*)data.GetPtr())
, filename(std::move(srcfile))
, isPSXElf(isPSXElf_)
{ {
checkElfSize(data.GetSizeInBytes()); checkElfSize(data.GetSizeInBytes());
readIso(isofile); 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") : data(hdrsize, "ELF headers")
, filename(std::move(srcfile))
, header(*(ELF_HEADER*)data.GetPtr()) , header(*(ELF_HEADER*)data.GetPtr())
, filename(std::move(srcfile))
, isPSXElf(isPSXElf_)
{ {
checkElfSize(data.GetSizeInBytes()); checkElfSize(data.GetSizeInBytes());
readFile(); readFile();
initElfHeaders(isPSXElf); initElfHeaders();
} }
void ElfObject::initElfHeaders(bool isPSXElf) void ElfObject::initElfHeaders()
{ {
if (isPSXElf) if (isPSXElf)
{
return; return;
}
DevCon.WriteLn("Initializing Elf: %d bytes", data.GetSizeInBytes()); DevCon.WriteLn("Initializing Elf: %d bytes", data.GetSizeInBytes());
@ -133,19 +147,57 @@ void ElfObject::initElfHeaders(bool isPSXElf)
//applyPatches(); //applyPatches();
} }
bool ElfObject::hasValidPSXHeader()
{
if (data.GetSizeInBytes() < sizeof(PSXEXEHeader))
return false;
const PSXEXEHeader* header = reinterpret_cast<const PSXEXEHeader*>(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<unsigned>(data.GetSizeInBytes() - sizeof(PSXEXEHeader)));
}
return true;
}
bool ElfObject::hasProgramHeaders() { return (proghead != NULL); } bool ElfObject::hasProgramHeaders() { return (proghead != NULL); }
bool ElfObject::hasSectionHeaders() { return (secthead != NULL); } bool ElfObject::hasSectionHeaders() { return (secthead != NULL); }
bool ElfObject::hasHeaders() { return (hasProgramHeaders() && hasSectionHeaders()); } bool ElfObject::hasHeaders() { return (hasProgramHeaders() && hasSectionHeaders()); }
u32 ElfObject::getEntryPoint()
{
if (isPSXElf)
{
if (hasValidPSXHeader())
return reinterpret_cast<const PSXEXEHeader*>(data.GetPtr())->initial_pc;
else
return 0xFFFFFFFFu;
}
else
{
return header.e_entry;
}
}
std::pair<u32,u32> ElfObject::getTextRange() std::pair<u32,u32> ElfObject::getTextRange()
{ {
for (int i = 0; i < header.e_phnum; i++) if (isPSXElf && hasProgramHeaders())
{ {
u32 start = proghead[i].p_vaddr; for (int i = 0; i < header.e_phnum; i++)
u32 size = proghead[i].p_memsz; {
const u32 start = proghead[i].p_vaddr;
const u32 size = proghead[i].p_memsz;
if (start <= header.e_entry && (start+size) > header.e_entry) if (start <= header.e_entry && (start + size) > header.e_entry)
return std::make_pair(start,size); return std::make_pair(start, size);
}
} }
return std::make_pair(0,0); return std::make_pair(0,0);
@ -310,82 +362,9 @@ void ElfObject::loadSectionHeaders()
void ElfObject::loadHeaders() void ElfObject::loadHeaders()
{ {
if (isPSXElf)
return;
loadProgramHeaders(); loadProgramHeaders();
loadSectionHeaders(); 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<int>(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<int>(value.size()), value.data());
name = value;
retype = 1;
}
else if( key == "VMODE" )
{
Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Disc region type = %.*s",
static_cast<int>(value.size()), value.data());
}
else if( key == "VER" )
{
Console.WriteLn( Color_Blue, "(SYSTEM.CNF) Software version = %.*s",
static_cast<int>(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;
}

View File

@ -122,24 +122,23 @@ class ElfObject
{ {
private: private:
SafeArray<u8> data; SafeArray<u8> data;
ELF_HEADER& header;
ELF_PHR* proghead = nullptr; ELF_PHR* proghead = nullptr;
ELF_SHR* secthead = nullptr; ELF_SHR* secthead = nullptr;
std::string filename; std::string filename;
bool isPSXElf;
void initElfHeaders(bool isPSXElf); void initElfHeaders();
bool hasValidPSXHeader();
void readIso(IsoFile& file); void readIso(IsoFile& file);
void readFile(); void readFile();
void checkElfSize(s64 elfsize); void checkElfSize(s64 elfsize);
public: public:
ELF_HEADER& header; ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_);
ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_);
// Destructor! bool IsPSXElf() const { return isPSXElf; }
// 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);
void loadProgramHeaders(); void loadProgramHeaders();
void loadSectionHeaders(); void loadSectionHeaders();
@ -150,17 +149,8 @@ class ElfObject
bool hasHeaders(); bool hasHeaders();
std::pair<u32,u32> getTextRange(); std::pair<u32,u32> getTextRange();
u32 getEntryPoint();
u32 getCRC(); u32 getCRC();
}; };
//------------------- //-------------------
extern void loadElfFile(const std::string& filename);
extern int GetPS2ElfName( std::string& dest );
extern u32 ElfCRC;
extern u32 ElfEntry;
extern std::pair<u32,u32> ElfTextRange;
extern std::string LastELF;
extern bool isPSXElf;
extern std::string ElfVersion;

View File

@ -21,6 +21,7 @@
#include "Gif_Unit.h" #include "Gif_Unit.h"
#include "Counters.h" #include "Counters.h"
#include "Config.h" #include "Config.h"
#include "VMManager.h"
using namespace Threading; using namespace Threading;
using namespace R5900; using namespace R5900;
@ -45,7 +46,8 @@ void gsReset()
void gsUpdateFrequency(Pcsx2Config& config) void gsUpdateFrequency(Pcsx2Config& config)
{ {
if (config.GS.FrameLimitEnable) if (config.GS.FrameLimitEnable &&
(!config.EnableFastBootFastForward || !VMManager::Internal::IsFastBootInProgress()))
{ {
switch (config.LimiterMode) switch (config.LimiterMode)
{ {

View File

@ -667,7 +667,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
std::string_view compression_str; std::string_view compression_str;
if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed) if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed)
{ {
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, VMManager::GetGameSerial(), m_crc, m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, VMManager::GetDiscSerial(), m_crc,
screenshot_width, screenshot_height, screenshot_width, screenshot_height,
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
fd, m_regs)); fd, m_regs));
@ -675,7 +675,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
} }
else if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::LZMA) else if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::LZMA)
{ {
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, VMManager::GetGameSerial(), m_crc, m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, VMManager::GetDiscSerial(), m_crc,
screenshot_width, screenshot_height, screenshot_width, screenshot_height,
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
fd, m_regs)); fd, m_regs));
@ -683,7 +683,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
} }
else else
{ {
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpZst(m_snapshot, VMManager::GetGameSerial(), m_crc, m_dump = std::unique_ptr<GSDumpBase>(new GSDumpZst(m_snapshot, VMManager::GetDiscSerial(), m_crc,
screenshot_width, screenshot_height, screenshot_width, screenshot_height,
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
fd, m_regs)); fd, m_regs));
@ -780,14 +780,14 @@ static std::string GSGetBaseFilename()
std::string filename; std::string filename;
// append the game serial and title // 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); Path::SanitizeFileName(&name);
if (name.length() > 219) if (name.length() > 219)
name.resize(219); name.resize(219);
filename += name; filename += name;
} }
if (std::string serial(VMManager::GetGameSerial()); !serial.empty()) if (std::string serial = VMManager::GetDiscSerial(); !serial.empty())
{ {
Path::SanitizeFileName(&serial); Path::SanitizeFileName(&serial);
filename += '_'; filename += '_';

View File

@ -303,7 +303,7 @@ std::string GSTextureReplacements::GetDumpFilename(const TextureName& name, u32
void GSTextureReplacements::Initialize() void GSTextureReplacements::Initialize()
{ {
s_current_serial = VMManager::GetGameSerial(); s_current_serial = VMManager::GetDiscSerial();
if (GSConfig.DumpReplaceableTextures || GSConfig.LoadTextureReplacements) if (GSConfig.DumpReplaceableTextures || GSConfig.LoadTextureReplacements)
StartWorkerThread(); StartWorkerThread();
@ -313,7 +313,7 @@ void GSTextureReplacements::Initialize()
void GSTextureReplacements::GameChanged() void GSTextureReplacements::GameChanged()
{ {
std::string new_serial(VMManager::GetGameSerial()); std::string new_serial = VMManager::GetDiscSerial();
if (s_current_serial == new_serial) if (s_current_serial == new_serial)
return; return;

View File

@ -204,9 +204,6 @@ static void GSDumpReplayerLoadInitialState()
std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(), std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(),
std::min(Ps2MemSize::GSregs, static_cast<u32>(s_dump_file->GetRegsData().size()))); std::min(Ps2MemSize::GSregs, static_cast<u32>(s_dump_file->GetRegsData().size())));
// update serial to load hw fixes
VMManager::Internal::GameStartingOnCPUThread();
// load GS state // load GS state
freezeData fd = {static_cast<int>(s_dump_file->GetStateData().size()), freezeData fd = {static_cast<int>(s_dump_file->GetStateData().size()),
const_cast<u8*>(s_dump_file->GetStateData().data())}; const_cast<u8*>(s_dump_file->GetStateData().data())};

View File

@ -60,22 +60,17 @@ std::string GameDatabaseSchema::GameEntry::memcardFiltersAsString() const
const std::string* GameDatabaseSchema::GameEntry::findPatch(u32 crc) 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); auto it = patches.find(crc);
if (it != patches.end()) if (it != patches.end())
{
Console.WriteLn(fmt::format("[GameDB] Found patch with CRC '{:08X}'", crc));
return &it->second; return &it->second;
}
it = patches.find(0); it = patches.find(0);
if (it != patches.end()) if (it != patches.end())
{
Console.WriteLn("[GameDB] Found and falling back to default patch");
return &it->second; return &it->second;
}
Console.WriteLn("[GameDB] No CRC-specific patch or default patch found");
return nullptr; return nullptr;
} }

View File

@ -173,19 +173,10 @@ bool GameList::GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std::
if (CDVD->open(path.c_str()) != 0) if (CDVD->open(path.c_str()) != 0)
return false; return false;
// TODO: we could include the version in the game list?
*disc_type = DoCDVDdetectDiskType(); *disc_type = DoCDVDdetectDiskType();
cdvdReloadElfInfo(); cdvdGetDiscInfo(serial, nullptr, nullptr, crc, nullptr);
*serial = DiscSerial;
*crc = ElfCRC;
DoCDVDclose(); DoCDVDclose();
// TODO(Stenzek): These globals are **awful**. Clean it up.
DiscSerial.clear();
ElfCRC = 0;
ElfEntry = -1;
LastELF.clear();
return true; return true;
} }

View File

@ -79,9 +79,9 @@ static void HotkeyCycleSaveSlot(s32 delta)
else else
s_current_save_slot = (s_current_save_slot % CYCLE_SAVE_STATE_SLOTS) + 1; s_current_save_slot = (s_current_save_slot % CYCLE_SAVE_STATE_SLOTS) + 1;
const u32 crc = VMManager::GetGameCRC(); const u32 crc = VMManager::GetDiscCRC();
const std::string serial(VMManager::GetGameSerial()); const std::string serial = VMManager::GetDiscSerial();
const std::string filename(VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot)); const std::string filename = VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot);
FILESYSTEM_STAT_DATA sd; FILESYSTEM_STAT_DATA sd;
if (!filename.empty() && FileSystem::StatFile(filename.c_str(), &sd)) if (!filename.empty() && FileSystem::StatFile(filename.c_str(), &sd))
{ {
@ -109,34 +109,21 @@ static void HotkeyCycleSaveSlot(s32 delta)
static void HotkeyLoadStateSlot(s32 slot) static void HotkeyLoadStateSlot(s32 slot)
{ {
const u32 crc = VMManager::GetGameCRC(); // Can reapply settings and thus binds, therefore must be deferred.
if (crc == 0) Host::RunOnCPUThread([slot]() {
{ if (!VMManager::HasSaveStateInSlot(VMManager::GetDiscSerial().c_str(), VMManager::GetDiscCRC(), slot))
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot load state from a slot without a game running.", {
Host::OSD_INFO_DURATION); Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot),
return; Host::OSD_INFO_DURATION);
} return;
}
const std::string serial(VMManager::GetGameSerial()); VMManager::LoadStateFromSlot(slot);
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);
} }
static void HotkeySaveStateSlot(s32 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); VMManager::SaveStateToSlot(slot);
} }

View File

@ -200,7 +200,7 @@ namespace FullscreenUI
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Main // 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 ToggleTheme();
static void PauseForMenuOpen(); static void PauseForMenuOpen();
static void ClosePauseMenu(); static void ClosePauseMenu();
@ -229,9 +229,9 @@ namespace FullscreenUI
// local copies of the currently-running game // local copies of the currently-running game
static std::string s_current_game_title; static std::string s_current_game_title;
static std::string s_current_game_subtitle; static std::string s_current_game_subtitle;
static std::string s_current_game_serial; static std::string s_current_disc_serial;
static std::string s_current_game_path; static std::string s_current_disc_path;
static u32 s_current_game_crc; static u32 s_current_disc_crc;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Resources // Resources
@ -578,7 +578,8 @@ bool FullscreenUI::Initialize()
if (VMManager::HasValidVM()) if (VMManager::HasValidVM())
{ {
UpdateGameDetails(VMManager::GetDiscPath(), VMManager::GetGameSerial(), VMManager::GetGameName(), VMManager::GetGameCRC()); UpdateGameDetails(VMManager::GetDiscPath(), VMManager::GetDiscSerial(), VMManager::GetTitle(),
VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
} }
else 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()) if (!IsInitialized())
return; return;
GetMTGS().RunOnGSThread([path = std::move(path), serial = std::move(serial), title = std::move(title), crc]() { GetMTGS().RunOnGSThread(
if (!IsInitialized()) [path = std::move(path), serial = std::move(serial), title = std::move(title), disc_crc, crc]() {
return; 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()) if (!serial.empty())
s_current_game_subtitle = fmt::format("{} / {:08X}", serial, crc); 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_subtitle = {};
s_current_game_title = std::move(title); s_current_game_title = std::move(title);
s_current_game_serial = std::move(serial); s_current_disc_serial = std::move(serial);
s_current_game_path = std::move(path); s_current_disc_path = std::move(path);
s_current_game_crc = crc; s_current_disc_crc = disc_crc;
} }
void FullscreenUI::ToggleTheme() void FullscreenUI::ToggleTheme()
@ -744,9 +746,9 @@ void FullscreenUI::Shutdown(bool clear_state)
s_graphics_adapter_list_cache = {}; s_graphics_adapter_list_cache = {};
s_current_game_title = {}; s_current_game_title = {};
s_current_game_subtitle = {}; s_current_game_subtitle = {};
s_current_game_serial = {}; s_current_disc_serial = {};
s_current_game_path = {}; s_current_disc_path = {};
s_current_game_crc = 0; s_current_disc_crc = 0;
s_current_main_window = MainWindowType::None; s_current_main_window = MainWindowType::None;
s_current_pause_submenu = PauseSubMenu::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(), 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() void FullscreenUI::DoChangeDisc()
@ -2333,13 +2335,13 @@ void FullscreenUI::SwitchToGameSettings(const std::string_view& serial, u32 crc)
void FullscreenUI::SwitchToGameSettings() 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; return;
auto lock = GameList::GetLock(); 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) 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) if (entry)
SwitchToGameSettings(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_width = has_rich_presence ? 60.0f : 50.0f;
const float image_height = has_rich_presence ? 90.0f : 75.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( const ImVec2 title_size(
g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits<float>::max(), -1.0f, s_current_game_title.c_str())); g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits<float>::max(), -1.0f, s_current_game_title.c_str()));
const ImVec2 path_size(path_string.empty() ? 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)); 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); 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<std::time_t>(VMManager::GetSessionPlayedTime()); const std::time_t session_time = static_cast<std::time_t>(VMManager::GetSessionPlayedTime());
const std::string played_time_str(GameList::FormatTimespan(cached_played_time + session_time, true)); 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)); const std::string session_time_str(GameList::FormatTimespan(session_time, true));
@ -4389,7 +4391,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
case PauseSubMenu::None: case PauseSubMenu::None:
{ {
// NOTE: Menu close must come first, because otherwise VM destruction options will race. // 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()) if (ActiveButton(ICON_FA_PLAY " Resume Game", false) || WantsToCloseMenu())
ClosePauseMenu(); ClosePauseMenu();
@ -4614,7 +4616,7 @@ bool FullscreenUI::OpenSaveStateSelector(bool is_loading)
s_save_state_selector_game_path = {}; s_save_state_selector_game_path = {};
s_save_state_selector_loading = is_loading; s_save_state_selector_loading = is_loading;
s_save_state_selector_resuming = false; 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; s_save_state_selector_open = true;
return true; return true;
@ -5815,7 +5817,7 @@ GSTexture* FullscreenUI::GetCoverForCurrentGame()
{ {
auto lock = GameList::GetLock(); 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) if (!entry)
return s_fallback_disc_texture.get(); return s_fallback_disc_texture.get();

View File

@ -29,7 +29,7 @@ namespace FullscreenUI
void CheckForConfigChanges(const Pcsx2Config& old_config); void CheckForConfigChanges(const Pcsx2Config& old_config);
void OnVMStarted(); void OnVMStarted();
void OnVMDestroyed(); 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 OpenPauseMenu();
void OpenAchievementsWindow(); void OpenAchievementsWindow();
void OpenLeaderboardsWindow(); void OpenLeaderboardsWindow();

View File

@ -513,30 +513,22 @@ static void intCancelInstruction()
static void intExecute() 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. // 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. // It will come back as nonzero when we exit execution.
if (fastjmp_set(&intJmpBuf) != 0) if (fastjmp_set(&intJmpBuf) != 0)
return; return;
// I hope this doesn't cause issues with the optimizer... infinite loop with a constant expression.
for (;;) for (;;)
{ {
// The execution was splited in three parts so it is easier to if (!VMManager::Internal::HasBootedELF())
// resume it after a cancelled instruction. {
switch (state) { // Avoid reloading every instruction.
case RESET: u32 elf_entry_point = VMManager::Internal::GetCurrentELFEntryPoint();
u32 eeload_main = g_eeloadMain;
while (true)
{ {
do execI();
{
execI();
} while (cpuRegs.pc != (g_eeloadMain ? g_eeloadMain : EELOAD_START));
if (cpuRegs.pc == EELOAD_START) if (cpuRegs.pc == EELOAD_START)
{ {
@ -544,11 +536,13 @@ static void intExecute()
u32 mainjump = memRead32(EELOAD_START + 0x9c); u32 mainjump = memRead32(EELOAD_START + 0x9c);
if (mainjump >> 26 == 3) // JAL if (mainjump >> 26 == 3) // JAL
g_eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU); g_eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU);
eeload_main = g_eeloadMain;
} }
else if (cpuRegs.pc == g_eeloadMain) else if (cpuRegs.pc == g_eeloadMain)
{ {
eeloadHook(); eeloadHook();
if (g_SkipBiosHack) if (VMManager::Internal::IsFastBootInProgress())
{ {
// See comments on this code in iR5900-32.cpp's recRecompile() // See comments on this code in iR5900-32.cpp's recRecompile()
u32 typeAexecjump = memRead32(EELOAD_START + 0x470); u32 typeAexecjump = memRead32(EELOAD_START + 0x470);
@ -562,39 +556,24 @@ static void intExecute()
else else
Console.WriteLn("intExecute: Could not enable launch arguments for fast boot mode; unidentified BIOS version! Please report this to the PCSX2 developers."); 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) else if (cpuRegs.pc == g_eeloadExec)
{ {
eeloadHook2(); eeloadHook2();
} }
else if (cpuRegs.pc == elf_entry_point)
if (!g_GameLoading)
break;
state = GAME_LOADING;
[[fallthrough]];
}
case GAME_LOADING:
{
if (ElfEntry != 0xFFFFFFFF)
{ {
do VMManager::Internal::EntryPointCompilingOnCPUThread();
{ break;
execI();
} while (cpuRegs.pc != ElfEntry);
eeGameStarting();
} }
state = GAME_RUNNING;
[[fallthrough]];
} }
}
case GAME_RUNNING: else
{ {
while (true) while (true)
execI(); execI();
}
break;
} }
} }
} }

View File

@ -23,6 +23,7 @@
#include "R5900.h" #include "R5900.h"
#include "ps2/BiosTools.h" #include "ps2/BiosTools.h"
#include "x86/iR3000A.h" #include "x86/iR3000A.h"
#include "VMManager.h"
#include "common/FileSystem.h" #include "common/FileSystem.h"
#include "common/Path.h" #include "common/Path.h"
@ -170,6 +171,17 @@ namespace R3000A
else if (!hostRoot.empty()) // relative paths else if (!hostRoot.empty()) // relative paths
new_path = Path::Combine(hostRoot, native_path); 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. // 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! // 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)); std::string canonicalized_path(Path::Canonicalize(new_path));
@ -562,7 +574,7 @@ namespace R3000A
if (not_number_pos == std::string::npos) if (not_number_pos == std::string::npos)
return false; 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() int open_HLE()

View File

@ -268,7 +268,7 @@ bool SysMtgsThread::TryOpenGS()
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs)) if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
return false; return false;
GSSetGameCRC(ElfCRC); GSSetGameCRC(VMManager::GetDiscCRC());
return true; return true;
} }

View File

@ -48,8 +48,6 @@ BIOS
#include "common/AlignedMalloc.h" #include "common/AlignedMalloc.h"
#include "GSDumpReplayer.h"
#ifdef ENABLECACHE #ifdef ENABLECACHE
#include "Cache.h" #include "Cache.h"
#endif #endif
@ -841,11 +839,7 @@ void eeMemoryReserve::Reset()
vtlb_VMap(0x00000000,0x00000000,0x20000000); vtlb_VMap(0x00000000,0x00000000,0x20000000);
vtlb_VMapUnmap(0x20000000,0x60000000); vtlb_VMapUnmap(0x20000000,0x60000000);
const bool needs_bios = !GSDumpReplayer::IsReplayingDump(); CopyBIOSToMemory();
// TODO(Stenzek): Move BIOS loading out and far away...
if (needs_bios && !LoadBIOS())
pxFailRel("Failed to load BIOS");
} }
void eeMemoryReserve::Release() void eeMemoryReserve::Release()

View File

@ -437,7 +437,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
{ {
if (!VMManager::HasValidVM()) if (!VMManager::HasValidVM())
goto error; goto error;
const std::string gameName = VMManager::GetGameName(); const std::string gameName = VMManager::GetTitle();
const u32 size = gameName.size() + 1; const u32 size = gameName.size() + 1;
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size)) if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error; goto error;
@ -451,7 +451,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
{ {
if (!VMManager::HasValidVM()) if (!VMManager::HasValidVM())
goto error; goto error;
const std::string gameSerial = VMManager::GetGameSerial(); const std::string gameSerial = VMManager::GetDiscSerial();
const u32 size = gameSerial.size() + 1; const u32 size = gameSerial.size() + 1;
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size)) if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error; goto error;
@ -465,7 +465,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
{ {
if (!VMManager::HasValidVM()) if (!VMManager::HasValidVM())
goto error; goto error;
const std::string crc(fmt::format("{:08x}", VMManager::GetGameCRC())); const std::string crc = fmt::format("{:08x}", VMManager::GetDiscCRC());
const u32 size = crc.size() + 1; const u32 size = crc.size() + 1;
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size)) if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error; goto error;
@ -479,6 +479,8 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
{ {
if (!VMManager::HasValidVM()) if (!VMManager::HasValidVM())
goto error; goto error;
const std::string ElfVersion = VMManager::GetDiscVersion();
const u32 size = ElfVersion.size() + 1; const u32 size = ElfVersion.size() + 1;
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size)) if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error; goto error;

View File

@ -187,7 +187,6 @@ namespace Patch
static EnablePatchList s_enabled_cheats; static EnablePatchList s_enabled_cheats;
static EnablePatchList s_enabled_patches; static EnablePatchList s_enabled_patches;
static u32 s_patches_crc; static u32 s_patches_crc;
static std::string s_patches_serial;
static std::optional<AspectRatioType> s_override_aspect_ratio; static std::optional<AspectRatioType> s_override_aspect_ratio;
static std::optional<GSInterlaceMode> s_override_interlace_mode; static std::optional<GSInterlaceMode> s_override_interlace_mode;
@ -520,18 +519,16 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
return count; 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_crc = crc;
s_patches_serial = std::move(serial);
// Skip reloading gamedb patches if the serial hasn't changed. if (reload_files)
if (serial_changed)
{ {
s_gamedb_patches.clear(); s_gamedb_patches.clear();
const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_patches_serial); const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(serial);
if (game) if (game)
{ {
const std::string* patches = game->findPatch(crc); 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); 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(); s_game_patches.clear();
EnumeratePnachFiles( 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); const u32 patch_count = LoadPatchesFromString(&s_game_patches, pnach_data);
if (patch_count > 0) if (patch_count > 0)
Console.WriteLn(Color_Green, fmt::format("Found {} game patches in {}.", patch_count, filename)); 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(); s_cheat_patches.clear();
EnumeratePnachFiles( 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); const u32 patch_count = LoadPatchesFromString(&s_cheat_patches, pnach_data);
if (patch_count > 0) if (patch_count > 0)
Console.WriteLn(Color_Green, fmt::format("Found {} cheats in {}.", patch_count, filename)); 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(); s_override_interlace_mode.reset();
std::string message; std::string message;
u32 gp_count = 0;
if (EmuConfig.EnablePatches) if (EmuConfig.EnablePatches)
{ {
const u32 gp_count = EnablePatches(s_gamedb_patches, EnablePatchList()); gp_count = EnablePatches(s_gamedb_patches, EnablePatchList());
if (gp_count > 0) if (gp_count > 0)
fmt::format_to(std::back_inserter(message), "{} GameDB patches", gp_count); 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); fmt::format_to(std::back_inserter(message), "{}{} cheat patches", message.empty() ? "" : ", ", c_count);
// Display message on first boot when we load patches. // 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()) if (!message.empty())
{ {
@ -673,7 +665,6 @@ void Patch::UnloadPatches()
s_override_interlace_mode = {}; s_override_interlace_mode = {};
s_override_aspect_ratio = {}; s_override_aspect_ratio = {};
s_patches_crc = 0; s_patches_crc = 0;
s_patches_serial = {};
s_active_patches = {}; s_active_patches = {};
s_active_dynamic_patches = {}; s_active_dynamic_patches = {};
s_enabled_patches = {}; s_enabled_patches = {};

View File

@ -96,8 +96,7 @@ namespace Patch
extern PatchInfoList GetPatchInfo(const std::string& serial, u32 crc, bool cheats, u32* num_unlabelled_patches); 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. /// 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(const std::string& serial, u32 crc, bool 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 UpdateActivePatches(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(); extern void ApplyPatchSettingOverrides();

View File

@ -1305,6 +1305,8 @@ Pcsx2Config::Pcsx2Config()
McdEnableEjection = true; McdEnableEjection = true;
McdFolderAutoManage = true; McdFolderAutoManage = true;
EnablePatches = true; EnablePatches = true;
EnableFastBoot = true;
EnablePerGameSettings = true;
EnableRecordingTools = true; EnableRecordingTools = true;
EnableGameFixes = true; EnableGameFixes = true;
InhibitScreensaver = true; InhibitScreensaver = true;
@ -1348,6 +1350,9 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBool(EnablePINE); SettingsWrapBitBool(EnablePINE);
SettingsWrapBitBool(EnableWideScreenPatches); SettingsWrapBitBool(EnableWideScreenPatches);
SettingsWrapBitBool(EnableNoInterlacingPatches); SettingsWrapBitBool(EnableNoInterlacingPatches);
SettingsWrapBitBool(EnableFastBoot);
SettingsWrapBitBool(EnableFastBootFastForward);
SettingsWrapBitBool(EnablePerGameSettings);
SettingsWrapBitBool(EnableRecordingTools); SettingsWrapBitBool(EnableRecordingTools);
SettingsWrapBitBool(EnableGameFixes); SettingsWrapBitBool(EnableGameFixes);
SettingsWrapBitBool(SaveStateOnShutdown); SettingsWrapBitBool(SaveStateOnShutdown);
@ -1472,7 +1477,6 @@ bool Pcsx2Config::operator==(const Pcsx2Config& right) const
void Pcsx2Config::CopyRuntimeConfig(Pcsx2Config& cfg) void Pcsx2Config::CopyRuntimeConfig(Pcsx2Config& cfg)
{ {
GS.LimitScalar = cfg.GS.LimitScalar; GS.LimitScalar = cfg.GS.LimitScalar;
UseBOOT2Injection = cfg.UseBOOT2Injection;
CurrentBlockdump = std::move(cfg.CurrentBlockdump); CurrentBlockdump = std::move(cfg.CurrentBlockdump);
CurrentIRX = std::move(cfg.CurrentIRX); CurrentIRX = std::move(cfg.CurrentIRX);
CurrentGameArgs = std::move(cfg.CurrentGameArgs); CurrentGameArgs = std::move(cfg.CurrentGameArgs);

View File

@ -51,10 +51,6 @@ alignas(16) fpuRegisters fpuRegs;
alignas(16) tlbs tlb[48]; alignas(16) tlbs tlb[48];
R5900cpu *Cpu = NULL; 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; static const uint eeWaitCycles = 3072;
bool eeEventTestIsActive = false; bool eeEventTestIsActive = false;
@ -104,24 +100,12 @@ void cpuReset()
extern void Deci2Reset(); // lazy, no good header for it yet. extern void Deci2Reset(); // lazy, no good header for it yet.
Deci2Reset(); Deci2Reset();
g_SkipBiosHack = EmuConfig.UseBOOT2Injection; AllowParams1 = !VMManager::Internal::IsFastBootInProgress();
AllowParams1 = !g_SkipBiosHack; AllowParams2 = !VMManager::Internal::IsFastBootInProgress();
AllowParams2 = !g_SkipBiosHack;
ElfCRC = 0; g_eeloadMain = 0;
DiscSerial.clear(); g_eeloadExec = 0;
ElfEntry = -1; g_osdsys_str = 0;
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;
} }
__ri void cpuException(u32 code, u32 bd) __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( !(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 ); cpuClearInt( n );
callback(); 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 // 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. // 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 // 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()) while ((cpuRegs.interrupt & 0x1FFFF) && _cpuTestInterrupts())
; ;
@ -568,27 +552,6 @@ __fi void CPU_INT( EE_EventType n, s32 ecycle)
cpuSetNextEventDelta(cpuRegs.eCycle[n]); 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 // Count arguments, save their starting locations, and replace the space separators with null terminators so they're separate strings
int ParseArgumentString(u32 arg_block) int ParseArgumentString(u32 arg_block)
{ {
@ -635,16 +598,6 @@ int ParseArgumentString(u32 arg_block)
// Called from recompilers; define is mandatory. // Called from recompilers; define is mandatory.
void eeloadHook() 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; std::string elfname;
int argc = cpuRegs.GPR.n.a0.SD[0]; int argc = cpuRegs.GPR.n.a0.SD[0];
if (argc) // calls to EELOAD *after* the first one during the startup process will come here 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 // 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, // 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. // 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(); const char *argString = EmuConfig.CurrentGameArgs.c_str();
Console.WriteLn("eeloadHook: Supplying launch argument(s) '%s' to module '%s'...", argString, elfname.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 // 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. // 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()) if (!elf_override.empty())
{ {
elftoload = StringUtil::StdStringFromFormat("host:%s", elf_override.c_str()); elfname = fmt::format("host:{}", elf_override);
} }
else else
{ {
if (disctype == 2) CDVDDiscType disc_type;
elftoload = discelf; 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 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 // 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() // "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() // 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 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")) if (!strcmp((char*)PSM(g_osdsys_str), "rom0:OSDSYS"))
{ {
// Overwrite OSDSYS with game's ELF name // Overwrite OSDSYS with game's ELF name
strcpy((char*)PSM(g_osdsys_str), elftoload.c_str()); strcpy((char*)PSM(g_osdsys_str), elfname.c_str());
g_GameLoading = true;
return;
} }
} }
} }
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)) VMManager::Internal::ELFLoadingOnCPUThread(std::move(elfname));
g_GameLoading = true;
} }
// Called from recompilers; define is mandatory. // Called from recompilers; define is mandatory.

View File

@ -26,10 +26,6 @@ class BaseR5900Exception;
// them in iR5900.h would mean having to include that into more files than I care to // 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. // 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 namespace Exception
{ {
// Implementation Note: this exception has no meaningful type information and we don't // 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 const u32 EELOAD_SIZE = 0x20000; // overestimate for searching
extern u32 g_eeloadMain, g_eeloadExec; extern u32 g_eeloadMain, g_eeloadExec;
extern void eeGameStarting();
extern void eeloadHook(); extern void eeloadHook();
extern void eeloadHook2(); extern void eeloadHook2();

View File

@ -969,7 +969,7 @@ void SYSCALL()
AllowParams1 = true; AllowParams1 = true;
break; break;
case Syscall::GetOsdConfigParam: case Syscall::GetOsdConfigParam:
if(!NoOSD && g_SkipBiosHack && !AllowParams1) if(!NoOSD && !AllowParams1)
{ {
u32 memaddr = cpuRegs.GPR.n.a0.UL[0]; u32 memaddr = cpuRegs.GPR.n.a0.UL[0];
u8 params[16]; u8 params[16];
@ -993,7 +993,7 @@ void SYSCALL()
AllowParams2 = true; AllowParams2 = true;
break; break;
case Syscall::GetOsdConfigParam2: case Syscall::GetOsdConfigParam2:
if (!NoOSD && g_SkipBiosHack && !AllowParams2) if (!NoOSD && !AllowParams2)
{ {
u32 memaddr = cpuRegs.GPR.n.a0.UL[0]; u32 memaddr = cpuRegs.GPR.n.a0.UL[0];
u8 params[16]; u8 params[16];

View File

@ -78,7 +78,7 @@ bool InputRecording::create(const std::string& fileName, const bool fromSaveStat
m_file.setEmulatorVersion(); m_file.setEmulatorVersion();
m_file.setAuthor(authorName); m_file.setAuthor(authorName);
m_file.setGameName(resolveGameName()); m_file.setGameName(VMManager::GetTitle());
m_file.writeHeader(); m_file.writeHeader();
initializeState(); initializeState();
InputRec::log("Started new input recording"); InputRec::log("Started new input recording");
@ -129,9 +129,9 @@ bool InputRecording::play(const std::string& filename)
initializeState(); initializeState();
InputRec::log("Replaying input recording"); InputRec::log("Replaying input recording");
m_file.logRecordingMetadata(); 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; 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() void InputRecording::incFrameCounter()
{ {
if (!m_is_active) if (!m_is_active)

View File

@ -70,11 +70,6 @@ private:
void initializeState(); void initializeState();
void setStartingFrame(u32 startingFrame); void setStartingFrame(u32 startingFrame);
void closeActiveFile(); 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; extern InputRecording g_InputRecording;

View File

@ -174,11 +174,11 @@ SaveStateBase& SaveStateBase::FreezeBios()
SaveStateBase& SaveStateBase::FreezeInternals() SaveStateBase& SaveStateBase::FreezeInternals()
{ {
const u32 previousCRC = ElfCRC;
// Print this until the MTVU problem in gifPathFreeze is taken care of (rama) // 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"); if (THREAD_VU1) Console.Warning("MTVU speedhack is enabled, saved states may not be stable");
vmFreeze();
// Second Block - Various CPU Registers and States // Second Block - Various CPU Registers and States
// ----------------------------------------------- // -----------------------------------------------
FreezeTag( "cpuRegs" ); FreezeTag( "cpuRegs" );
@ -188,30 +188,7 @@ SaveStateBase& SaveStateBase::FreezeInternals()
Freeze(tlb); // tlbs Freeze(tlb); // tlbs
Freeze(AllowParams1); //OSDConfig written (Fast Boot) Freeze(AllowParams1); //OSDConfig written (Fast Boot)
Freeze(AllowParams2); 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 // Third Block - Cycle Timers and Events
// ------------------------------------- // -------------------------------------
FreezeTag( "Cycles" ); FreezeTag( "Cycles" );

View File

@ -36,7 +36,7 @@ enum class FreezeAction
// [SAVEVERSION+] // [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated. // 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 // the freezing data between submodules and core
@ -147,6 +147,18 @@ public:
} }
} }
void FreezeString(std::string& s)
{
// overwritten when loading
u32 length = static_cast<u32>(s.length());
Freeze(length);
if (IsLoading())
s.resize(length);
FreezeMem(s.data(), length);
}
uint GetCurrentPos() const uint GetCurrentPos() const
{ {
return m_idx; return m_idx;
@ -189,8 +201,7 @@ public:
protected: protected:
void Init( VmStateBuffer* memblock ); void Init( VmStateBuffer* memblock );
// Load/Save functions for the various components of our glorious emulator! void vmFreeze();
void mtvuFreeze(); void mtvuFreeze();
void rcntFreeze(); void rcntFreeze();
void vuMicroFreeze(); void vuMicroFreeze();

View File

@ -346,34 +346,3 @@ void SysClearExecutionCache()
dVifReset(1); 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);
}

View File

@ -144,9 +144,6 @@ extern SysCpuProviderPack& GetCpuProviders();
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs. extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
extern void SysClearExecutionCache(); // clears recompiled execution caches! extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern std::string SysGetBiosDiscID();
extern std::string SysGetDiscID();
extern SysMainMemory& GetVmMemory(); extern SysMainMemory& GetVmMemory();
extern void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR); extern void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR);

View File

@ -332,7 +332,7 @@ namespace usb_lightgun
void GunCon2State::AutoConfigure() void GunCon2State::AutoConfigure()
{ {
const std::string serial(VMManager::GetGameSerial()); const std::string serial = VMManager::GetDiscSerial();
for (const GameConfig& gc : s_game_config) for (const GameConfig& gc : s_game_config)
{ {
if (serial != gc.serial) if (serial != gc.serial)

File diff suppressed because it is too large Load Diff

View File

@ -74,14 +74,23 @@ namespace VMManager
/// Returns the path of the disc currently running. /// Returns the path of the disc currently running.
std::string GetDiscPath(); std::string GetDiscPath();
/// Returns the crc of the executable currently running. /// Returns the serial of the disc currently running.
u32 GetGameCRC(); std::string GetDiscSerial();
/// Returns the serial of the disc/executable currently running. /// Returns the path of the main ELF of the disc currently running.
std::string GetGameSerial(); std::string GetDiscELF();
/// Returns the name of the disc/executable currently running. /// 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). /// Loads global settings (i.e. EmuConfig).
void LoadSettings(); void LoadSettings();
@ -107,6 +116,9 @@ namespace VMManager
/// Reloads game specific settings, and applys any changes present. /// Reloads game specific settings, and applys any changes present.
bool ReloadGameSettings(); 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. /// Returns the save state filename for the given game serial/crc.
std::string GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot); 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. /// Updates the variables in the EmuFolders namespace, reloading subsystems if needed.
void UpdateEmuFolders(); 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(); bool IsExecutionInterrupted();
void ELFLoadingOnCPUThread(std::string elf_path);
void EntryPointCompilingOnCPUThread(); void EntryPointCompilingOnCPUThread();
void GameStartingOnCPUThread();
void SwappingGameOnCPUThread();
void VSyncOnCPUThread(); void VSyncOnCPUThread();
} // namespace Internal } // namespace Internal
} // namespace VMManager } // namespace VMManager
@ -262,8 +285,8 @@ namespace Host
void OnSaveStateSaved(const std::string_view& filename); void OnSaveStateSaved(const std::string_view& filename);
/// Provided by the host; called when the running executable changes. /// 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, void OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
const std::string& game_name, u32 game_crc); const std::string& disc_serial, u32 disc_crc, u32 current_crc);
/// Provided by the host; called once per frame at guest vsync. /// Provided by the host; called once per frame at guest vsync.
void VSyncOnCPUThread(); void VSyncOnCPUThread();

View File

@ -54,9 +54,11 @@ bool NoOSD;
bool AllowParams1; bool AllowParams1;
bool AllowParams2; bool AllowParams2;
std::string BiosDescription; std::string BiosDescription;
std::string BiosZone;
std::string BiosSerial; std::string BiosSerial;
std::string BiosPath; std::string BiosPath;
BiosDebugInformation CurrentBiosInformation; BiosDebugInformation CurrentBiosInformation;
std::vector<u8> BiosRom;
static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& description, u32& region, std::string& zone, std::string& serial) 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; return true;
} }
template <size_t _size> static void ChecksumIt(u32& result, u32 offset, u32 size)
void ChecksumIt(u32& result, const u8 (&srcdata)[_size])
{ {
pxAssume((_size & 3) == 0); const u8* srcdata = &BiosRom[offset];
for (size_t i = 0; i < _size / 4; ++i) pxAssume((size & 3) == 0);
result ^= ((u32*)srcdata)[i]; for (size_t i = 0; i < size / 4; ++i)
result ^= reinterpret_cast<const u32*>(srcdata)[i];
} }
// Attempts to load a BIOS rom sub-component, by trying multiple combinations of base // 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: // Parameters:
// ext - extension of the sub-component to load. Valid options are rom1 and rom2. // ext - extension of the sub-component to load. Valid options are rom1 and rom2.
// //
template <size_t _size> static void LoadExtraRom(const char* ext, u32 offset, u32 size)
static void LoadExtraRom(const char* ext, u8 (&dest)[_size])
{ {
// Try first a basic extension concatenation (normally results in something like name.bin.rom1) // 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)); 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"); auto fp = FileSystem::OpenManagedCFile(Bios1.c_str(), "rb");
if (!fp || std::fread(dest, static_cast<size_t>(std::min<s64>(_size, filesize)), 1, fp.get()) != 1) if (!fp || std::fread(&BiosRom[offset], static_cast<size_t>(std::min<s64>(size, filesize)), 1, fp.get()) != 1)
{ {
Console.Warning("BIOS Warning: %s could not be read (permission denied?)", ext); Console.Warning("BIOS Warning: %s could not be read (permission denied?)", ext);
return; return;
@ -261,6 +264,29 @@ static std::string FindBiosImage()
return std::string(); 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 // Loads the configured bios rom file into PS2 memory. PS2 memory must be allocated prior to
// this method being called. // this method being called.
// //
@ -297,11 +323,12 @@ bool LoadBIOS()
if (filesize <= 0) if (filesize <= 0)
return false; return false;
std::string zone; LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, BiosZone, BiosSerial);
LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, zone, BiosSerial);
BiosRom.resize(Ps2MemSize::Rom);
if (FileSystem::FSeek64(fp.get(), 0, SEEK_SET) || if (FileSystem::FSeek64(fp.get(), 0, SEEK_SET) ||
std::fread(eeMem->ROM, static_cast<size_t>(std::min<s64>(Ps2MemSize::Rom, filesize)), 1, fp.get()) != 1) std::fread(BiosRom.data(), static_cast<size_t>(std::min<s64>(Ps2MemSize::Rom, filesize)), 1, fp.get()) != 1)
{ {
return false; return false;
} }
@ -314,40 +341,31 @@ bool LoadBIOS()
NoOSD = false; NoOSD = false;
BiosChecksum = 0; BiosChecksum = 0;
ChecksumIt(BiosChecksum, eeMem->ROM); ChecksumIt(BiosChecksum, 0, Ps2MemSize::Rom);
BiosPath = std::move(path); BiosPath = std::move(path);
//injectIRX("host.irx"); //not fully tested; still buggy //injectIRX("host.irx"); //not fully tested; still buggy
LoadExtraRom("rom1", eeMem->ROM1); LoadExtraRom("rom1", Ps2MemSize::Rom, Ps2MemSize::Rom1);
LoadExtraRom("rom2", eeMem->ROM2); 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) if (EmuConfig.CurrentIRX.length() > 3)
LoadIrx(EmuConfig.CurrentIRX, &eeMem->ROM[0x3C0000], sizeof(eeMem->ROM) - 0x3C0000); LoadIrx(EmuConfig.CurrentIRX, &eeMem->ROM[0x3C0000], sizeof(eeMem->ROM) - 0x3C0000);
CurrentBiosInformation.eeThreadListAddr = 0; 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());
} }

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs /* 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 * 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- * of the GNU Lesser General Public License as published by the Free Software Found-
@ -14,7 +14,9 @@
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
const u32 ThreadListInstructions[3] = const u32 ThreadListInstructions[3] =
{ {
@ -30,6 +32,7 @@ struct BiosDebugInformation
u32 iopModListAddr; u32 iopModListAddr;
}; };
// TODO: namespace this
extern BiosDebugInformation CurrentBiosInformation; extern BiosDebugInformation CurrentBiosInformation;
extern u32 BiosVersion; // Used by CDVD extern u32 BiosVersion; // Used by CDVD
extern u32 BiosRegion; // Used by CDVD extern u32 BiosRegion; // Used by CDVD
@ -38,9 +41,23 @@ extern bool AllowParams1;
extern bool AllowParams2; extern bool AllowParams2;
extern u32 BiosChecksum; extern u32 BiosChecksum;
extern std::string BiosDescription; 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 BiosSerial;
extern std::string BiosPath; 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<u8> BiosRom;
extern bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone); 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 IsBIOSAvailable(const std::string& full_path);
extern bool LoadBIOS();
extern void CopyBIOSToMemory();

View File

@ -2220,6 +2220,9 @@ static void recRecompile(const u32 startpc)
if (recPtr >= (recMem->GetPtrEnd() - _64kb)) if (recPtr >= (recMem->GetPtrEnd() - _64kb))
eeRecNeedsReset = true; eeRecNeedsReset = true;
if (HWADDR(startpc) == VMManager::Internal::GetCurrentELFEntryPoint())
VMManager::Internal::EntryPointCompilingOnCPUThread();
if (eeRecNeedsReset) if (eeRecNeedsReset)
{ {
eeRecNeedsReset = false; eeRecNeedsReset = false;
@ -2229,9 +2232,6 @@ static void recRecompile(const u32 startpc)
xSetPtr(recPtr); xSetPtr(recPtr);
recPtr = xGetAlignedCallTarget(); recPtr = xGetAlignedCallTarget();
if (0x8000d618 == startpc)
DbgCon.WriteLn("Compiling block @ 0x%08x", startpc);
s_pCurBlock = PC_GETBLOCK(startpc); s_pCurBlock = PC_GETBLOCK(startpc);
pxAssert(s_pCurBlock->GetFnptr() == (uptr)JITCompile || s_pCurBlock->GetFnptr() == (uptr)JITCompileInBlock); 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)) if (g_eeloadMain && HWADDR(startpc) == HWADDR(g_eeloadMain))
{ {
xFastCall((void*)eeloadHook); 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 // 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. // 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)) if (g_eeloadExec && HWADDR(startpc) == HWADDR(g_eeloadExec))
xFastCall((void*)eeloadHook2); 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; g_branch = 0;
// reset recomp state variables // reset recomp state variables

View File

@ -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, void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
const std::string& game_name, u32 game_crc) const std::string& disc_serial, u32 disc_crc, u32 current_crc)
{ {
} }