mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
0cf4cb6e4f
commit
36c27188a4
|
@ -293,8 +293,8 @@ void Host::OnVMResumed()
|
|||
{
|
||||
}
|
||||
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
|
||||
const std::string& disc_serial, u32 disc_crc, u32 current_crc)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -746,9 +746,9 @@ void MainWindow::updateWindowTitle()
|
|||
{
|
||||
QString suffix(QtHost::GetAppConfigSuffix());
|
||||
QString main_title(QtHost::GetAppNameAndVersion() + suffix);
|
||||
QString display_title(m_current_game_name + suffix);
|
||||
QString display_title(m_current_title + suffix);
|
||||
|
||||
if (!s_vm_valid || m_current_game_name.isEmpty())
|
||||
if (!s_vm_valid || m_current_title.isEmpty())
|
||||
display_title = main_title;
|
||||
else if (isRenderingToMain())
|
||||
main_title = display_title;
|
||||
|
@ -920,7 +920,7 @@ bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b
|
|||
return true;
|
||||
|
||||
// If we don't have a crc, we can't save state.
|
||||
allow_save_to_state &= (m_current_game_crc != 0);
|
||||
allow_save_to_state &= (m_current_disc_crc != 0);
|
||||
bool save_state = allow_save_to_state && default_save_to_state;
|
||||
|
||||
// Only confirm on UI thread because we need to display a msgbox.
|
||||
|
@ -1213,15 +1213,15 @@ void MainWindow::onChangeDiscMenuAboutToHide()
|
|||
void MainWindow::onLoadStateMenuAboutToShow()
|
||||
{
|
||||
m_ui.menuLoadState->clear();
|
||||
updateSaveStateMenusEnableState(!m_current_game_serial.isEmpty());
|
||||
populateLoadStateMenu(m_ui.menuLoadState, m_current_disc_path, m_current_game_serial, m_current_game_crc);
|
||||
updateSaveStateMenusEnableState(!m_current_disc_serial.isEmpty());
|
||||
populateLoadStateMenu(m_ui.menuLoadState, m_current_disc_path, m_current_disc_serial, m_current_disc_crc);
|
||||
}
|
||||
|
||||
void MainWindow::onSaveStateMenuAboutToShow()
|
||||
{
|
||||
m_ui.menuSaveState->clear();
|
||||
updateSaveStateMenusEnableState(!m_current_game_serial.isEmpty());
|
||||
populateSaveStateMenu(m_ui.menuSaveState, m_current_game_serial, m_current_game_crc);
|
||||
updateSaveStateMenusEnableState(!m_current_disc_serial.isEmpty());
|
||||
populateSaveStateMenu(m_ui.menuSaveState, m_current_disc_serial, m_current_disc_crc);
|
||||
}
|
||||
|
||||
void MainWindow::onViewToolbarActionToggled(bool checked)
|
||||
|
@ -1269,23 +1269,29 @@ void MainWindow::onViewGamePropertiesActionTriggered()
|
|||
return;
|
||||
|
||||
// prefer to use a game list entry, if we have one, that way the summary is populated
|
||||
if (!m_current_disc_path.isEmpty() || !m_current_elf_override.isEmpty())
|
||||
if (!m_current_disc_path.isEmpty() && m_current_elf_override.isEmpty())
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = m_current_elf_override.isEmpty() ?
|
||||
GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData()) :
|
||||
GameList::GetEntryForPath(m_current_elf_override.toUtf8().constData());
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData());
|
||||
if (entry)
|
||||
{
|
||||
SettingsDialog::openGamePropertiesDialog(
|
||||
entry, m_current_elf_override.isEmpty() ? std::string_view(entry->serial) : std::string_view(), entry->crc);
|
||||
SettingsDialog::openGamePropertiesDialog(entry, entry->serial, entry->crc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// open properties for the current running file (isn't in the game list)
|
||||
if (m_current_game_crc != 0)
|
||||
SettingsDialog::openGamePropertiesDialog(nullptr, m_current_game_serial.toStdString(), m_current_game_crc);
|
||||
if (m_current_disc_crc == 0)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Game Properties"), tr("Game properties is unavailable for the current game."));
|
||||
return;
|
||||
}
|
||||
|
||||
// can't use serial for ELFs, because they might have a disc set
|
||||
if (m_current_elf_override.isEmpty())
|
||||
SettingsDialog::openGamePropertiesDialog(nullptr, m_current_disc_serial.toStdString(), m_current_disc_crc);
|
||||
else
|
||||
SettingsDialog::openGamePropertiesDialog(nullptr, std::string_view(), m_current_disc_crc);
|
||||
}
|
||||
|
||||
void MainWindow::onGitHubRepositoryActionTriggered()
|
||||
|
@ -1602,13 +1608,15 @@ void MainWindow::onVMStopped()
|
|||
m_game_list_widget->refresh(false);
|
||||
}
|
||||
|
||||
void MainWindow::onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc)
|
||||
void MainWindow::onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path,
|
||||
const QString& serial, quint32 disc_crc, quint32 crc)
|
||||
{
|
||||
m_current_disc_path = path;
|
||||
m_current_title = title;
|
||||
m_current_elf_override = elf_override;
|
||||
m_current_game_serial = serial;
|
||||
m_current_game_name = name;
|
||||
m_current_game_crc = crc;
|
||||
m_current_disc_path = disc_path;
|
||||
m_current_disc_serial = serial;
|
||||
m_current_disc_crc = disc_crc;
|
||||
m_current_running_crc = crc;
|
||||
updateWindowTitle();
|
||||
updateSaveStateMenusEnableState(!serial.isEmpty());
|
||||
}
|
||||
|
|
|
@ -179,7 +179,8 @@ private Q_SLOTS:
|
|||
void onVMResumed();
|
||||
void onVMStopped();
|
||||
|
||||
void onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc);
|
||||
void onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path,
|
||||
const QString& serial, quint32 disc_crc, quint32 crc);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
@ -279,11 +280,12 @@ private:
|
|||
|
||||
QMenu* m_settings_toolbar_menu = nullptr;
|
||||
|
||||
QString m_current_disc_path;
|
||||
QString m_current_title;
|
||||
QString m_current_elf_override;
|
||||
QString m_current_game_serial;
|
||||
QString m_current_game_name;
|
||||
quint32 m_current_game_crc;
|
||||
QString m_current_disc_path;
|
||||
QString m_current_disc_serial;
|
||||
quint32 m_current_disc_crc;
|
||||
quint32 m_current_running_crc;
|
||||
|
||||
bool m_display_created = false;
|
||||
bool m_relative_mouse_mode = false;
|
||||
|
|
|
@ -667,14 +667,7 @@ void EmuThread::reloadPatches()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!VMManager::HasValidVM())
|
||||
return;
|
||||
|
||||
Patch::ReloadPatches(true, false, true, true);
|
||||
|
||||
// Might change widescreen mode.
|
||||
if (Patch::ReloadPatchAffectingOptions())
|
||||
applySettings();
|
||||
VMManager::ReloadPatches(true, false, true, true);
|
||||
}
|
||||
|
||||
void EmuThread::reloadInputSources()
|
||||
|
@ -968,11 +961,11 @@ void Host::OnVMResumed()
|
|||
emit g_emu_thread->onVMResumed();
|
||||
}
|
||||
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
|
||||
const std::string& disc_serial, u32 disc_crc, u32 current_crc)
|
||||
{
|
||||
emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(elf_override),
|
||||
QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc);
|
||||
emit g_emu_thread->onGameChanged(QString::fromStdString(title), QString::fromStdString(elf_override),
|
||||
QString::fromStdString(disc_path), QString::fromStdString(disc_serial), disc_crc, current_crc);
|
||||
}
|
||||
|
||||
void EmuThread::updatePerformanceMetrics(bool force)
|
||||
|
@ -1124,7 +1117,7 @@ void Host::VSyncOnCPUThread()
|
|||
|
||||
void Host::RunOnCPUThread(std::function<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..
|
||||
function();
|
||||
|
|
|
@ -137,7 +137,8 @@ Q_SIGNALS:
|
|||
void onVMStopped();
|
||||
|
||||
/// Provided by the host; called when the running executable changes.
|
||||
void onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc);
|
||||
void onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path,
|
||||
const QString& serial, quint32 disc_crc, quint32 crc);
|
||||
|
||||
void onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices);
|
||||
void onInputDeviceConnected(const QString& identifier, const QString& device_name);
|
||||
|
|
|
@ -30,23 +30,29 @@
|
|||
|
||||
BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_dialog(dialog)
|
||||
{
|
||||
SettingsInterface* sif = dialog->getSettingsInterface();
|
||||
|
||||
m_ui.setupUi(this);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBootFastForward, "EmuCore", "EnableFastBootFastForward", false);
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory,
|
||||
m_ui.resetSearchDirectory, "Folders", "Bios", Path::Combine(EmuFolders::DataRoot, "bios"));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Checked"),
|
||||
tr("Patches the BIOS to skip the console's boot animation."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Forward Boot"), tr("Unchecked"),
|
||||
tr("Removes emulation speed throttle until the game starts to reduce startup time."));
|
||||
|
||||
refreshList();
|
||||
|
||||
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
|
||||
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
|
||||
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
|
||||
connect(m_ui.fastBoot, &QCheckBox::stateChanged, this, &BIOSSettingsWidget::fastBootChanged);
|
||||
}
|
||||
|
||||
BIOSSettingsWidget::~BIOSSettingsWidget()
|
||||
|
@ -139,6 +145,12 @@ void BIOSSettingsWidget::listItemChanged(const QTreeWidgetItem* current, const Q
|
|||
g_emu_thread->applySettings();
|
||||
}
|
||||
|
||||
void BIOSSettingsWidget::fastBootChanged()
|
||||
{
|
||||
const bool enabled = m_dialog->getEffectiveBoolValue("EmuCore", "EnableFastBoot", true);
|
||||
m_ui.fastBootFastForward->setEnabled(enabled);
|
||||
}
|
||||
|
||||
BIOSSettingsWidget::RefreshThread::RefreshThread(BIOSSettingsWidget* parent, const QString& directory)
|
||||
: QThread(parent)
|
||||
, m_parent(parent)
|
||||
|
|
|
@ -52,8 +52,11 @@ private Q_SLOTS:
|
|||
void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous);
|
||||
void listRefreshed(const QVector<BIOSInfo>& items);
|
||||
|
||||
void fastBootChanged();
|
||||
|
||||
private:
|
||||
Ui::BIOSSettingsWidget m_ui;
|
||||
SettingsDialog* m_dialog;
|
||||
|
||||
class RefreshThread final : public QThread
|
||||
{
|
||||
|
|
|
@ -136,11 +136,22 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="fastBoot">
|
||||
<property name="text">
|
||||
<string>Fast Boot</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="fastBoot">
|
||||
<property name="text">
|
||||
<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>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
|
@ -92,8 +92,7 @@ namespace Achievements
|
|||
static unsigned PeekMemoryBlock(unsigned address, unsigned char* buffer, unsigned num_bytes);
|
||||
static void PokeMemory(unsigned address, unsigned num_bytes, void* ud, unsigned value);
|
||||
static bool IsMastered();
|
||||
static void ActivateLockedAchievements();
|
||||
static bool ActivateAchievement(Achievement* achievement);
|
||||
static void ActivateAchievementsAndLeaderboards();
|
||||
static void DeactivateAchievement(Achievement* achievement);
|
||||
static void SendPing();
|
||||
static void SendPlaying();
|
||||
|
@ -150,7 +149,8 @@ namespace Achievements
|
|||
static std::string s_username;
|
||||
static std::string s_api_token;
|
||||
|
||||
static u32 s_last_game_crc;
|
||||
static u32 s_last_disc_crc;
|
||||
static u32 s_current_crc;
|
||||
static std::string s_game_path;
|
||||
static std::string s_game_hash;
|
||||
static std::string s_game_title;
|
||||
|
@ -379,7 +379,8 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard
|
|||
while (!s_leaderboards.empty())
|
||||
{
|
||||
Leaderboard& lb = s_leaderboards.back();
|
||||
rc_runtime_deactivate_lboard(&s_rcheevos_runtime, lb.id);
|
||||
if (lb.active)
|
||||
rc_runtime_deactivate_lboard(&s_rcheevos_runtime, lb.id);
|
||||
s_leaderboards.pop_back();
|
||||
}
|
||||
|
||||
|
@ -406,7 +407,7 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard
|
|||
|
||||
void Achievements::ClearGameHash()
|
||||
{
|
||||
s_last_game_crc = 0;
|
||||
s_last_disc_crc = 0;
|
||||
std::string().swap(s_game_hash);
|
||||
}
|
||||
|
||||
|
@ -515,7 +516,7 @@ void Achievements::Initialize()
|
|||
s_logged_in = (!s_username.empty() && !s_api_token.empty());
|
||||
|
||||
if (VMManager::HasValidVM())
|
||||
GameChanged(VMManager::GetGameCRC());
|
||||
GameChanged(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
|
||||
}
|
||||
|
||||
void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_config)
|
||||
|
@ -645,7 +646,13 @@ void Achievements::SetChallengeMode(bool enabled)
|
|||
achievement.locked = true;
|
||||
}
|
||||
for (Leaderboard& leaderboard : s_leaderboards)
|
||||
rc_runtime_deactivate_lboard(&s_rcheevos_runtime, leaderboard.id);
|
||||
{
|
||||
if (leaderboard.active)
|
||||
{
|
||||
rc_runtime_deactivate_lboard(&s_rcheevos_runtime, leaderboard.id);
|
||||
leaderboard.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-grab unlocks, this will reactivate what's locked in non-hardcore mode later on
|
||||
|
@ -767,8 +774,8 @@ void Achievements::LoadState(const u8* state_data, u32 state_data_size)
|
|||
return;
|
||||
|
||||
// this assumes that the CRC and ELF name has been loaded prior to the cheevos state (it should be).
|
||||
if (ElfCRC != s_last_game_crc)
|
||||
GameChanged(ElfCRC);
|
||||
if (VMManager::GetDiscCRC() != s_last_disc_crc)
|
||||
GameChanged(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
if (IsUsingRAIntegration())
|
||||
|
@ -1118,7 +1125,7 @@ void Achievements::GetUserUnlocksCallback(s32 status_code, const std::string& co
|
|||
}
|
||||
|
||||
// start scanning for locked achievements
|
||||
ActivateLockedAchievements();
|
||||
ActivateAchievementsAndLeaderboards();
|
||||
DisplayAchievementSummary();
|
||||
SendPlaying();
|
||||
UpdateRichPresence();
|
||||
|
@ -1227,16 +1234,10 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten
|
|||
lboard.id = defn.id;
|
||||
lboard.title = defn.title;
|
||||
lboard.description = defn.description;
|
||||
lboard.memaddr = defn.definition;
|
||||
lboard.format = defn.format;
|
||||
lboard.active = false;
|
||||
s_leaderboards.push_back(std::move(lboard));
|
||||
|
||||
// Always track the leaderboard, if we don't have leaderboards enabled we just won't submit it.
|
||||
// That way if someone activates them later on, current progress will count.
|
||||
const int err = rc_runtime_activate_lboard(&s_rcheevos_runtime, defn.id, defn.definition, nullptr, 0);
|
||||
if (err != RC_OK)
|
||||
Console.Error("Leaderboard %u memaddr parse error: %s", defn.id, rc_error_str(err));
|
||||
else
|
||||
DevCon.WriteLn("Activated leaderboard %s (%u)", defn.title, defn.id);
|
||||
}
|
||||
|
||||
// Parse rich presence.
|
||||
|
@ -1265,7 +1266,7 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten
|
|||
}
|
||||
else
|
||||
{
|
||||
ActivateLockedAchievements();
|
||||
ActivateAchievementsAndLeaderboards();
|
||||
DisplayAchievementSummary();
|
||||
Host::OnAchievementsRefreshed();
|
||||
}
|
||||
|
@ -1399,7 +1400,7 @@ std::optional<std::vector<u8>> Achievements::ReadELFFromCurrentDisc(const std::s
|
|||
|
||||
std::string Achievements::GetGameHash()
|
||||
{
|
||||
const std::string& elf_path = LastELF;
|
||||
const std::string elf_path = VMManager::GetDiscELF();
|
||||
if (elf_path.empty())
|
||||
return {};
|
||||
|
||||
|
@ -1457,15 +1458,21 @@ void Achievements::GetGameIdCallback(s32 status_code, const std::string& content
|
|||
GetPatches(game_id);
|
||||
}
|
||||
|
||||
void Achievements::GameChanged(u32 crc)
|
||||
void Achievements::GameChanged(u32 disc_crc, u32 crc)
|
||||
{
|
||||
std::unique_lock lock(s_achievements_mutex);
|
||||
if (!s_active)
|
||||
return;
|
||||
|
||||
// avoid reading+hashing the executable if the crc hasn't changed
|
||||
if (s_last_game_crc == crc)
|
||||
if (s_last_disc_crc == disc_crc)
|
||||
{
|
||||
// but we might've just finished booting the game, in which case we need to activate.
|
||||
if (crc != 0)
|
||||
ActivateAchievementsAndLeaderboards();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string game_hash(GetGameHash());
|
||||
if (s_game_hash == game_hash)
|
||||
|
@ -1481,7 +1488,8 @@ void Achievements::GameChanged(u32 crc)
|
|||
|
||||
ClearGameInfo();
|
||||
ClearGameHash();
|
||||
s_last_game_crc = crc;
|
||||
s_last_disc_crc = disc_crc;
|
||||
s_current_crc = crc;
|
||||
s_game_hash = std::move(game_hash);
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
|
@ -1495,13 +1503,14 @@ void Achievements::GameChanged(u32 crc)
|
|||
if (s_game_hash.empty())
|
||||
{
|
||||
// when we're booting the bios, or shutting down, this will fail
|
||||
if (crc != 0)
|
||||
if (disc_crc != 0)
|
||||
{
|
||||
Host::AddKeyedOSDMessage("retroachievements_disc_read_failed", "Failed to read executable from disc. Achievements disabled.",
|
||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
}
|
||||
|
||||
s_last_game_crc = 0;
|
||||
s_last_disc_crc = 0;
|
||||
s_current_crc = crc;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1733,31 +1742,48 @@ bool Achievements::IsMastered()
|
|||
return true;
|
||||
}
|
||||
|
||||
void Achievements::ActivateLockedAchievements()
|
||||
void Achievements::ActivateAchievementsAndLeaderboards()
|
||||
{
|
||||
if (!VMManager::Internal::HasBootedELF())
|
||||
{
|
||||
Console.Warning("Deferring achievement activate until ELF has booted.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Achievement& cheevo : s_achievements)
|
||||
{
|
||||
if (cheevo.locked)
|
||||
ActivateAchievement(&cheevo);
|
||||
if (cheevo.active || !cheevo.locked)
|
||||
continue;
|
||||
|
||||
const int err =
|
||||
rc_runtime_activate_achievement(&s_rcheevos_runtime, cheevo.id, cheevo.memaddr.c_str(), nullptr, 0);
|
||||
if (err != RC_OK)
|
||||
{
|
||||
Console.Error("Achievement %u memaddr parse error: %s", cheevo.id, rc_error_str(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Activated achievement %s (%u)", cheevo.title.c_str(), cheevo.id);
|
||||
cheevo.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Achievements::ActivateAchievement(Achievement* achievement)
|
||||
{
|
||||
if (achievement->active)
|
||||
return true;
|
||||
|
||||
const int err = rc_runtime_activate_achievement(&s_rcheevos_runtime, achievement->id, achievement->memaddr.c_str(), nullptr, 0);
|
||||
if (err != RC_OK)
|
||||
for (Leaderboard& lb : s_leaderboards)
|
||||
{
|
||||
Console.Error("Achievement %u memaddr parse error: %s", achievement->id, rc_error_str(err));
|
||||
return false;
|
||||
// Always track the leaderboard, if we don't have leaderboards enabled we just won't submit it.
|
||||
// That way if someone activates them later on, current progress will count.
|
||||
if (lb.active)
|
||||
continue;
|
||||
|
||||
const int err = rc_runtime_activate_lboard(&s_rcheevos_runtime, lb.id, lb.memaddr.c_str(), nullptr, 0);
|
||||
if (err != RC_OK)
|
||||
{
|
||||
Console.Error("Leaderboard %u memaddr parse error: %s", lb.id, rc_error_str(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Activated leaderboard %s (%u)", lb.title.c_str(), lb.id);
|
||||
lb.active = true;
|
||||
}
|
||||
|
||||
achievement->active = true;
|
||||
|
||||
DevCon.WriteLn("Activated achievement %s (%u)", achievement->title.c_str(), achievement->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Achievements::DeactivateAchievement(Achievement* achievement)
|
||||
|
@ -2249,7 +2275,7 @@ void Achievements::RAIntegration::RACallbackRebuildMenu()
|
|||
|
||||
void Achievements::RAIntegration::RACallbackEstimateTitle(char* buf)
|
||||
{
|
||||
std::string title(fmt::format("{0} ({1}) [{2:08X}]", VMManager::GetGameName(), VMManager::GetGameSerial(), VMManager::GetGameCRC()));
|
||||
std::string title(fmt::format("{0} ({1}) [{2:08X}]", VMManager::GetTitle(), VMManager::GetDiscSerial(), VMManager::GetDiscCRC()));
|
||||
StringUtil::Strlcpy(buf, title, 256);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,9 @@ namespace Achievements
|
|||
u32 id;
|
||||
std::string title;
|
||||
std::string description;
|
||||
std::string memaddr;
|
||||
int format;
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct LeaderboardEntry
|
||||
|
@ -131,7 +133,7 @@ namespace Achievements
|
|||
bool Login(const char* username, const char* password);
|
||||
void Logout();
|
||||
|
||||
void GameChanged(u32 crc);
|
||||
void GameChanged(u32 disc_crc, u32 crc);
|
||||
|
||||
const std::string& GetGameTitle();
|
||||
const std::string& GetGameIcon();
|
||||
|
|
|
@ -40,12 +40,6 @@
|
|||
#include "Recording/InputRecording.h"
|
||||
#include "Host.h"
|
||||
|
||||
// This typically reflects the Sony-assigned serial code for the Disc, if one exists.
|
||||
// (examples: SLUS-2113, etc).
|
||||
// If the disc is homebrew then it probably won't have a valid serial; in which case
|
||||
// this string will be empty.
|
||||
std::string DiscSerial;
|
||||
|
||||
cdvdStruct cdvd;
|
||||
|
||||
s64 PSXCLK = 36864000;
|
||||
|
@ -389,13 +383,13 @@ s32 cdvdWriteConfig(const u8* config)
|
|||
}
|
||||
|
||||
// Sets ElfCRC to the CRC of the game bound to the CDVD source.
|
||||
static __fi ElfObject* loadElf(std::string filename, bool isPSXElf)
|
||||
std::unique_ptr<ElfObject> cdvdLoadElf(std::string filename, bool isPSXElf)
|
||||
{
|
||||
if (StringUtil::StartsWith(filename, "host:"))
|
||||
{
|
||||
std::string host_filename(filename.substr(5));
|
||||
s64 host_size = FileSystem::GetPathFileSize(host_filename.c_str());
|
||||
return new ElfObject(std::move(host_filename), static_cast<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!
|
||||
|
@ -420,31 +414,13 @@ static __fi ElfObject* loadElf(std::string filename, bool isPSXElf)
|
|||
filename += ";1";
|
||||
}
|
||||
|
||||
// Fix cdrom:path, the iso reader doesn't like it.
|
||||
if (StringUtil::StartsWith(filename, "cdrom:") && filename[6] != '\\' && filename[6] != '/')
|
||||
filename.insert(6, 1, '\\');
|
||||
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file(isofs, filename);
|
||||
return new ElfObject(std::move(filename), file, isPSXElf);
|
||||
}
|
||||
|
||||
static __fi void _reloadElfInfo(std::string elfpath)
|
||||
{
|
||||
// Now's a good time to reload the ELF info...
|
||||
if (elfpath == LastELF)
|
||||
return;
|
||||
|
||||
std::unique_ptr<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).
|
||||
return std::make_unique<ElfObject>(std::move(filename), file, isPSXElf);
|
||||
}
|
||||
|
||||
u32 cdvdGetElfCRC(const std::string& path)
|
||||
|
@ -466,6 +442,84 @@ u32 cdvdGetElfCRC(const std::string& path)
|
|||
}
|
||||
}
|
||||
|
||||
// return value:
|
||||
// 0 - Invalid or unknown disc.
|
||||
// 1 - PS1 CD
|
||||
// 2 - PS2 CD
|
||||
static CDVDDiscType GetPS2ElfName(std::string* name, std::string* version)
|
||||
{
|
||||
CDVDDiscType retype = CDVDDiscType::Other;
|
||||
name->clear();
|
||||
version->clear();
|
||||
|
||||
try {
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file( isofs, "SYSTEM.CNF;1");
|
||||
|
||||
int size = file.getLength();
|
||||
if( size == 0 ) return CDVDDiscType::Other;
|
||||
|
||||
while( !file.eof() )
|
||||
{
|
||||
const std::string line(file.readLine());
|
||||
std::string_view key, value;
|
||||
if (!StringUtil::ParseAssignmentString(line, &key, &value))
|
||||
continue;
|
||||
|
||||
if( value.empty() && file.getLength() != file.getSeekPos() )
|
||||
{ // Some games have a character on the last line of the file, don't print the error in those cases.
|
||||
Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" );
|
||||
Console.Indent().WriteLn(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( key == "BOOT2" )
|
||||
{
|
||||
Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PS2 Disc = %.*s",
|
||||
static_cast<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)
|
||||
{
|
||||
// cdrom:\SCES_123.45;1
|
||||
|
@ -478,7 +532,7 @@ static std::string ExecutablePathToSerial(const std::string& path)
|
|||
else
|
||||
{
|
||||
// cdrom:SCES_123.45;1
|
||||
pos = serial.rfind(':');
|
||||
pos = path.rfind(':');
|
||||
if (pos != std::string::npos)
|
||||
serial = path.substr(pos + 1);
|
||||
else
|
||||
|
@ -492,8 +546,11 @@ static std::string ExecutablePathToSerial(const std::string& path)
|
|||
|
||||
// check that it matches our expected format.
|
||||
// this maintains the old behavior of PCSX2.
|
||||
if (!StringUtil::WildcardMatch(serial.c_str(), "????_???.??*"))
|
||||
if (!StringUtil::WildcardMatch(serial.c_str(), "????_???.??*") &&
|
||||
!StringUtil::WildcardMatch(serial.c_str(), "????""-???.??*")) // double quote because trigraphs
|
||||
{
|
||||
serial.clear();
|
||||
}
|
||||
|
||||
// SCES_123.45 -> SCES-12345
|
||||
for (std::string::size_type pos = 0; pos < serial.size();)
|
||||
|
@ -515,60 +572,62 @@ static std::string ExecutablePathToSerial(const std::string& path)
|
|||
return serial;
|
||||
}
|
||||
|
||||
void cdvdReloadElfInfo(std::string elfoverride)
|
||||
void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type)
|
||||
{
|
||||
// called from context of executing VM code (recompilers), so we need to trap exceptions
|
||||
// and route them through the VM's exception handler. (needed for non-SEH platforms, such
|
||||
// as Linux/GCC)
|
||||
DevCon.WriteLn(Color_Green, "Reload ELF");
|
||||
try
|
||||
{
|
||||
std::string elfpath;
|
||||
u32 discType = GetPS2ElfName(elfpath);
|
||||
DiscSerial = ExecutablePathToSerial(elfpath);
|
||||
std::string elfpath, version;
|
||||
const CDVDDiscType disc_type = GetPS2ElfName(&elfpath, &version);
|
||||
|
||||
// Use the serial from the disc (if any), and the ELF CRC of the override.
|
||||
if (!elfoverride.empty())
|
||||
// Don't bother parsing it if we don't need the CRC.
|
||||
if (out_crc)
|
||||
{
|
||||
u32 crc = 0;
|
||||
|
||||
if (disc_type == CDVDDiscType::PS2Disc || disc_type == CDVDDiscType::PS1Disc)
|
||||
{
|
||||
_reloadElfInfo(std::move(elfoverride));
|
||||
return;
|
||||
try
|
||||
{
|
||||
const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc);
|
||||
std::unique_ptr<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)
|
||||
{
|
||||
// PCSX2 currently only recognizes *.elf executables in proper PS2 format.
|
||||
// To support different PSX titles in the console title and for savestates, this code bypasses all the detection,
|
||||
// simply using the exe name, stripped of problematic characters.
|
||||
return;
|
||||
}
|
||||
|
||||
// Isn't a disc we recognize?
|
||||
if (discType == 0)
|
||||
return;
|
||||
|
||||
// Recognized and PS2 (BOOT2). Good job, user.
|
||||
_reloadElfInfo(std::move(elfpath));
|
||||
*out_crc = crc;
|
||||
}
|
||||
catch ([[maybe_unused]] Exception::FileNotFound& e)
|
||||
|
||||
if (out_serial)
|
||||
{
|
||||
Console.Error("Failed to load ELF info");
|
||||
LastELF.clear();
|
||||
DiscSerial.clear();
|
||||
ElfCRC = 0;
|
||||
ElfEntry = 0;
|
||||
ElfTextRange = {};
|
||||
return;
|
||||
if (disc_type != CDVDDiscType::Other)
|
||||
*out_serial = ExecutablePathToSerial(elfpath);
|
||||
else
|
||||
out_serial->clear();
|
||||
}
|
||||
if (out_elf_path)
|
||||
*out_elf_path = std::move(elfpath);
|
||||
if (out_version)
|
||||
*out_version = std::move(version);
|
||||
if (out_disc_type)
|
||||
*out_disc_type = disc_type;
|
||||
}
|
||||
|
||||
void cdvdReadKey(u8, u16, u32 arg2, u8* key)
|
||||
{
|
||||
const std::string DiscSerial = VMManager::GetDiscSerial();
|
||||
|
||||
s32 numbers = 0, letters = 0;
|
||||
u32 key_0_3;
|
||||
u8 key_4, key_14;
|
||||
|
||||
cdvdReloadElfInfo();
|
||||
|
||||
// clear key values
|
||||
memset(key, 0, 16);
|
||||
|
||||
|
@ -697,7 +756,7 @@ s32 cdvdCtrlTrayClose()
|
|||
|
||||
DevCon.WriteLn(Color_Green, "Close virtual disk tray");
|
||||
|
||||
if (!g_GameStarted && g_SkipBiosHack)
|
||||
if (VMManager::Internal::IsFastBootInProgress())
|
||||
{
|
||||
DevCon.WriteLn(Color_Green, "Media already loaded (fast boot)");
|
||||
cdvdUpdateReady(CDVD_DRIVE_READY);
|
||||
|
@ -901,10 +960,6 @@ void cdvdReset()
|
|||
cdvd.RTC.year = (u8)(curtime.tm_year - 100); // offset from 2000
|
||||
}
|
||||
|
||||
g_GameStarted = false;
|
||||
g_GameLoading = false;
|
||||
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
|
||||
|
||||
cdvdCtrlTrayClose();
|
||||
}
|
||||
|
||||
|
@ -930,7 +985,7 @@ void cdvdNewDiskCB()
|
|||
cdvdDetectDisk();
|
||||
|
||||
// If not ejected but we've swapped source pretend it got ejected
|
||||
if ((g_GameStarted || !g_SkipBiosHack) && cdvd.Tray.trayState != CDVD_DISC_EJECT)
|
||||
if (!VMManager::Internal::IsFastBootInProgress() && cdvd.Tray.trayState != CDVD_DISC_EJECT)
|
||||
{
|
||||
DevCon.WriteLn(Color_Green, "Ejecting media");
|
||||
cdvdUpdateStatus(CDVD_STATUS_TRAY_OPEN);
|
||||
|
@ -1448,12 +1503,6 @@ void cdvdUpdateTrayState()
|
|||
cdvd.Tray.trayState = CDVD_DISC_SEEKING;
|
||||
cdvdUpdateStatus(CDVD_STATUS_SEEK);
|
||||
cdvd.Tray.cdvdActionSeconds = 2;
|
||||
// If we're swapping disc, reload the elf, patches etc to reflect the new disc.
|
||||
if (g_GameStarted)
|
||||
{
|
||||
cdvdReloadElfInfo();
|
||||
VMManager::Internal::SwappingGameOnCPUThread();
|
||||
}
|
||||
break;
|
||||
case CDVD_DISC_SEEKING:
|
||||
cdvd.Spinning = true;
|
||||
|
@ -2568,22 +2617,25 @@ static void cdvdWrite16(u8 rt) // SCOMMAND
|
|||
// break;
|
||||
|
||||
case 0x27: // GetPS1BootParam (0:13) - called only by China region PS2 models
|
||||
{
|
||||
// Return Disc Serial which is passed to PS1DRV and later used to find matching config.
|
||||
SetSCMDResultSize(13);
|
||||
|
||||
// Return Disc Serial which is passed to PS1DRV and later used to find matching config.
|
||||
SetSCMDResultSize(13);
|
||||
cdvd.SCMDResult[0] = 0;
|
||||
cdvd.SCMDResult[1] = DiscSerial[0];
|
||||
cdvd.SCMDResult[2] = DiscSerial[1];
|
||||
cdvd.SCMDResult[3] = DiscSerial[2];
|
||||
cdvd.SCMDResult[4] = DiscSerial[3];
|
||||
cdvd.SCMDResult[5] = DiscSerial[4];
|
||||
cdvd.SCMDResult[6] = DiscSerial[5];
|
||||
cdvd.SCMDResult[7] = DiscSerial[6];
|
||||
cdvd.SCMDResult[8] = DiscSerial[7];
|
||||
cdvd.SCMDResult[9] = DiscSerial[9]; // Skipping dot here is required.
|
||||
cdvd.SCMDResult[10] = DiscSerial[10];
|
||||
cdvd.SCMDResult[11] = DiscSerial[11];
|
||||
cdvd.SCMDResult[12] = DiscSerial[12];
|
||||
const std::string DiscSerial = VMManager::GetDiscSerial();
|
||||
cdvd.SCMDResult[0] = 0;
|
||||
cdvd.SCMDResult[1] = DiscSerial[0];
|
||||
cdvd.SCMDResult[2] = DiscSerial[1];
|
||||
cdvd.SCMDResult[3] = DiscSerial[2];
|
||||
cdvd.SCMDResult[4] = DiscSerial[3];
|
||||
cdvd.SCMDResult[5] = DiscSerial[4];
|
||||
cdvd.SCMDResult[6] = DiscSerial[5];
|
||||
cdvd.SCMDResult[7] = DiscSerial[6];
|
||||
cdvd.SCMDResult[8] = DiscSerial[7];
|
||||
cdvd.SCMDResult[9] = DiscSerial[9]; // Skipping dot here is required.
|
||||
cdvd.SCMDResult[10] = DiscSerial[10];
|
||||
cdvd.SCMDResult[11] = DiscSerial[11];
|
||||
cdvd.SCMDResult[12] = DiscSerial[12];
|
||||
}
|
||||
break;
|
||||
|
||||
// case 0x28: // cdvdman_call150 (1:1) - In V10 Bios
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
#include "CDVDcommon.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class ElfObject;
|
||||
|
||||
#define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
|
||||
#define itob(i) ((i) / 10 * 16 + (i) % 10) /* u_char to BCD */
|
||||
|
||||
|
@ -76,6 +79,13 @@ struct cdvdRTC
|
|||
u8 year;
|
||||
};
|
||||
|
||||
enum class CDVDDiscType : u8
|
||||
{
|
||||
Other,
|
||||
PS1Disc,
|
||||
PS2Disc
|
||||
};
|
||||
|
||||
enum TrayStates
|
||||
{
|
||||
CDVD_DISC_ENGAGED,
|
||||
|
@ -176,9 +186,11 @@ extern void cdvdNewDiskCB();
|
|||
extern u8 cdvdRead(u8 key);
|
||||
extern void cdvdWrite(u8 key, u8 rt);
|
||||
|
||||
extern void cdvdReloadElfInfo(std::string elfoverride = std::string());
|
||||
extern void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type);
|
||||
extern u32 cdvdGetElfCRC(const std::string& path);
|
||||
extern std::unique_ptr<ElfObject> cdvdLoadElf(std::string filename, bool isPSXElf);
|
||||
|
||||
extern s32 cdvdCtrlTrayOpen();
|
||||
extern s32 cdvdCtrlTrayClose();
|
||||
|
||||
extern std::string DiscSerial;
|
||||
|
|
|
@ -346,6 +346,12 @@ CDVD_SourceType CDVDsys_GetSourceType()
|
|||
return m_CurrentSourceType;
|
||||
}
|
||||
|
||||
void CDVDsys_ClearFiles()
|
||||
{
|
||||
for (u32 i = 0; i < std::size(m_SourceFilename); i++)
|
||||
m_SourceFilename[i] = {};
|
||||
}
|
||||
|
||||
void CDVDsys_ChangeSource(CDVD_SourceType type)
|
||||
{
|
||||
if (CDVD != NULL)
|
||||
|
@ -375,14 +381,6 @@ bool DoCDVDopen()
|
|||
|
||||
CDVD->newDiskCB(cdvdNewDiskCB);
|
||||
|
||||
// Win32 Fail: the old CDVD api expects MBCS on Win32 platforms, but generating a MBCS
|
||||
// from unicode is problematic since we need to know the codepage of the text being
|
||||
// converted (which isn't really practical knowledge). A 'best guess' would be the
|
||||
// default codepage of the user's Windows install, but even that will fail and return
|
||||
// question marks if the filename is another language.
|
||||
|
||||
//TODO_CDVD check if ISO and Disc use UTF8
|
||||
|
||||
auto CurrentSourceType = enum_cast(m_CurrentSourceType);
|
||||
int ret = CDVD->open(!m_SourceFilename[CurrentSourceType].empty() ? m_SourceFilename[CurrentSourceType].c_str() : nullptr);
|
||||
if (ret == -1)
|
||||
|
@ -396,19 +394,14 @@ bool DoCDVDopen()
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string somepick(Path::StripExtension(FileSystem::GetDisplayNameFromPath(m_SourceFilename[CurrentSourceType])));
|
||||
//FWIW Disc serial availability doesn't seem reliable enough, sometimes it's there and sometime it's just null
|
||||
//Shouldn't the serial be available all time? Potentially need to look into Elfreloadinfo() reliability
|
||||
//TODO: Add extra fallback case for CRC.
|
||||
if (somepick.empty() && !DiscSerial.empty())
|
||||
somepick = StringUtil::StdStringFromFormat("Untitled-%s", DiscSerial.c_str());
|
||||
else if (somepick.empty())
|
||||
somepick = "Untitled";
|
||||
std::string dump_name(Path::StripExtension(FileSystem::GetDisplayNameFromPath(m_SourceFilename[CurrentSourceType])));
|
||||
if (dump_name.empty())
|
||||
dump_name = "Untitled";
|
||||
|
||||
if (EmuConfig.CurrentBlockdump.empty())
|
||||
EmuConfig.CurrentBlockdump = FileSystem::GetWorkingDirectory();
|
||||
|
||||
std::string temp(Path::Combine(EmuConfig.CurrentBlockdump, somepick));
|
||||
std::string temp(Path::Combine(EmuConfig.CurrentBlockdump, dump_name));
|
||||
|
||||
#ifdef ENABLE_TIMESTAMPS
|
||||
std::time_t curtime_t = std::time(nullptr);
|
||||
|
|
|
@ -158,6 +158,7 @@ extern void CDVDsys_ChangeSource(CDVD_SourceType type);
|
|||
extern void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile);
|
||||
extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype);
|
||||
extern CDVD_SourceType CDVDsys_GetSourceType();
|
||||
extern void CDVDsys_ClearFiles();
|
||||
|
||||
extern bool DoCDVDopen();
|
||||
extern void DoCDVDclose();
|
||||
|
|
|
@ -1244,14 +1244,15 @@ struct Pcsx2Config
|
|||
EnablePINE : 1, // enables inter-process communication
|
||||
EnableWideScreenPatches : 1,
|
||||
EnableNoInterlacingPatches : 1,
|
||||
EnableFastBoot : 1,
|
||||
EnableFastBootFastForward : 1,
|
||||
EnablePerGameSettings : 1,
|
||||
// TODO - Vaser - where are these settings exposed in the Qt UI?
|
||||
EnableRecordingTools : 1,
|
||||
EnableGameFixes : 1, // enables automatic game fixes
|
||||
SaveStateOnShutdown : 1, // default value for saving state on shutdown
|
||||
EnableDiscordPresence : 1, // enables discord rich presence integration
|
||||
InhibitScreensaver : 1,
|
||||
// when enabled uses BOOT2 injection, skipping sony bios splashes
|
||||
UseBOOT2Injection : 1,
|
||||
BackupSavestate : 1,
|
||||
SavestateZstdCompression : 1,
|
||||
// enables simulated ejection of memory cards when loading savestates
|
||||
|
|
|
@ -22,40 +22,54 @@
|
|||
#include "Elfheader.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
|
||||
u32 ElfCRC;
|
||||
u32 ElfEntry;
|
||||
std::pair<u32,u32> ElfTextRange;
|
||||
std::string LastELF;
|
||||
bool isPSXElf;
|
||||
std::string ElfVersion;
|
||||
#pragma pack(push, 1)
|
||||
struct PSXEXEHeader
|
||||
{
|
||||
char id[8]; // 0x000-0x007 PS-X EXE
|
||||
char pad1[8]; // 0x008-0x00F
|
||||
u32 initial_pc; // 0x010
|
||||
u32 initial_gp; // 0x014
|
||||
u32 load_address; // 0x018
|
||||
u32 file_size; // 0x01C excluding 0x800-byte header
|
||||
u32 unk0; // 0x020
|
||||
u32 unk1; // 0x024
|
||||
u32 memfill_start; // 0x028
|
||||
u32 memfill_size; // 0x02C
|
||||
u32 initial_sp_base; // 0x030
|
||||
u32 initial_sp_offset; // 0x034
|
||||
u32 reserved[5]; // 0x038-0x04B
|
||||
char marker[0x7B4]; // 0x04C-0x7FF
|
||||
};
|
||||
static_assert(sizeof(PSXEXEHeader) == 0x800);
|
||||
#pragma pack(pop)
|
||||
|
||||
// All of ElfObjects functions.
|
||||
ElfObject::ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf)
|
||||
ElfObject::ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_)
|
||||
: data(isofile.getLength(), "ELF headers")
|
||||
, filename(std::move(srcfile))
|
||||
, header(*(ELF_HEADER*)data.GetPtr())
|
||||
, filename(std::move(srcfile))
|
||||
, isPSXElf(isPSXElf_)
|
||||
{
|
||||
checkElfSize(data.GetSizeInBytes());
|
||||
readIso(isofile);
|
||||
initElfHeaders(isPSXElf);
|
||||
initElfHeaders();
|
||||
}
|
||||
|
||||
ElfObject::ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf)
|
||||
ElfObject::ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_)
|
||||
: data(hdrsize, "ELF headers")
|
||||
, filename(std::move(srcfile))
|
||||
, header(*(ELF_HEADER*)data.GetPtr())
|
||||
, filename(std::move(srcfile))
|
||||
, isPSXElf(isPSXElf_)
|
||||
{
|
||||
checkElfSize(data.GetSizeInBytes());
|
||||
readFile();
|
||||
initElfHeaders(isPSXElf);
|
||||
initElfHeaders();
|
||||
}
|
||||
|
||||
void ElfObject::initElfHeaders(bool isPSXElf)
|
||||
void ElfObject::initElfHeaders()
|
||||
{
|
||||
if (isPSXElf)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Initializing Elf: %d bytes", data.GetSizeInBytes());
|
||||
|
||||
|
@ -133,19 +147,57 @@ void ElfObject::initElfHeaders(bool isPSXElf)
|
|||
//applyPatches();
|
||||
}
|
||||
|
||||
bool ElfObject::hasValidPSXHeader()
|
||||
{
|
||||
if (data.GetSizeInBytes() < sizeof(PSXEXEHeader))
|
||||
return false;
|
||||
|
||||
const PSXEXEHeader* header = reinterpret_cast<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::hasSectionHeaders() { return (secthead != NULL); }
|
||||
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()
|
||||
{
|
||||
for (int i = 0; i < header.e_phnum; i++)
|
||||
if (isPSXElf && hasProgramHeaders())
|
||||
{
|
||||
u32 start = proghead[i].p_vaddr;
|
||||
u32 size = proghead[i].p_memsz;
|
||||
for (int i = 0; i < header.e_phnum; i++)
|
||||
{
|
||||
const u32 start = proghead[i].p_vaddr;
|
||||
const u32 size = proghead[i].p_memsz;
|
||||
|
||||
if (start <= header.e_entry && (start+size) > header.e_entry)
|
||||
return std::make_pair(start,size);
|
||||
if (start <= header.e_entry && (start + size) > header.e_entry)
|
||||
return std::make_pair(start, size);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(0,0);
|
||||
|
@ -310,82 +362,9 @@ void ElfObject::loadSectionHeaders()
|
|||
|
||||
void ElfObject::loadHeaders()
|
||||
{
|
||||
if (isPSXElf)
|
||||
return;
|
||||
|
||||
loadProgramHeaders();
|
||||
loadSectionHeaders();
|
||||
}
|
||||
|
||||
// return value:
|
||||
// 0 - Invalid or unknown disc.
|
||||
// 1 - PS1 CD
|
||||
// 2 - PS2 CD
|
||||
int GetPS2ElfName( std::string& name )
|
||||
{
|
||||
int retype = 0;
|
||||
|
||||
try {
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file( isofs, "SYSTEM.CNF;1");
|
||||
|
||||
int size = file.getLength();
|
||||
if( size == 0 ) return 0;
|
||||
|
||||
while( !file.eof() )
|
||||
{
|
||||
const std::string line(file.readLine());
|
||||
std::string_view key, value;
|
||||
if (!StringUtil::ParseAssignmentString(line, &key, &value))
|
||||
continue;
|
||||
|
||||
if( value.empty() && file.getLength() != file.getSeekPos() )
|
||||
{ // Some games have a character on the last line of the file, don't print the error in those cases.
|
||||
Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" );
|
||||
Console.Indent().WriteLn(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( key == "BOOT2" )
|
||||
{
|
||||
Console.WriteLn( Color_StrongBlue, "(SYSTEM.CNF) Detected PS2 Disc = %.*s",
|
||||
static_cast<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;
|
||||
}
|
||||
|
|
|
@ -122,24 +122,23 @@ class ElfObject
|
|||
{
|
||||
private:
|
||||
SafeArray<u8> data;
|
||||
ELF_HEADER& header;
|
||||
ELF_PHR* proghead = nullptr;
|
||||
ELF_SHR* secthead = nullptr;
|
||||
std::string filename;
|
||||
bool isPSXElf;
|
||||
|
||||
void initElfHeaders(bool isPSXElf);
|
||||
void initElfHeaders();
|
||||
bool hasValidPSXHeader();
|
||||
void readIso(IsoFile& file);
|
||||
void readFile();
|
||||
void checkElfSize(s64 elfsize);
|
||||
|
||||
public:
|
||||
ELF_HEADER& header;
|
||||
ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf_);
|
||||
ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf_);
|
||||
|
||||
// Destructor!
|
||||
// C++ does all the cleanup automagically for us.
|
||||
virtual ~ElfObject() = default;
|
||||
|
||||
ElfObject(std::string srcfile, IsoFile& isofile, bool isPSXElf);
|
||||
ElfObject(std::string srcfile, u32 hdrsize, bool isPSXElf);
|
||||
bool IsPSXElf() const { return isPSXElf; }
|
||||
|
||||
void loadProgramHeaders();
|
||||
void loadSectionHeaders();
|
||||
|
@ -150,17 +149,8 @@ class ElfObject
|
|||
bool hasHeaders();
|
||||
|
||||
std::pair<u32,u32> getTextRange();
|
||||
u32 getEntryPoint();
|
||||
u32 getCRC();
|
||||
};
|
||||
|
||||
//-------------------
|
||||
extern void loadElfFile(const std::string& filename);
|
||||
extern int GetPS2ElfName( std::string& dest );
|
||||
|
||||
|
||||
extern u32 ElfCRC;
|
||||
extern u32 ElfEntry;
|
||||
extern std::pair<u32,u32> ElfTextRange;
|
||||
extern std::string LastELF;
|
||||
extern bool isPSXElf;
|
||||
extern std::string ElfVersion;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "Gif_Unit.h"
|
||||
#include "Counters.h"
|
||||
#include "Config.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
using namespace Threading;
|
||||
using namespace R5900;
|
||||
|
@ -45,7 +46,8 @@ void gsReset()
|
|||
|
||||
void gsUpdateFrequency(Pcsx2Config& config)
|
||||
{
|
||||
if (config.GS.FrameLimitEnable)
|
||||
if (config.GS.FrameLimitEnable &&
|
||||
(!config.EnableFastBootFastForward || !VMManager::Internal::IsFastBootInProgress()))
|
||||
{
|
||||
switch (config.LimiterMode)
|
||||
{
|
||||
|
|
|
@ -667,7 +667,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
|||
std::string_view compression_str;
|
||||
if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed)
|
||||
{
|
||||
m_dump = std::unique_ptr<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_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||
fd, m_regs));
|
||||
|
@ -675,7 +675,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
|||
}
|
||||
else if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::LZMA)
|
||||
{
|
||||
m_dump = std::unique_ptr<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_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||
fd, m_regs));
|
||||
|
@ -683,7 +683,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
|||
}
|
||||
else
|
||||
{
|
||||
m_dump = std::unique_ptr<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_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||
fd, m_regs));
|
||||
|
@ -780,14 +780,14 @@ static std::string GSGetBaseFilename()
|
|||
std::string filename;
|
||||
|
||||
// append the game serial and title
|
||||
if (std::string name(VMManager::GetGameName()); !name.empty())
|
||||
if (std::string name(VMManager::GetTitle()); !name.empty())
|
||||
{
|
||||
Path::SanitizeFileName(&name);
|
||||
if (name.length() > 219)
|
||||
name.resize(219);
|
||||
filename += name;
|
||||
}
|
||||
if (std::string serial(VMManager::GetGameSerial()); !serial.empty())
|
||||
if (std::string serial = VMManager::GetDiscSerial(); !serial.empty())
|
||||
{
|
||||
Path::SanitizeFileName(&serial);
|
||||
filename += '_';
|
||||
|
|
|
@ -303,7 +303,7 @@ std::string GSTextureReplacements::GetDumpFilename(const TextureName& name, u32
|
|||
|
||||
void GSTextureReplacements::Initialize()
|
||||
{
|
||||
s_current_serial = VMManager::GetGameSerial();
|
||||
s_current_serial = VMManager::GetDiscSerial();
|
||||
|
||||
if (GSConfig.DumpReplaceableTextures || GSConfig.LoadTextureReplacements)
|
||||
StartWorkerThread();
|
||||
|
@ -313,7 +313,7 @@ void GSTextureReplacements::Initialize()
|
|||
|
||||
void GSTextureReplacements::GameChanged()
|
||||
{
|
||||
std::string new_serial(VMManager::GetGameSerial());
|
||||
std::string new_serial = VMManager::GetDiscSerial();
|
||||
if (s_current_serial == new_serial)
|
||||
return;
|
||||
|
||||
|
|
|
@ -204,9 +204,6 @@ static void GSDumpReplayerLoadInitialState()
|
|||
std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(),
|
||||
std::min(Ps2MemSize::GSregs, static_cast<u32>(s_dump_file->GetRegsData().size())));
|
||||
|
||||
// update serial to load hw fixes
|
||||
VMManager::Internal::GameStartingOnCPUThread();
|
||||
|
||||
// load GS state
|
||||
freezeData fd = {static_cast<int>(s_dump_file->GetStateData().size()),
|
||||
const_cast<u8*>(s_dump_file->GetStateData().data())};
|
||||
|
|
|
@ -60,22 +60,17 @@ std::string GameDatabaseSchema::GameEntry::memcardFiltersAsString() const
|
|||
|
||||
const std::string* GameDatabaseSchema::GameEntry::findPatch(u32 crc) const
|
||||
{
|
||||
Console.WriteLn(fmt::format("[GameDB] Searching for patch with CRC '{:08X}'", crc));
|
||||
if (crc == 0)
|
||||
return nullptr;
|
||||
|
||||
auto it = patches.find(crc);
|
||||
if (it != patches.end())
|
||||
{
|
||||
Console.WriteLn(fmt::format("[GameDB] Found patch with CRC '{:08X}'", crc));
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
it = patches.find(0);
|
||||
if (it != patches.end())
|
||||
{
|
||||
Console.WriteLn("[GameDB] Found and falling back to default patch");
|
||||
return &it->second;
|
||||
}
|
||||
Console.WriteLn("[GameDB] No CRC-specific patch or default patch found");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -173,19 +173,10 @@ bool GameList::GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std::
|
|||
if (CDVD->open(path.c_str()) != 0)
|
||||
return false;
|
||||
|
||||
// TODO: we could include the version in the game list?
|
||||
*disc_type = DoCDVDdetectDiskType();
|
||||
cdvdReloadElfInfo();
|
||||
|
||||
*serial = DiscSerial;
|
||||
*crc = ElfCRC;
|
||||
|
||||
cdvdGetDiscInfo(serial, nullptr, nullptr, crc, nullptr);
|
||||
DoCDVDclose();
|
||||
|
||||
// TODO(Stenzek): These globals are **awful**. Clean it up.
|
||||
DiscSerial.clear();
|
||||
ElfCRC = 0;
|
||||
ElfEntry = -1;
|
||||
LastELF.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,9 +79,9 @@ static void HotkeyCycleSaveSlot(s32 delta)
|
|||
else
|
||||
s_current_save_slot = (s_current_save_slot % CYCLE_SAVE_STATE_SLOTS) + 1;
|
||||
|
||||
const u32 crc = VMManager::GetGameCRC();
|
||||
const std::string serial(VMManager::GetGameSerial());
|
||||
const std::string filename(VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot));
|
||||
const u32 crc = VMManager::GetDiscCRC();
|
||||
const std::string serial = VMManager::GetDiscSerial();
|
||||
const std::string filename = VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot);
|
||||
FILESYSTEM_STAT_DATA sd;
|
||||
if (!filename.empty() && FileSystem::StatFile(filename.c_str(), &sd))
|
||||
{
|
||||
|
@ -109,34 +109,21 @@ static void HotkeyCycleSaveSlot(s32 delta)
|
|||
|
||||
static void HotkeyLoadStateSlot(s32 slot)
|
||||
{
|
||||
const u32 crc = VMManager::GetGameCRC();
|
||||
if (crc == 0)
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot load state from a slot without a game running.",
|
||||
Host::OSD_INFO_DURATION);
|
||||
return;
|
||||
}
|
||||
// Can reapply settings and thus binds, therefore must be deferred.
|
||||
Host::RunOnCPUThread([slot]() {
|
||||
if (!VMManager::HasSaveStateInSlot(VMManager::GetDiscSerial().c_str(), VMManager::GetDiscCRC(), slot))
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot),
|
||||
Host::OSD_INFO_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string serial(VMManager::GetGameSerial());
|
||||
if (!VMManager::HasSaveStateInSlot(serial.c_str(), crc, slot))
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot),
|
||||
Host::OSD_INFO_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
VMManager::LoadStateFromSlot(slot);
|
||||
VMManager::LoadStateFromSlot(slot);
|
||||
});
|
||||
}
|
||||
|
||||
static void HotkeySaveStateSlot(s32 slot)
|
||||
{
|
||||
if (VMManager::GetGameCRC() == 0)
|
||||
{
|
||||
Host::AddIconOSDMessage("SaveStateToSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot save state to a slot without a game running.",
|
||||
Host::OSD_INFO_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
VMManager::SaveStateToSlot(slot);
|
||||
}
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ namespace FullscreenUI
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static void UpdateGameDetails(std::string path, std::string serial, std::string title, u32 crc);
|
||||
static void UpdateGameDetails(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc);
|
||||
static void ToggleTheme();
|
||||
static void PauseForMenuOpen();
|
||||
static void ClosePauseMenu();
|
||||
|
@ -229,9 +229,9 @@ namespace FullscreenUI
|
|||
// local copies of the currently-running game
|
||||
static std::string s_current_game_title;
|
||||
static std::string s_current_game_subtitle;
|
||||
static std::string s_current_game_serial;
|
||||
static std::string s_current_game_path;
|
||||
static u32 s_current_game_crc;
|
||||
static std::string s_current_disc_serial;
|
||||
static std::string s_current_disc_path;
|
||||
static u32 s_current_disc_crc;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Resources
|
||||
|
@ -578,7 +578,8 @@ bool FullscreenUI::Initialize()
|
|||
|
||||
if (VMManager::HasValidVM())
|
||||
{
|
||||
UpdateGameDetails(VMManager::GetDiscPath(), VMManager::GetGameSerial(), VMManager::GetGameName(), VMManager::GetGameCRC());
|
||||
UpdateGameDetails(VMManager::GetDiscPath(), VMManager::GetDiscSerial(), VMManager::GetTitle(),
|
||||
VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -651,20 +652,21 @@ void FullscreenUI::OnVMDestroyed()
|
|||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::GameChanged(std::string path, std::string serial, std::string title, u32 crc)
|
||||
void FullscreenUI::GameChanged(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc)
|
||||
{
|
||||
if (!IsInitialized())
|
||||
return;
|
||||
|
||||
GetMTGS().RunOnGSThread([path = std::move(path), serial = std::move(serial), title = std::move(title), crc]() {
|
||||
if (!IsInitialized())
|
||||
return;
|
||||
GetMTGS().RunOnGSThread(
|
||||
[path = std::move(path), serial = std::move(serial), title = std::move(title), disc_crc, crc]() {
|
||||
if (!IsInitialized())
|
||||
return;
|
||||
|
||||
UpdateGameDetails(std::move(path), std::move(serial), std::move(title), crc);
|
||||
});
|
||||
UpdateGameDetails(std::move(path), std::move(serial), std::move(title), disc_crc, crc);
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::UpdateGameDetails(std::string path, std::string serial, std::string title, u32 crc)
|
||||
void FullscreenUI::UpdateGameDetails(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc)
|
||||
{
|
||||
if (!serial.empty())
|
||||
s_current_game_subtitle = fmt::format("{} / {:08X}", serial, crc);
|
||||
|
@ -672,9 +674,9 @@ void FullscreenUI::UpdateGameDetails(std::string path, std::string serial, std::
|
|||
s_current_game_subtitle = {};
|
||||
|
||||
s_current_game_title = std::move(title);
|
||||
s_current_game_serial = std::move(serial);
|
||||
s_current_game_path = std::move(path);
|
||||
s_current_game_crc = crc;
|
||||
s_current_disc_serial = std::move(serial);
|
||||
s_current_disc_path = std::move(path);
|
||||
s_current_disc_crc = disc_crc;
|
||||
}
|
||||
|
||||
void FullscreenUI::ToggleTheme()
|
||||
|
@ -744,9 +746,9 @@ void FullscreenUI::Shutdown(bool clear_state)
|
|||
s_graphics_adapter_list_cache = {};
|
||||
s_current_game_title = {};
|
||||
s_current_game_subtitle = {};
|
||||
s_current_game_serial = {};
|
||||
s_current_game_path = {};
|
||||
s_current_game_crc = 0;
|
||||
s_current_disc_serial = {};
|
||||
s_current_disc_path = {};
|
||||
s_current_disc_crc = 0;
|
||||
|
||||
s_current_main_window = MainWindowType::None;
|
||||
s_current_pause_submenu = PauseSubMenu::None;
|
||||
|
@ -1053,7 +1055,7 @@ void FullscreenUI::DoChangeDiscFromFile()
|
|||
};
|
||||
|
||||
OpenFileSelector(ICON_FA_COMPACT_DISC " Select Disc Image", false, std::move(callback), GetDiscImageFilters(),
|
||||
std::string(Path::GetDirectory(s_current_game_path)));
|
||||
std::string(Path::GetDirectory(s_current_disc_path)));
|
||||
}
|
||||
|
||||
void FullscreenUI::DoChangeDisc()
|
||||
|
@ -2333,13 +2335,13 @@ void FullscreenUI::SwitchToGameSettings(const std::string_view& serial, u32 crc)
|
|||
|
||||
void FullscreenUI::SwitchToGameSettings()
|
||||
{
|
||||
if (s_current_game_serial.empty() || s_current_game_crc == 0)
|
||||
if (s_current_disc_serial.empty() || s_current_disc_crc == 0)
|
||||
return;
|
||||
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_game_path.c_str());
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str());
|
||||
if (!entry)
|
||||
entry = GameList::GetEntryBySerialAndCRC(s_current_game_serial.c_str(), s_current_game_crc);
|
||||
entry = GameList::GetEntryBySerialAndCRC(s_current_disc_serial.c_str(), s_current_disc_crc);
|
||||
|
||||
if (entry)
|
||||
SwitchToGameSettings(entry);
|
||||
|
@ -4266,7 +4268,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
|
||||
const float image_width = has_rich_presence ? 60.0f : 50.0f;
|
||||
const float image_height = has_rich_presence ? 90.0f : 75.0f;
|
||||
const std::string_view path_string(Path::GetFileName(s_current_game_path));
|
||||
const std::string_view path_string(Path::GetFileName(s_current_disc_path));
|
||||
const ImVec2 title_size(
|
||||
g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits<float>::max(), -1.0f, s_current_game_title.c_str()));
|
||||
const ImVec2 path_size(path_string.empty() ?
|
||||
|
@ -4345,9 +4347,9 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
const ImVec2 time_pos(display_size.x - LayoutScale(10.0f) - time_size.x, LayoutScale(10.0f));
|
||||
DrawShadowedText(dl, g_large_font, time_pos, IM_COL32(255, 255, 255, 255), buf);
|
||||
|
||||
if (!s_current_game_serial.empty())
|
||||
if (!s_current_disc_serial.empty())
|
||||
{
|
||||
const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(s_current_game_serial);
|
||||
const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(s_current_disc_serial);
|
||||
const std::time_t session_time = static_cast<std::time_t>(VMManager::GetSessionPlayedTime());
|
||||
const std::string played_time_str(GameList::FormatTimespan(cached_played_time + session_time, true));
|
||||
const std::string session_time_str(GameList::FormatTimespan(session_time, true));
|
||||
|
@ -4389,7 +4391,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
case PauseSubMenu::None:
|
||||
{
|
||||
// NOTE: Menu close must come first, because otherwise VM destruction options will race.
|
||||
const bool can_load_or_save_state = s_current_game_crc != 0;
|
||||
const bool can_load_or_save_state = s_current_disc_crc != 0;
|
||||
|
||||
if (ActiveButton(ICON_FA_PLAY " Resume Game", false) || WantsToCloseMenu())
|
||||
ClosePauseMenu();
|
||||
|
@ -4614,7 +4616,7 @@ bool FullscreenUI::OpenSaveStateSelector(bool is_loading)
|
|||
s_save_state_selector_game_path = {};
|
||||
s_save_state_selector_loading = is_loading;
|
||||
s_save_state_selector_resuming = false;
|
||||
if (PopulateSaveStateListEntries(s_current_game_title.c_str(), s_current_game_serial.c_str(), s_current_game_crc) > 0)
|
||||
if (PopulateSaveStateListEntries(s_current_game_title.c_str(), s_current_disc_serial.c_str(), s_current_disc_crc) > 0)
|
||||
{
|
||||
s_save_state_selector_open = true;
|
||||
return true;
|
||||
|
@ -5815,7 +5817,7 @@ GSTexture* FullscreenUI::GetCoverForCurrentGame()
|
|||
{
|
||||
auto lock = GameList::GetLock();
|
||||
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_game_path.c_str());
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str());
|
||||
if (!entry)
|
||||
return s_fallback_disc_texture.get();
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace FullscreenUI
|
|||
void CheckForConfigChanges(const Pcsx2Config& old_config);
|
||||
void OnVMStarted();
|
||||
void OnVMDestroyed();
|
||||
void GameChanged(std::string path, std::string serial, std::string title, u32 crc);
|
||||
void GameChanged(std::string title, std::string path, std::string serial, u32 disc_crc, u32 crc);
|
||||
void OpenPauseMenu();
|
||||
void OpenAchievementsWindow();
|
||||
void OpenLeaderboardsWindow();
|
||||
|
|
|
@ -513,30 +513,22 @@ static void intCancelInstruction()
|
|||
|
||||
static void intExecute()
|
||||
{
|
||||
enum ExecuteState {
|
||||
RESET,
|
||||
GAME_LOADING,
|
||||
GAME_RUNNING
|
||||
};
|
||||
ExecuteState state = RESET;
|
||||
|
||||
// This will come back as zero the first time it runs, or on instruction cancel.
|
||||
// It will come back as nonzero when we exit execution.
|
||||
if (fastjmp_set(&intJmpBuf) != 0)
|
||||
return;
|
||||
|
||||
// I hope this doesn't cause issues with the optimizer... infinite loop with a constant expression.
|
||||
for (;;)
|
||||
{
|
||||
// The execution was splited in three parts so it is easier to
|
||||
// resume it after a cancelled instruction.
|
||||
switch (state) {
|
||||
case RESET:
|
||||
if (!VMManager::Internal::HasBootedELF())
|
||||
{
|
||||
// Avoid reloading every instruction.
|
||||
u32 elf_entry_point = VMManager::Internal::GetCurrentELFEntryPoint();
|
||||
u32 eeload_main = g_eeloadMain;
|
||||
|
||||
while (true)
|
||||
{
|
||||
do
|
||||
{
|
||||
execI();
|
||||
} while (cpuRegs.pc != (g_eeloadMain ? g_eeloadMain : EELOAD_START));
|
||||
execI();
|
||||
|
||||
if (cpuRegs.pc == EELOAD_START)
|
||||
{
|
||||
|
@ -544,11 +536,13 @@ static void intExecute()
|
|||
u32 mainjump = memRead32(EELOAD_START + 0x9c);
|
||||
if (mainjump >> 26 == 3) // JAL
|
||||
g_eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU);
|
||||
|
||||
eeload_main = g_eeloadMain;
|
||||
}
|
||||
else if (cpuRegs.pc == g_eeloadMain)
|
||||
{
|
||||
eeloadHook();
|
||||
if (g_SkipBiosHack)
|
||||
if (VMManager::Internal::IsFastBootInProgress())
|
||||
{
|
||||
// See comments on this code in iR5900-32.cpp's recRecompile()
|
||||
u32 typeAexecjump = memRead32(EELOAD_START + 0x470);
|
||||
|
@ -562,39 +556,24 @@ static void intExecute()
|
|||
else
|
||||
Console.WriteLn("intExecute: Could not enable launch arguments for fast boot mode; unidentified BIOS version! Please report this to the PCSX2 developers.");
|
||||
}
|
||||
|
||||
elf_entry_point = VMManager::Internal::GetCurrentELFEntryPoint();
|
||||
}
|
||||
else if (cpuRegs.pc == g_eeloadExec)
|
||||
{
|
||||
eeloadHook2();
|
||||
}
|
||||
|
||||
if (!g_GameLoading)
|
||||
break;
|
||||
|
||||
state = GAME_LOADING;
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case GAME_LOADING:
|
||||
{
|
||||
if (ElfEntry != 0xFFFFFFFF)
|
||||
else if (cpuRegs.pc == elf_entry_point)
|
||||
{
|
||||
do
|
||||
{
|
||||
execI();
|
||||
} while (cpuRegs.pc != ElfEntry);
|
||||
eeGameStarting();
|
||||
VMManager::Internal::EntryPointCompilingOnCPUThread();
|
||||
break;
|
||||
}
|
||||
state = GAME_RUNNING;
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case GAME_RUNNING:
|
||||
{
|
||||
while (true)
|
||||
execI();
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
execI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "R5900.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "x86/iR3000A.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
|
@ -170,6 +171,17 @@ namespace R3000A
|
|||
else if (!hostRoot.empty()) // relative paths
|
||||
new_path = Path::Combine(hostRoot, native_path);
|
||||
|
||||
// Allow opening the ELF override.
|
||||
if (new_path == VMManager::Internal::GetELFOverride())
|
||||
return new_path;
|
||||
|
||||
// Allow nothing if hostfs isn't enabled.
|
||||
if (!EmuConfig.HostFs)
|
||||
{
|
||||
new_path.clear();
|
||||
return new_path;
|
||||
}
|
||||
|
||||
// Double-check that it falls within the directory of the elf.
|
||||
// Not a real sandbox, but emulators shouldn't be treated as such. Don't run untrusted code!
|
||||
std::string canonicalized_path(Path::Canonicalize(new_path));
|
||||
|
@ -562,7 +574,7 @@ namespace R3000A
|
|||
if (not_number_pos == std::string::npos)
|
||||
return false;
|
||||
|
||||
return ((!g_GameStarted || EmuConfig.HostFs) && 0 == path.compare(0, 4, "host") && path[not_number_pos] == ':');
|
||||
return (path.compare(0, 4, "host") == 0 && path[not_number_pos] == ':');
|
||||
}
|
||||
|
||||
int open_HLE()
|
||||
|
|
|
@ -268,7 +268,7 @@ bool SysMtgsThread::TryOpenGS()
|
|||
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
|
||||
return false;
|
||||
|
||||
GSSetGameCRC(ElfCRC);
|
||||
GSSetGameCRC(VMManager::GetDiscCRC());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,8 +48,6 @@ BIOS
|
|||
|
||||
#include "common/AlignedMalloc.h"
|
||||
|
||||
#include "GSDumpReplayer.h"
|
||||
|
||||
#ifdef ENABLECACHE
|
||||
#include "Cache.h"
|
||||
#endif
|
||||
|
@ -841,11 +839,7 @@ void eeMemoryReserve::Reset()
|
|||
vtlb_VMap(0x00000000,0x00000000,0x20000000);
|
||||
vtlb_VMapUnmap(0x20000000,0x60000000);
|
||||
|
||||
const bool needs_bios = !GSDumpReplayer::IsReplayingDump();
|
||||
|
||||
// TODO(Stenzek): Move BIOS loading out and far away...
|
||||
if (needs_bios && !LoadBIOS())
|
||||
pxFailRel("Failed to load BIOS");
|
||||
CopyBIOSToMemory();
|
||||
}
|
||||
|
||||
void eeMemoryReserve::Release()
|
||||
|
|
|
@ -437,7 +437,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
|
|||
{
|
||||
if (!VMManager::HasValidVM())
|
||||
goto error;
|
||||
const std::string gameName = VMManager::GetGameName();
|
||||
const std::string gameName = VMManager::GetTitle();
|
||||
const u32 size = gameName.size() + 1;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
|
@ -451,7 +451,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
|
|||
{
|
||||
if (!VMManager::HasValidVM())
|
||||
goto error;
|
||||
const std::string gameSerial = VMManager::GetGameSerial();
|
||||
const std::string gameSerial = VMManager::GetDiscSerial();
|
||||
const u32 size = gameSerial.size() + 1;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
|
@ -465,7 +465,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
|
|||
{
|
||||
if (!VMManager::HasValidVM())
|
||||
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;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
|
@ -479,6 +479,8 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(gsl::span<u8> buf, std::vector<u8
|
|||
{
|
||||
if (!VMManager::HasValidVM())
|
||||
goto error;
|
||||
|
||||
const std::string ElfVersion = VMManager::GetDiscVersion();
|
||||
const u32 size = ElfVersion.size() + 1;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
|
|
|
@ -187,7 +187,6 @@ namespace Patch
|
|||
static EnablePatchList s_enabled_cheats;
|
||||
static EnablePatchList s_enabled_patches;
|
||||
static u32 s_patches_crc;
|
||||
static std::string s_patches_serial;
|
||||
static std::optional<AspectRatioType> s_override_aspect_ratio;
|
||||
static std::optional<GSInterlaceMode> s_override_interlace_mode;
|
||||
|
||||
|
@ -520,18 +519,16 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||
return count;
|
||||
}
|
||||
|
||||
void Patch::ReloadPatches(std::string serial, u32 crc, bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed)
|
||||
void Patch::ReloadPatches(const std::string& serial, u32 crc, bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed)
|
||||
{
|
||||
const bool serial_changed = (s_patches_serial != serial);
|
||||
reload_files |= (s_patches_crc != crc);
|
||||
s_patches_crc = crc;
|
||||
s_patches_serial = std::move(serial);
|
||||
|
||||
// Skip reloading gamedb patches if the serial hasn't changed.
|
||||
if (serial_changed)
|
||||
if (reload_files)
|
||||
{
|
||||
s_gamedb_patches.clear();
|
||||
|
||||
const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_patches_serial);
|
||||
const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(serial);
|
||||
if (game)
|
||||
{
|
||||
const std::string* patches = game->findPatch(crc);
|
||||
|
@ -544,18 +541,10 @@ void Patch::ReloadPatches(std::string serial, u32 crc, bool force_reload_files,
|
|||
|
||||
LoadDynamicPatches(game->dynaPatches);
|
||||
}
|
||||
}
|
||||
|
||||
ReloadPatches(serial_changed, reload_enabled_list, verbose, verbose_if_changed);
|
||||
}
|
||||
|
||||
void Patch::ReloadPatches(bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed)
|
||||
{
|
||||
if (force_reload_files)
|
||||
{
|
||||
s_game_patches.clear();
|
||||
EnumeratePnachFiles(
|
||||
s_patches_serial, s_patches_crc, false, false, [](const std::string& filename, const std::string& pnach_data) {
|
||||
serial, s_patches_crc, false, false, [](const std::string& filename, const std::string& pnach_data) {
|
||||
const u32 patch_count = LoadPatchesFromString(&s_game_patches, pnach_data);
|
||||
if (patch_count > 0)
|
||||
Console.WriteLn(Color_Green, fmt::format("Found {} game patches in {}.", patch_count, filename));
|
||||
|
@ -563,7 +552,7 @@ void Patch::ReloadPatches(bool force_reload_files, bool reload_enabled_list, boo
|
|||
|
||||
s_cheat_patches.clear();
|
||||
EnumeratePnachFiles(
|
||||
s_patches_serial, s_patches_crc, true, false, [](const std::string& filename, const std::string& pnach_data) {
|
||||
serial, s_patches_crc, true, false, [](const std::string& filename, const std::string& pnach_data) {
|
||||
const u32 patch_count = LoadPatchesFromString(&s_cheat_patches, pnach_data);
|
||||
if (patch_count > 0)
|
||||
Console.WriteLn(Color_Green, fmt::format("Found {} cheats in {}.", patch_count, filename));
|
||||
|
@ -584,9 +573,10 @@ void Patch::UpdateActivePatches(bool reload_enabled_list, bool verbose, bool ver
|
|||
s_override_interlace_mode.reset();
|
||||
|
||||
std::string message;
|
||||
u32 gp_count = 0;
|
||||
if (EmuConfig.EnablePatches)
|
||||
{
|
||||
const u32 gp_count = EnablePatches(s_gamedb_patches, EnablePatchList());
|
||||
gp_count = EnablePatches(s_gamedb_patches, EnablePatchList());
|
||||
if (gp_count > 0)
|
||||
fmt::format_to(std::back_inserter(message), "{} GameDB patches", gp_count);
|
||||
}
|
||||
|
@ -600,7 +590,9 @@ void Patch::UpdateActivePatches(bool reload_enabled_list, bool verbose, bool ver
|
|||
fmt::format_to(std::back_inserter(message), "{}{} cheat patches", message.empty() ? "" : ", ", c_count);
|
||||
|
||||
// Display message on first boot when we load patches.
|
||||
if (verbose || (verbose_if_changed && prev_count != s_active_patches.size()))
|
||||
// Except when it's just GameDB.
|
||||
const bool just_gamedb = (p_count == 0 && c_count == 0 && gp_count > 0);
|
||||
if (verbose || (verbose_if_changed && prev_count != s_active_patches.size() && !just_gamedb))
|
||||
{
|
||||
if (!message.empty())
|
||||
{
|
||||
|
@ -673,7 +665,6 @@ void Patch::UnloadPatches()
|
|||
s_override_interlace_mode = {};
|
||||
s_override_aspect_ratio = {};
|
||||
s_patches_crc = 0;
|
||||
s_patches_serial = {};
|
||||
s_active_patches = {};
|
||||
s_active_dynamic_patches = {};
|
||||
s_enabled_patches = {};
|
||||
|
|
|
@ -96,8 +96,7 @@ namespace Patch
|
|||
extern PatchInfoList GetPatchInfo(const std::string& serial, u32 crc, bool cheats, u32* num_unlabelled_patches);
|
||||
|
||||
/// Reloads cheats/patches. If verbose is set, the number of patches loaded will be shown in the OSD.
|
||||
extern void ReloadPatches(std::string serial, u32 crc, bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed);
|
||||
extern void ReloadPatches(bool force_reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed);
|
||||
extern void ReloadPatches(const std::string& serial, u32 crc, bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed);
|
||||
|
||||
extern void UpdateActivePatches(bool reload_enabled_list, bool verbose, bool verbose_if_changed);
|
||||
extern void ApplyPatchSettingOverrides();
|
||||
|
|
|
@ -1305,6 +1305,8 @@ Pcsx2Config::Pcsx2Config()
|
|||
McdEnableEjection = true;
|
||||
McdFolderAutoManage = true;
|
||||
EnablePatches = true;
|
||||
EnableFastBoot = true;
|
||||
EnablePerGameSettings = true;
|
||||
EnableRecordingTools = true;
|
||||
EnableGameFixes = true;
|
||||
InhibitScreensaver = true;
|
||||
|
@ -1348,6 +1350,9 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
|
|||
SettingsWrapBitBool(EnablePINE);
|
||||
SettingsWrapBitBool(EnableWideScreenPatches);
|
||||
SettingsWrapBitBool(EnableNoInterlacingPatches);
|
||||
SettingsWrapBitBool(EnableFastBoot);
|
||||
SettingsWrapBitBool(EnableFastBootFastForward);
|
||||
SettingsWrapBitBool(EnablePerGameSettings);
|
||||
SettingsWrapBitBool(EnableRecordingTools);
|
||||
SettingsWrapBitBool(EnableGameFixes);
|
||||
SettingsWrapBitBool(SaveStateOnShutdown);
|
||||
|
@ -1472,7 +1477,6 @@ bool Pcsx2Config::operator==(const Pcsx2Config& right) const
|
|||
void Pcsx2Config::CopyRuntimeConfig(Pcsx2Config& cfg)
|
||||
{
|
||||
GS.LimitScalar = cfg.GS.LimitScalar;
|
||||
UseBOOT2Injection = cfg.UseBOOT2Injection;
|
||||
CurrentBlockdump = std::move(cfg.CurrentBlockdump);
|
||||
CurrentIRX = std::move(cfg.CurrentIRX);
|
||||
CurrentGameArgs = std::move(cfg.CurrentGameArgs);
|
||||
|
|
|
@ -51,10 +51,6 @@ alignas(16) fpuRegisters fpuRegs;
|
|||
alignas(16) tlbs tlb[48];
|
||||
R5900cpu *Cpu = NULL;
|
||||
|
||||
bool g_SkipBiosHack; // set at boot if the skip bios hack is on, reset before the game has started
|
||||
bool g_GameStarted; // set when we reach the game's entry point or earlier if the entry point cannot be determined
|
||||
bool g_GameLoading; // EELOAD has been called to load the game
|
||||
|
||||
static const uint eeWaitCycles = 3072;
|
||||
|
||||
bool eeEventTestIsActive = false;
|
||||
|
@ -104,24 +100,12 @@ void cpuReset()
|
|||
extern void Deci2Reset(); // lazy, no good header for it yet.
|
||||
Deci2Reset();
|
||||
|
||||
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
|
||||
AllowParams1 = !g_SkipBiosHack;
|
||||
AllowParams2 = !g_SkipBiosHack;
|
||||
AllowParams1 = !VMManager::Internal::IsFastBootInProgress();
|
||||
AllowParams2 = !VMManager::Internal::IsFastBootInProgress();
|
||||
|
||||
ElfCRC = 0;
|
||||
DiscSerial.clear();
|
||||
ElfEntry = -1;
|
||||
g_GameStarted = false;
|
||||
g_GameLoading = false;
|
||||
|
||||
// FIXME: LastELF should be reset on media changes as well as on CPU resets, in
|
||||
// the very unlikely case that a user swaps to another media source that "looks"
|
||||
// the same (identical ELF names) but is actually different (devs actually could
|
||||
// run into this while testing minor binary hacked changes to ISO images, which
|
||||
// is why I found out about this) --air
|
||||
LastELF.clear();
|
||||
|
||||
g_eeloadMain = 0, g_eeloadExec = 0, g_osdsys_str = 0;
|
||||
g_eeloadMain = 0;
|
||||
g_eeloadExec = 0;
|
||||
g_osdsys_str = 0;
|
||||
}
|
||||
|
||||
__ri void cpuException(u32 code, u32 bd)
|
||||
|
@ -279,7 +263,7 @@ static __fi void TESTINT( u8 n, void (*callback)() )
|
|||
{
|
||||
if( !(cpuRegs.interrupt & (1 << n)) ) return;
|
||||
|
||||
if(!g_GameStarted || CHECK_INSTANTDMAHACK || cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) )
|
||||
if(CHECK_INSTANTDMAHACK || cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) )
|
||||
{
|
||||
cpuClearInt( n );
|
||||
callback();
|
||||
|
@ -416,7 +400,7 @@ __fi void _cpuEventTest_Shared()
|
|||
// where a DMA buffer is overwritten without waiting for the transfer to end, which causes the fonts to get all messed up
|
||||
// so to fix it, we run all the DMA's instantly when in the BIOS.
|
||||
// Only use the lower 17 bits of the cpuRegs.interrupt as the upper bits are for VU0/1 sync which can't be done in a tight loop
|
||||
if ((!g_GameStarted || CHECK_INSTANTDMAHACK) && dmacRegs.ctrl.DMAE && !(psHu8(DMAC_ENABLER + 2) & 1) && (cpuRegs.interrupt & 0x1FFFF))
|
||||
if (CHECK_INSTANTDMAHACK && dmacRegs.ctrl.DMAE && !(psHu8(DMAC_ENABLER + 2) & 1) && (cpuRegs.interrupt & 0x1FFFF))
|
||||
{
|
||||
while ((cpuRegs.interrupt & 0x1FFFF) && _cpuTestInterrupts())
|
||||
;
|
||||
|
@ -568,27 +552,6 @@ __fi void CPU_INT( EE_EventType n, s32 ecycle)
|
|||
cpuSetNextEventDelta(cpuRegs.eCycle[n]);
|
||||
}
|
||||
|
||||
// Called from recompilers; define is mandatory.
|
||||
void eeGameStarting()
|
||||
{
|
||||
if (!g_GameStarted)
|
||||
{
|
||||
//Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry );
|
||||
g_GameStarted = true;
|
||||
g_GameLoading = false;
|
||||
|
||||
// GameStartingInThread may issue a reset of the cpu and/or recompilers. Check for and
|
||||
// handle such things here:
|
||||
VMManager::Internal::GameStartingOnCPUThread();
|
||||
if (VMManager::Internal::IsExecutionInterrupted())
|
||||
Cpu->ExitExecution();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn( Color_Green, "(R5900) Re-executed ELF Entry point (ignored) [addr=0x%08X]", ElfEntry );
|
||||
}
|
||||
}
|
||||
|
||||
// Count arguments, save their starting locations, and replace the space separators with null terminators so they're separate strings
|
||||
int ParseArgumentString(u32 arg_block)
|
||||
{
|
||||
|
@ -635,16 +598,6 @@ int ParseArgumentString(u32 arg_block)
|
|||
// Called from recompilers; define is mandatory.
|
||||
void eeloadHook()
|
||||
{
|
||||
const std::string& elf_override(VMManager::Internal::GetElfOverride());
|
||||
|
||||
if (!elf_override.empty())
|
||||
cdvdReloadElfInfo(StringUtil::StdStringFromFormat("host:%s", elf_override.c_str()));
|
||||
else
|
||||
cdvdReloadElfInfo();
|
||||
|
||||
std::string discelf;
|
||||
int disctype = GetPS2ElfName(discelf);
|
||||
|
||||
std::string elfname;
|
||||
int argc = cpuRegs.GPR.n.a0.SD[0];
|
||||
if (argc) // calls to EELOAD *after* the first one during the startup process will come here
|
||||
|
@ -664,7 +617,7 @@ void eeloadHook()
|
|||
// mode). Then EELOAD is called with the argument "rom0:PS2LOGO". At this point, we do not need any additional tricks
|
||||
// because EELOAD is now ready to accept launch arguments. So in full-boot mode, we simply wait for PS2LOGO to be called,
|
||||
// then we add the desired launch arguments. PS2LOGO passes those on to the game itself as it calls EELOAD a third time.
|
||||
if (!EmuConfig.CurrentGameArgs.empty() && !strcmp(elfname.c_str(), "rom0:PS2LOGO"))
|
||||
if (!EmuConfig.CurrentGameArgs.empty() && elfname == "rom0:PS2LOGO")
|
||||
{
|
||||
const char *argString = EmuConfig.CurrentGameArgs.c_str();
|
||||
Console.WriteLn("eeloadHook: Supplying launch argument(s) '%s' to module '%s'...", argString, elfname.c_str());
|
||||
|
@ -711,24 +664,32 @@ void eeloadHook()
|
|||
|
||||
// If "fast boot" was chosen, then on EELOAD's first call we won't yet know what the game's ELF is. Find the name and write it
|
||||
// into EELOAD's memory.
|
||||
if (g_SkipBiosHack && elfname.empty())
|
||||
if (VMManager::Internal::IsFastBootInProgress() && elfname.empty())
|
||||
{
|
||||
std::string elftoload;
|
||||
const std::string& elf_override = VMManager::Internal::GetELFOverride();
|
||||
if (!elf_override.empty())
|
||||
{
|
||||
elftoload = StringUtil::StdStringFromFormat("host:%s", elf_override.c_str());
|
||||
elfname = fmt::format("host:{}", elf_override);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (disctype == 2)
|
||||
elftoload = discelf;
|
||||
CDVDDiscType disc_type;
|
||||
std::string disc_elf;
|
||||
cdvdGetDiscInfo(nullptr, &disc_elf, nullptr, nullptr, &disc_type);
|
||||
if (disc_type == CDVDDiscType::PS2Disc)
|
||||
{
|
||||
// only allow fast boot for PS2 games
|
||||
elfname = std::move(disc_elf);
|
||||
}
|
||||
else
|
||||
g_SkipBiosHack = false; // We're not fast booting, so disable it (Fixes some weirdness with the BIOS)
|
||||
{
|
||||
Console.Warning(fmt::format("Not allowing fast boot for non-PS2 ELF {}", disc_elf));
|
||||
}
|
||||
}
|
||||
|
||||
// When fast-booting, we insert the game's ELF name into EELOAD so that the game is called instead of the default call of
|
||||
// "rom0:OSDSYS"; any launch arguments supplied by the user will be inserted into EELOAD later by eeloadHook2()
|
||||
if (!elftoload.empty())
|
||||
if (!elfname.empty())
|
||||
{
|
||||
// Find and save location of default/fallback call "rom0:OSDSYS"; to be used later by eeloadHook2()
|
||||
for (g_osdsys_str = EELOAD_START; g_osdsys_str < EELOAD_START + EELOAD_SIZE; g_osdsys_str += 8) // strings are 64-bit aligned
|
||||
|
@ -736,16 +697,20 @@ void eeloadHook()
|
|||
if (!strcmp((char*)PSM(g_osdsys_str), "rom0:OSDSYS"))
|
||||
{
|
||||
// Overwrite OSDSYS with game's ELF name
|
||||
strcpy((char*)PSM(g_osdsys_str), elftoload.c_str());
|
||||
g_GameLoading = true;
|
||||
return;
|
||||
strcpy((char*)PSM(g_osdsys_str), elfname.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stop fast forwarding if we're doing that for boot.
|
||||
VMManager::Internal::DisableFastBoot();
|
||||
AllowParams1 = true;
|
||||
AllowParams2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_GameStarted && ((disctype == 2 && elfname == discelf) || disctype == 1))
|
||||
g_GameLoading = true;
|
||||
VMManager::Internal::ELFLoadingOnCPUThread(std::move(elfname));
|
||||
}
|
||||
|
||||
// Called from recompilers; define is mandatory.
|
||||
|
|
|
@ -26,10 +26,6 @@ class BaseR5900Exception;
|
|||
// them in iR5900.h would mean having to include that into more files than I care to
|
||||
// right now, so we're sticking them here for now until a better solution comes along.
|
||||
|
||||
extern bool g_SkipBiosHack;
|
||||
extern bool g_GameStarted;
|
||||
extern bool g_GameLoading;
|
||||
|
||||
namespace Exception
|
||||
{
|
||||
// Implementation Note: this exception has no meaningful type information and we don't
|
||||
|
@ -276,7 +272,6 @@ const u32 EELOAD_START = 0x82000;
|
|||
const u32 EELOAD_SIZE = 0x20000; // overestimate for searching
|
||||
extern u32 g_eeloadMain, g_eeloadExec;
|
||||
|
||||
extern void eeGameStarting();
|
||||
extern void eeloadHook();
|
||||
extern void eeloadHook2();
|
||||
|
||||
|
|
|
@ -969,7 +969,7 @@ void SYSCALL()
|
|||
AllowParams1 = true;
|
||||
break;
|
||||
case Syscall::GetOsdConfigParam:
|
||||
if(!NoOSD && g_SkipBiosHack && !AllowParams1)
|
||||
if(!NoOSD && !AllowParams1)
|
||||
{
|
||||
u32 memaddr = cpuRegs.GPR.n.a0.UL[0];
|
||||
u8 params[16];
|
||||
|
@ -993,7 +993,7 @@ void SYSCALL()
|
|||
AllowParams2 = true;
|
||||
break;
|
||||
case Syscall::GetOsdConfigParam2:
|
||||
if (!NoOSD && g_SkipBiosHack && !AllowParams2)
|
||||
if (!NoOSD && !AllowParams2)
|
||||
{
|
||||
u32 memaddr = cpuRegs.GPR.n.a0.UL[0];
|
||||
u8 params[16];
|
||||
|
|
|
@ -78,7 +78,7 @@ bool InputRecording::create(const std::string& fileName, const bool fromSaveStat
|
|||
|
||||
m_file.setEmulatorVersion();
|
||||
m_file.setAuthor(authorName);
|
||||
m_file.setGameName(resolveGameName());
|
||||
m_file.setGameName(VMManager::GetTitle());
|
||||
m_file.writeHeader();
|
||||
initializeState();
|
||||
InputRec::log("Started new input recording");
|
||||
|
@ -129,9 +129,9 @@ bool InputRecording::play(const std::string& filename)
|
|||
initializeState();
|
||||
InputRec::log("Replaying input recording");
|
||||
m_file.logRecordingMetadata();
|
||||
if (resolveGameName() != m_file.getGameName())
|
||||
if (VMManager::GetTitle() != m_file.getGameName())
|
||||
{
|
||||
InputRec::consoleLog(fmt::format("Input recording was possibly constructed for a different game. Expected: {}, Actual: {}", m_file.getGameName(), resolveGameName()));
|
||||
InputRec::consoleLog(fmt::format("Input recording was possibly constructed for a different game. Expected: {}, Actual: {}", m_file.getGameName(), VMManager::GetTitle()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -229,21 +229,6 @@ void InputRecording::processRecordQueue()
|
|||
}
|
||||
}
|
||||
|
||||
std::string InputRecording::resolveGameName()
|
||||
{
|
||||
std::string gameName;
|
||||
const std::string gameKey = SysGetDiscID();
|
||||
if (!gameKey.empty())
|
||||
{
|
||||
auto game = GameDatabase::findGame(gameKey);
|
||||
if (game)
|
||||
{
|
||||
gameName = fmt::format("{} ({})", game->name, game->region);
|
||||
}
|
||||
}
|
||||
return !gameName.empty() ? gameName : VMManager::GetGameName();
|
||||
}
|
||||
|
||||
void InputRecording::incFrameCounter()
|
||||
{
|
||||
if (!m_is_active)
|
||||
|
|
|
@ -70,11 +70,6 @@ private:
|
|||
void initializeState();
|
||||
void setStartingFrame(u32 startingFrame);
|
||||
void closeActiveFile();
|
||||
|
||||
private:
|
||||
// Resolve the name and region of the game currently loaded using the GameDB
|
||||
// If the game cannot be found in the DB, the fallback is the ISO filename
|
||||
std::string resolveGameName();
|
||||
};
|
||||
|
||||
extern InputRecording g_InputRecording;
|
||||
|
|
|
@ -174,11 +174,11 @@ SaveStateBase& SaveStateBase::FreezeBios()
|
|||
|
||||
SaveStateBase& SaveStateBase::FreezeInternals()
|
||||
{
|
||||
const u32 previousCRC = ElfCRC;
|
||||
|
||||
// Print this until the MTVU problem in gifPathFreeze is taken care of (rama)
|
||||
if (THREAD_VU1) Console.Warning("MTVU speedhack is enabled, saved states may not be stable");
|
||||
|
||||
vmFreeze();
|
||||
|
||||
// Second Block - Various CPU Registers and States
|
||||
// -----------------------------------------------
|
||||
FreezeTag( "cpuRegs" );
|
||||
|
@ -188,30 +188,7 @@ SaveStateBase& SaveStateBase::FreezeInternals()
|
|||
Freeze(tlb); // tlbs
|
||||
Freeze(AllowParams1); //OSDConfig written (Fast Boot)
|
||||
Freeze(AllowParams2);
|
||||
Freeze(g_GameStarted);
|
||||
Freeze(g_GameLoading);
|
||||
Freeze(ElfCRC);
|
||||
|
||||
char localDiscSerial[256];
|
||||
StringUtil::Strlcpy(localDiscSerial, DiscSerial.c_str(), sizeof(localDiscSerial));
|
||||
Freeze(localDiscSerial);
|
||||
if (IsLoading())
|
||||
{
|
||||
DiscSerial = localDiscSerial;
|
||||
|
||||
if (ElfCRC != previousCRC)
|
||||
{
|
||||
// HACK: LastELF isn't in the save state... Load it before we go too far into restoring state.
|
||||
// When we next bump save states, we should include it. We need this for achievements, because
|
||||
// we want to load and activate achievements before restoring any of their tracked state.
|
||||
if (const std::string& elf_override = VMManager::Internal::GetElfOverride(); !elf_override.empty())
|
||||
cdvdReloadElfInfo(fmt::format("host:{}", elf_override));
|
||||
else
|
||||
cdvdReloadElfInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Third Block - Cycle Timers and Events
|
||||
// -------------------------------------
|
||||
FreezeTag( "Cycles" );
|
||||
|
|
|
@ -36,7 +36,7 @@ enum class FreezeAction
|
|||
// [SAVEVERSION+]
|
||||
// This informs the auto updater that the users savestates will be invalidated.
|
||||
|
||||
static const u32 g_SaveVersion = (0x9A36 << 16) | 0x0000;
|
||||
static const u32 g_SaveVersion = (0x9A37 << 16) | 0x0000;
|
||||
|
||||
|
||||
// the freezing data between submodules and core
|
||||
|
@ -147,6 +147,18 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void FreezeString(std::string& s)
|
||||
{
|
||||
// overwritten when loading
|
||||
u32 length = static_cast<u32>(s.length());
|
||||
Freeze(length);
|
||||
|
||||
if (IsLoading())
|
||||
s.resize(length);
|
||||
|
||||
FreezeMem(s.data(), length);
|
||||
}
|
||||
|
||||
uint GetCurrentPos() const
|
||||
{
|
||||
return m_idx;
|
||||
|
@ -189,8 +201,7 @@ public:
|
|||
protected:
|
||||
void Init( VmStateBuffer* memblock );
|
||||
|
||||
// Load/Save functions for the various components of our glorious emulator!
|
||||
|
||||
void vmFreeze();
|
||||
void mtvuFreeze();
|
||||
void rcntFreeze();
|
||||
void vuMicroFreeze();
|
||||
|
|
|
@ -346,34 +346,3 @@ void SysClearExecutionCache()
|
|||
dVifReset(1);
|
||||
}
|
||||
}
|
||||
|
||||
// This function returns part of EXTINFO data of the BIOS rom
|
||||
// This module contains information about Sony build environment at offst 0x10
|
||||
// first 15 symbols is build date/time that is unique per rom and can be used as unique serial
|
||||
// Example for romver 0160EC20010704
|
||||
// 20010704-160707,ROMconf,PS20160EC20010704.bin,kuma@rom-server/~/f10k/g/app/rom
|
||||
// 20010704-160707 can be used as unique ID for Bios
|
||||
std::string SysGetBiosDiscID()
|
||||
{
|
||||
if (!BiosSerial.empty())
|
||||
return BiosSerial;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
// This function always returns a valid DiscID -- using the Sony serial when possible, and
|
||||
// falling back on the CRC checksum of the ELF binary if the PS2 software being run is
|
||||
// homebrew or some other serial-less item.
|
||||
std::string SysGetDiscID()
|
||||
{
|
||||
if (!DiscSerial.empty())
|
||||
return DiscSerial;
|
||||
|
||||
if (!ElfCRC)
|
||||
{
|
||||
// system is currently running the BIOS
|
||||
return SysGetBiosDiscID();
|
||||
}
|
||||
|
||||
return StringUtil::StdStringFromFormat("%08x", ElfCRC);
|
||||
}
|
||||
|
|
|
@ -144,9 +144,6 @@ extern SysCpuProviderPack& GetCpuProviders();
|
|||
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
|
||||
extern void SysClearExecutionCache(); // clears recompiled execution caches!
|
||||
|
||||
extern std::string SysGetBiosDiscID();
|
||||
extern std::string SysGetDiscID();
|
||||
|
||||
extern SysMainMemory& GetVmMemory();
|
||||
|
||||
extern void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR);
|
||||
|
|
|
@ -332,7 +332,7 @@ namespace usb_lightgun
|
|||
|
||||
void GunCon2State::AutoConfigure()
|
||||
{
|
||||
const std::string serial(VMManager::GetGameSerial());
|
||||
const std::string serial = VMManager::GetDiscSerial();
|
||||
for (const GameConfig& gc : s_game_config)
|
||||
{
|
||||
if (serial != gc.serial)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -74,14 +74,23 @@ namespace VMManager
|
|||
/// Returns the path of the disc currently running.
|
||||
std::string GetDiscPath();
|
||||
|
||||
/// Returns the crc of the executable currently running.
|
||||
u32 GetGameCRC();
|
||||
/// Returns the serial of the disc currently running.
|
||||
std::string GetDiscSerial();
|
||||
|
||||
/// Returns the serial of the disc/executable currently running.
|
||||
std::string GetGameSerial();
|
||||
/// Returns the path of the main ELF of the disc currently running.
|
||||
std::string GetDiscELF();
|
||||
|
||||
/// Returns the name of the disc/executable currently running.
|
||||
std::string GetGameName();
|
||||
std::string GetTitle();
|
||||
|
||||
/// Returns the CRC for the main ELF of the disc currently running.
|
||||
u32 GetDiscCRC();
|
||||
|
||||
/// Returns the version of the disc currently running.
|
||||
std::string GetDiscVersion();
|
||||
|
||||
/// Returns the crc of the executable currently running.
|
||||
u32 GetCurrentCRC();
|
||||
|
||||
/// Loads global settings (i.e. EmuConfig).
|
||||
void LoadSettings();
|
||||
|
@ -107,6 +116,9 @@ namespace VMManager
|
|||
/// Reloads game specific settings, and applys any changes present.
|
||||
bool ReloadGameSettings();
|
||||
|
||||
/// Reloads game patches.
|
||||
void ReloadPatches(bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed);
|
||||
|
||||
/// Returns the save state filename for the given game serial/crc.
|
||||
std::string GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot);
|
||||
|
||||
|
@ -215,11 +227,22 @@ namespace VMManager
|
|||
/// Updates the variables in the EmuFolders namespace, reloading subsystems if needed.
|
||||
void UpdateEmuFolders();
|
||||
|
||||
const std::string& GetElfOverride();
|
||||
/// Returns true if fast booting is active (requested but ELF not started).
|
||||
bool IsFastBootInProgress();
|
||||
|
||||
/// Disables fast boot if it was requested, and found to be incompatible.
|
||||
void DisableFastBoot();
|
||||
|
||||
/// Returns true if the current ELF has started executing.
|
||||
bool HasBootedELF();
|
||||
|
||||
/// Returns the PC of the currently-executing ELF's entry point.
|
||||
u32 GetCurrentELFEntryPoint();
|
||||
|
||||
const std::string& GetELFOverride();
|
||||
bool IsExecutionInterrupted();
|
||||
void ELFLoadingOnCPUThread(std::string elf_path);
|
||||
void EntryPointCompilingOnCPUThread();
|
||||
void GameStartingOnCPUThread();
|
||||
void SwappingGameOnCPUThread();
|
||||
void VSyncOnCPUThread();
|
||||
} // namespace Internal
|
||||
} // namespace VMManager
|
||||
|
@ -262,8 +285,8 @@ namespace Host
|
|||
void OnSaveStateSaved(const std::string_view& filename);
|
||||
|
||||
/// Provided by the host; called when the running executable changes.
|
||||
void OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc);
|
||||
void OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
|
||||
const std::string& disc_serial, u32 disc_crc, u32 current_crc);
|
||||
|
||||
/// Provided by the host; called once per frame at guest vsync.
|
||||
void VSyncOnCPUThread();
|
||||
|
|
|
@ -54,9 +54,11 @@ bool NoOSD;
|
|||
bool AllowParams1;
|
||||
bool AllowParams2;
|
||||
std::string BiosDescription;
|
||||
std::string BiosZone;
|
||||
std::string BiosSerial;
|
||||
std::string BiosPath;
|
||||
BiosDebugInformation CurrentBiosInformation;
|
||||
std::vector<u8> BiosRom;
|
||||
|
||||
static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& description, u32& region, std::string& zone, std::string& serial)
|
||||
{
|
||||
|
@ -177,12 +179,12 @@ static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& descriptio
|
|||
return true;
|
||||
}
|
||||
|
||||
template <size_t _size>
|
||||
void ChecksumIt(u32& result, const u8 (&srcdata)[_size])
|
||||
static void ChecksumIt(u32& result, u32 offset, u32 size)
|
||||
{
|
||||
pxAssume((_size & 3) == 0);
|
||||
for (size_t i = 0; i < _size / 4; ++i)
|
||||
result ^= ((u32*)srcdata)[i];
|
||||
const u8* srcdata = &BiosRom[offset];
|
||||
pxAssume((size & 3) == 0);
|
||||
for (size_t i = 0; i < size / 4; ++i)
|
||||
result ^= reinterpret_cast<const u32*>(srcdata)[i];
|
||||
}
|
||||
|
||||
// Attempts to load a BIOS rom sub-component, by trying multiple combinations of base
|
||||
|
@ -192,8 +194,7 @@ void ChecksumIt(u32& result, const u8 (&srcdata)[_size])
|
|||
// Parameters:
|
||||
// ext - extension of the sub-component to load. Valid options are rom1 and rom2.
|
||||
//
|
||||
template <size_t _size>
|
||||
static void LoadExtraRom(const char* ext, u8 (&dest)[_size])
|
||||
static void LoadExtraRom(const char* ext, u32 offset, u32 size)
|
||||
{
|
||||
// Try first a basic extension concatenation (normally results in something like name.bin.rom1)
|
||||
std::string Bios1(StringUtil::StdStringFromFormat("%s.%s", BiosPath.c_str(), ext));
|
||||
|
@ -210,8 +211,10 @@ static void LoadExtraRom(const char* ext, u8 (&dest)[_size])
|
|||
}
|
||||
}
|
||||
|
||||
BiosRom.resize(offset + size);
|
||||
|
||||
auto fp = FileSystem::OpenManagedCFile(Bios1.c_str(), "rb");
|
||||
if (!fp || std::fread(dest, static_cast<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);
|
||||
return;
|
||||
|
@ -261,6 +264,29 @@ static std::string FindBiosImage()
|
|||
return std::string();
|
||||
}
|
||||
|
||||
bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone)
|
||||
{
|
||||
std::string serial;
|
||||
const auto fp = FileSystem::OpenManagedCFile(filename, "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
// FPS2BIOS is smaller and of variable size
|
||||
//if (inway.Length() < 512*1024) return false;
|
||||
return LoadBiosVersion(fp.get(), version, description, region, zone, serial);
|
||||
}
|
||||
|
||||
bool IsBIOSAvailable(const std::string& full_path)
|
||||
{
|
||||
// We can't use EmuConfig here since it may not be loaded yet.
|
||||
if (!full_path.empty() && FileSystem::FileExists(full_path.c_str()))
|
||||
return true;
|
||||
|
||||
// No bios configured or the configured name is missing, check for one in the BIOS directory.
|
||||
const std::string auto_path(FindBiosImage());
|
||||
return !auto_path.empty() && FileSystem::FileExists(auto_path.c_str());
|
||||
}
|
||||
|
||||
// Loads the configured bios rom file into PS2 memory. PS2 memory must be allocated prior to
|
||||
// this method being called.
|
||||
//
|
||||
|
@ -297,11 +323,12 @@ bool LoadBIOS()
|
|||
if (filesize <= 0)
|
||||
return false;
|
||||
|
||||
std::string zone;
|
||||
LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, zone, BiosSerial);
|
||||
LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, BiosZone, BiosSerial);
|
||||
|
||||
BiosRom.resize(Ps2MemSize::Rom);
|
||||
|
||||
if (FileSystem::FSeek64(fp.get(), 0, SEEK_SET) ||
|
||||
std::fread(eeMem->ROM, static_cast<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;
|
||||
}
|
||||
|
@ -314,40 +341,31 @@ bool LoadBIOS()
|
|||
NoOSD = false;
|
||||
|
||||
BiosChecksum = 0;
|
||||
ChecksumIt(BiosChecksum, eeMem->ROM);
|
||||
ChecksumIt(BiosChecksum, 0, Ps2MemSize::Rom);
|
||||
BiosPath = std::move(path);
|
||||
|
||||
//injectIRX("host.irx"); //not fully tested; still buggy
|
||||
|
||||
LoadExtraRom("rom1", eeMem->ROM1);
|
||||
LoadExtraRom("rom2", eeMem->ROM2);
|
||||
LoadExtraRom("rom1", Ps2MemSize::Rom, Ps2MemSize::Rom1);
|
||||
LoadExtraRom("rom2", Ps2MemSize::Rom + Ps2MemSize::Rom1, Ps2MemSize::Rom2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CopyBIOSToMemory()
|
||||
{
|
||||
if (BiosRom.size() >= Ps2MemSize::Rom)
|
||||
{
|
||||
std::memcpy(eeMem->ROM, BiosRom.data(), sizeof(eeMem->ROM));
|
||||
if (BiosRom.size() >= (Ps2MemSize::Rom + Ps2MemSize::Rom1))
|
||||
{
|
||||
std::memcpy(eeMem->ROM1, BiosRom.data() + Ps2MemSize::Rom, sizeof(eeMem->ROM1));
|
||||
if (BiosRom.size() >= (Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2))
|
||||
std::memcpy(eeMem->ROM2, BiosRom.data() + Ps2MemSize::Rom + Ps2MemSize::Rom1, sizeof(eeMem->ROM2));
|
||||
}
|
||||
}
|
||||
|
||||
if (EmuConfig.CurrentIRX.length() > 3)
|
||||
LoadIrx(EmuConfig.CurrentIRX, &eeMem->ROM[0x3C0000], sizeof(eeMem->ROM) - 0x3C0000);
|
||||
|
||||
CurrentBiosInformation.eeThreadListAddr = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone)
|
||||
{
|
||||
std::string serial;
|
||||
const auto fp = FileSystem::OpenManagedCFile(filename, "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
// FPS2BIOS is smaller and of variable size
|
||||
//if (inway.Length() < 512*1024) return false;
|
||||
return LoadBiosVersion(fp.get(), version, description, region, zone, serial);
|
||||
}
|
||||
|
||||
bool IsBIOSAvailable(const std::string& full_path)
|
||||
{
|
||||
// We can't use EmuConfig here since it may not be loaded yet.
|
||||
if (!full_path.empty() && FileSystem::FileExists(full_path.c_str()))
|
||||
return true;
|
||||
|
||||
// No bios configured or the configured name is missing, check for one in the BIOS directory.
|
||||
const std::string auto_path(FindBiosImage());
|
||||
return !auto_path.empty() && FileSystem::FileExists(auto_path.c_str());
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
|
@ -14,7 +14,9 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
const u32 ThreadListInstructions[3] =
|
||||
{
|
||||
|
@ -30,6 +32,7 @@ struct BiosDebugInformation
|
|||
u32 iopModListAddr;
|
||||
};
|
||||
|
||||
// TODO: namespace this
|
||||
extern BiosDebugInformation CurrentBiosInformation;
|
||||
extern u32 BiosVersion; // Used by CDVD
|
||||
extern u32 BiosRegion; // Used by CDVD
|
||||
|
@ -38,9 +41,23 @@ extern bool AllowParams1;
|
|||
extern bool AllowParams2;
|
||||
extern u32 BiosChecksum;
|
||||
extern std::string BiosDescription;
|
||||
extern std::string BiosZone;
|
||||
|
||||
// This function returns part of EXTINFO data of the BIOS rom
|
||||
// This module contains information about Sony build environment at offst 0x10
|
||||
// first 15 symbols is build date/time that is unique per rom and can be used as unique serial
|
||||
// Example for romver 0160EC20010704
|
||||
// 20010704-160707,ROMconf,PS20160EC20010704.bin,kuma@rom-server/~/f10k/g/app/rom
|
||||
// 20010704-160707 can be used as unique ID for Bios
|
||||
extern std::string BiosSerial;
|
||||
extern std::string BiosPath;
|
||||
extern bool LoadBIOS();
|
||||
|
||||
// Copies of the BIOS ROM. Because the EE can write to the ROM area, we need to copy it on reset.
|
||||
// If we ever support read-only physical mappings, we can remove this.
|
||||
extern std::vector<u8> BiosRom;
|
||||
|
||||
extern bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone);
|
||||
extern bool IsBIOSAvailable(const std::string& full_path);
|
||||
|
||||
extern bool LoadBIOS();
|
||||
extern void CopyBIOSToMemory();
|
||||
|
|
|
@ -2220,6 +2220,9 @@ static void recRecompile(const u32 startpc)
|
|||
if (recPtr >= (recMem->GetPtrEnd() - _64kb))
|
||||
eeRecNeedsReset = true;
|
||||
|
||||
if (HWADDR(startpc) == VMManager::Internal::GetCurrentELFEntryPoint())
|
||||
VMManager::Internal::EntryPointCompilingOnCPUThread();
|
||||
|
||||
if (eeRecNeedsReset)
|
||||
{
|
||||
eeRecNeedsReset = false;
|
||||
|
@ -2229,9 +2232,6 @@ static void recRecompile(const u32 startpc)
|
|||
xSetPtr(recPtr);
|
||||
recPtr = xGetAlignedCallTarget();
|
||||
|
||||
if (0x8000d618 == startpc)
|
||||
DbgCon.WriteLn("Compiling block @ 0x%08x", startpc);
|
||||
|
||||
s_pCurBlock = PC_GETBLOCK(startpc);
|
||||
|
||||
pxAssert(s_pCurBlock->GetFnptr() == (uptr)JITCompile || s_pCurBlock->GetFnptr() == (uptr)JITCompileInBlock);
|
||||
|
@ -2254,7 +2254,7 @@ static void recRecompile(const u32 startpc)
|
|||
if (g_eeloadMain && HWADDR(startpc) == HWADDR(g_eeloadMain))
|
||||
{
|
||||
xFastCall((void*)eeloadHook);
|
||||
if (g_SkipBiosHack)
|
||||
if (VMManager::Internal::IsFastBootInProgress())
|
||||
{
|
||||
// There are four known versions of EELOAD, identifiable by the location of the 'jal' to the EELOAD function which
|
||||
// calls ExecPS2(). The function itself is at the same address in all BIOSs after v1.00-v1.10.
|
||||
|
@ -2274,14 +2274,6 @@ static void recRecompile(const u32 startpc)
|
|||
if (g_eeloadExec && HWADDR(startpc) == HWADDR(g_eeloadExec))
|
||||
xFastCall((void*)eeloadHook2);
|
||||
|
||||
// this is the only way patches get applied, doesn't depend on a hack
|
||||
if (g_GameLoading && HWADDR(startpc) == ElfEntry)
|
||||
{
|
||||
Console.WriteLn("Elf entry point @ 0x%08x about to get recompiled. Load patches first.", startpc);
|
||||
xFastCall((void*)eeGameStarting);
|
||||
VMManager::Internal::EntryPointCompilingOnCPUThread();
|
||||
}
|
||||
|
||||
g_branch = 0;
|
||||
|
||||
// reset recomp state variables
|
||||
|
|
|
@ -137,8 +137,8 @@ void Host::OnVMResumed()
|
|||
{
|
||||
}
|
||||
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
void Host::OnGameChanged(const std::string& title, const std::string& elf_override, const std::string& disc_path,
|
||||
const std::string& disc_serial, u32 disc_crc, u32 current_crc)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue