diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 0b3554e30..d2258c460 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -210,7 +210,7 @@ static std::string_view GetFileNameFromPath(const char* path) return std::string_view(filename_start + 1, filename_end - filename_start); } -static std::string_view GetTitleForPath(const char* path) +std::string_view GameList::GetTitleForPath(const char* path) { const char* extension = std::strrchr(path, '.'); if (path == extension) @@ -295,13 +295,11 @@ bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry) } else { - LoadDatabase(); - - auto iter = m_database.find(entry->code); - if (iter != m_database.end()) + const GameListDatabaseEntry* database_entry = GetDatabaseEntryForCode(entry->code); + if (database_entry) { - entry->title = iter->second.title; - entry->region = iter->second.region; + entry->title = database_entry->title; + entry->region = database_entry->region; } else { @@ -620,7 +618,7 @@ public: auto iter = m_database.find(code); if (iter == m_database.end()) { - GameList::GameDatabaseEntry gde; + GameListDatabaseEntry gde; gde.code = std::move(code); gde.region = GameList::GetRegionForCode(gde.code).value_or(ConsoleRegion::NTSC_U); gde.title = name; @@ -641,7 +639,7 @@ public: } private: - GameList::DatabaseMap& m_database; + DatabaseMap& m_database; }; void GameList::AddDirectory(std::string path, bool recursive) @@ -662,13 +660,22 @@ const GameListEntry* GameList::GetEntryForPath(const char* path) const const size_t path_length = std::strlen(path); for (const GameListEntry& entry : m_entries) { - if (entry.path.size() == path_length && StringUtil::Strcasecmp(entry.path.c_str(), path)) + if (entry.path.size() == path_length && StringUtil::Strcasecmp(entry.path.c_str(), path) == 0) return &entry; } return nullptr; } +const GameListDatabaseEntry* GameList::GetDatabaseEntryForCode(const std::string& code) const +{ + if (!m_database_load_tried) + const_cast(this)->LoadDatabase(); + + auto iter = m_database.find(code); + return (iter != m_database.end()) ? &iter->second : nullptr; +} + void GameList::SetPathsFromSettings(SettingsInterface& si) { m_search_directories.clear(); diff --git a/src/core/game_list.h b/src/core/game_list.h index bf7c8ee13..7af3dd59c 100644 --- a/src/core/game_list.h +++ b/src/core/game_list.h @@ -18,6 +18,13 @@ enum class GameListEntryType PSExe }; +struct GameListDatabaseEntry +{ + std::string code; + std::string title; + ConsoleRegion region; +}; + struct GameListEntry { std::string path; @@ -48,11 +55,13 @@ public: static std::optional GetRegionFromSystemArea(CDImage* cdi); static std::optional GetRegionForImage(CDImage* cdi); static std::optional GetRegionForPath(const char* image_path); + static std::string_view GetTitleForPath(const char* path); const EntryList& GetEntries() const { return m_entries; } const u32 GetEntryCount() const { return static_cast(m_entries.size()); } const GameListEntry* GetEntryForPath(const char* path) const; + const GameListDatabaseEntry* GetDatabaseEntryForCode(const std::string& code) const; void SetPathsFromSettings(SettingsInterface& si); void AddDirectory(std::string path, bool recursive); @@ -65,14 +74,7 @@ private: GAME_LIST_CACHE_VERSION = 2 }; - struct GameDatabaseEntry - { - std::string code; - std::string title; - ConsoleRegion region; - }; - - using DatabaseMap = std::unordered_map; + using DatabaseMap = std::unordered_map; using CacheMap = std::unordered_map; struct DirectoryEntry diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 510fd5a99..115968b30 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -8,6 +8,7 @@ #include "common/string_util.h" #include "common/timer.h" #include "dma.h" +#include "game_list.h" #include "gpu.h" #include "host_display.h" #include "mdec.h" @@ -51,6 +52,7 @@ static std::string GetRelativePath(const std::string& path, const char* new_file HostInterface::HostInterface() { + m_game_list = std::make_unique(); m_settings.SetDefaults(); m_last_throttle_time = Common::Timer::GetValue(); } @@ -435,6 +437,8 @@ void HostInterface::UpdateSpeedLimiterState() void HostInterface::OnPerformanceCountersUpdated() {} +void HostInterface::OnRunningGameChanged(const char* path, const char* game_code, const char* game_title) {} + void HostInterface::RunFrame() { m_frame_timer.Reset(); @@ -490,3 +494,31 @@ void HostInterface::ResetPerformanceCounters() m_worst_frame_time_accumulator = 0.0f; m_fps_timer.Reset(); } + +void HostInterface::UpdateRunningGame(const char* path, CDImage* image) +{ + if (!path) + { + OnRunningGameChanged("", "", ""); + return; + } + + const GameListEntry* list_entry = m_game_list->GetEntryForPath(path); + if (list_entry) + { + OnRunningGameChanged(path, list_entry->code.c_str(), list_entry->title.c_str()); + return; + } + + const std::string game_code = image ? GameList::GetGameCodeForImage(image) : std::string(); + const GameListDatabaseEntry* db_entry = + (!game_code.empty()) ? m_game_list->GetDatabaseEntryForCode(game_code) : nullptr; + if (!db_entry) + { + const std::string game_title(GameList::GetTitleForPath(path)); + OnRunningGameChanged(path, game_code.c_str(), game_title.c_str()); + return; + } + + OnRunningGameChanged(path, db_entry->code.c_str(), db_entry->title.c_str()); +} diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 1ba182935..e9356c461 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -11,12 +11,16 @@ #include class AudioStream; +class CDImage; class HostDisplay; +class GameList; class System; class HostInterface { + friend System; + public: HostInterface(); virtual ~HostInterface(); @@ -30,6 +34,9 @@ public: /// Returns a settings object which can be modified. Settings& GetSettings() { return m_settings; } + /// Returns the game list. + const GameList* GetGameList() const { return m_game_list.get(); } + /// Adjusts the throttle frequency, i.e. how many times we should sleep per second. void SetThrottleFrequency(double frequency) { m_throttle_period = static_cast(1000000000.0 / frequency); } @@ -73,6 +80,7 @@ protected: }; virtual void OnPerformanceCountersUpdated(); + virtual void OnRunningGameChanged(const char* path, const char* game_code, const char* game_title); void RunFrame(); @@ -89,9 +97,12 @@ protected: void UpdatePerformanceCounters(); void ResetPerformanceCounters(); + void UpdateRunningGame(const char* path, CDImage* image); + std::unique_ptr m_display; std::unique_ptr m_audio_stream; std::unique_ptr m_system; + std::unique_ptr m_game_list; Settings m_settings; u64 m_last_throttle_time = 0; diff --git a/src/core/system.cpp b/src/core/system.cpp index 2c286669c..db1befc6d 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -154,6 +154,9 @@ bool System::Boot(const char* filename) return false; } + // Notify change of disc. + m_host_interface->UpdateRunningGame(filename, media.get()); + // Insert CD, and apply fastboot patch if enabled. m_cdrom->InsertMedia(std::move(media)); if (m_cdrom->HasMedia() && GetSettings().bios_patch_fast_boot) diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 0b61f6636..008668785 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -42,7 +42,6 @@ void MainWindow::onEmulationStarted() { m_emulation_running = true; updateEmulationActions(false, true); - populateLoadSaveStateMenus(QString()); } void MainWindow::onEmulationStopped() @@ -121,6 +120,15 @@ void MainWindow::onPerformanceCountersUpdated(float speed, float fps, float vps, QStringLiteral("%1ms average, %2ms worst").arg(average_frame_time, 0, 'f', 2).arg(worst_frame_time, 0, 'f', 2)); } +void MainWindow::onRunningGameChanged(QString filename, QString game_code, QString game_title) +{ + populateLoadSaveStateMenus(game_code); + if (game_title.isEmpty()) + setWindowTitle(tr("DuckStation")); + else + setWindowTitle(game_title); +} + void MainWindow::onStartDiscActionTriggered() { QString filename = @@ -288,10 +296,11 @@ void MainWindow::connectSignals() connect(m_host_interface, &QtHostInterface::emulationStopped, this, &MainWindow::onEmulationStopped); connect(m_host_interface, &QtHostInterface::emulationPaused, this, &MainWindow::onEmulationPaused); connect(m_host_interface, &QtHostInterface::toggleFullscreenRequested, this, &MainWindow::toggleFullscreen); - connect(m_host_interface, &QtHostInterface::performanceCountersUpdated, this, - &MainWindow::onPerformanceCountersUpdated); connect(m_host_interface, &QtHostInterface::recreateDisplayWidgetRequested, this, &MainWindow::recreateDisplayWidget, Qt::BlockingQueuedConnection); + connect(m_host_interface, &QtHostInterface::performanceCountersUpdated, this, + &MainWindow::onPerformanceCountersUpdated); + connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged); connect(m_game_list_widget, &GameListWidget::bootEntryRequested, [this](const GameListEntry* entry) { // if we're not running, boot the system, otherwise swap discs @@ -370,23 +379,18 @@ void MainWindow::populateLoadSaveStateMenus(QString game_code) { // TODO: Do we want to test for the existance of these on disk here? load_menu->addAction(tr("Global Save %1 (2020-01-01 00:01:02)").arg(i + 1)); - - if (m_emulation_running) - save_menu->addAction(tr("Global Save %1").arg(i + 1)); + save_menu->addAction(tr("Global Save %1").arg(i + 1)); } if (!game_code.isEmpty()) { load_menu->addSeparator(); - if (m_emulation_running) - save_menu->addSeparator(); + save_menu->addSeparator(); for (int i = 0; i < NUM_SAVE_STATE_SLOTS; i++) { load_menu->addAction(tr("Game Save %1 (2020-01-01 00:01:02)").arg(i + 1)); - - if (m_emulation_running) - save_menu->addAction(tr("Game Save %1").arg(i + 1)); + save_menu->addAction(tr("Game Save %1").arg(i + 1)); } } } diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index e6058614a..e195c41e1 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -9,7 +9,6 @@ class QLabel; -class GameList; class GameListWidget; class QtHostInterface; @@ -30,6 +29,7 @@ private Q_SLOTS: void recreateDisplayWidget(bool create_device_context); void onPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time, float worst_frame_time); + void onRunningGameChanged(QString filename, QString game_code, QString game_title); void onStartDiscActionTriggered(); void onChangeDiscFromFileActionTriggered(); @@ -55,7 +55,6 @@ private: QtHostInterface* m_host_interface = nullptr; - std::unique_ptr m_game_list; GameListWidget* m_game_list_widget = nullptr; QWidget* m_display_widget = nullptr; diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 57e35a1a7..77b159f56 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -162,6 +162,7 @@ void QtHostInterface::createGameList() void QtHostInterface::refreshGameList(bool invalidate_cache /* = false */, bool invalidate_database /* = false */) { + std::lock_guard lock(m_qsettings_mutex); QtSettingsInterface si(m_qsettings); m_game_list->SetPathsFromSettings(si); m_game_list->Refresh(invalidate_cache, invalidate_database); @@ -243,6 +244,13 @@ void QtHostInterface::OnPerformanceCountersUpdated() emit performanceCountersUpdated(m_speed, m_fps, m_vps, m_average_frame_time, m_worst_frame_time); } +void QtHostInterface::OnRunningGameChanged(const char* path, const char* game_code, const char* game_title) +{ + HostInterface::OnRunningGameChanged(path, game_code, game_title); + + emit runningGameChanged(QString::fromUtf8(path), QString::fromUtf8(game_code), QString::fromUtf8(game_title)); +} + void QtHostInterface::updateInputMap() { if (!isOnWorkerThread()) @@ -390,6 +398,7 @@ void QtHostInterface::powerOffSystem() m_audio_stream->PauseOutput(true); m_display_window->destroyDeviceContext(); + emit runningGameChanged(QString(), QString(), QString()); emit emulationStopped(); } @@ -437,6 +446,7 @@ void QtHostInterface::doBootSystem(QString initial_filename, QString initial_sav std::string initial_filename_str = initial_filename.toStdString(); std::string initial_save_state_filename_str = initial_save_state_filename.toStdString(); + std::lock_guard lock(m_qsettings_mutex); if (!CreateSystem() || !BootSystem(initial_filename_str.empty() ? nullptr : initial_filename_str.c_str(), initial_save_state_filename_str.empty() ? nullptr : initial_save_state_filename_str.c_str())) @@ -501,6 +511,7 @@ void QtHostInterface::switchGPURenderer() { if (!m_display_window->initializeDeviceContext(m_settings.gpu_use_debug_device)) { + emit runningGameChanged(QString(), QString(), QString()); emit emulationStopped(); return; } diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index 3e20c2f4d..7a12297e4 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -70,6 +70,7 @@ Q_SIGNALS: void toggleFullscreenRequested(); void recreateDisplayWidgetRequested(bool create_device_context); void performanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, float worst_frame_time); + void runningGameChanged(QString filename, QString game_code, QString game_title); public Q_SLOTS: void applySettings(); @@ -87,6 +88,7 @@ private Q_SLOTS: protected: void OnPerformanceCountersUpdated() override; + void OnRunningGameChanged(const char* path, const char* game_code, const char* game_title) override; private: using InputButtonHandler = std::function; @@ -126,8 +128,6 @@ private: QSettings m_qsettings; std::mutex m_qsettings_mutex; - std::unique_ptr m_game_list; - QtDisplayWindow* m_display_window = nullptr; QThread* m_original_thread = nullptr; Thread* m_worker_thread = nullptr;