Qt: Use disc sets for changing discs

This commit is contained in:
Stenzek 2023-08-23 18:12:10 +10:00
parent 381bd92f87
commit 7d914a9384
7 changed files with 169 additions and 44 deletions

View File

@ -996,40 +996,85 @@ void FullscreenUI::DoChangeDiscFromFile()
void FullscreenUI::DoChangeDisc()
{
if (!System::HasMediaSubImages())
ImGuiFullscreen::ChoiceDialogOptions options;
if (System::HasMediaSubImages())
{
DoChangeDiscFromFile();
const u32 current_index = System::GetMediaSubImageIndex();
const u32 count = System::GetMediaSubImageCount();
options.reserve(count + 1);
options.emplace_back(FSUI_STR("From File..."), false);
for (u32 i = 0; i < count; i++)
options.emplace_back(System::GetMediaSubImageTitle(i), i == current_index);
auto callback = [](s32 index, const std::string& title, bool checked) {
if (index == 0)
{
CloseChoiceDialog();
DoChangeDiscFromFile();
return;
}
else if (index > 0)
{
System::SwitchMediaSubImage(static_cast<u32>(index - 1));
}
QueueResetFocus();
CloseChoiceDialog();
ReturnToMainWindow();
};
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), true, std::move(options),
std::move(callback));
return;
}
const u32 current_index = System::GetMediaSubImageIndex();
const u32 count = System::GetMediaSubImageCount();
ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(count + 1);
options.emplace_back("From File...", false);
for (u32 i = 0; i < count; i++)
options.emplace_back(System::GetMediaSubImageTitle(i), i == current_index);
auto callback = [](s32 index, const std::string& title, bool checked) {
if (index == 0)
if (const GameDatabase::Entry* entry = System::GetGameDatabaseEntry(); entry && !entry->disc_set_serials.empty())
{
const auto lock = GameList::GetLock();
const auto matches = GameList::GetMatchingEntriesForSerial(entry->disc_set_serials);
if (matches.size() > 1)
{
CloseChoiceDialog();
DoChangeDiscFromFile();
options.reserve(matches.size() + 1);
options.emplace_back(FSUI_STR("From File..."), false);
std::vector<std::string> paths;
paths.reserve(matches.size());
const std::string& current_path = System::GetDiscPath();
for (auto& [title, glentry] : matches)
{
options.emplace_back(std::move(title), current_path == glentry->path);
paths.push_back(glentry->path);
}
auto callback = [paths = std::move(paths)](s32 index, const std::string& title, bool checked) {
if (index == 0)
{
CloseChoiceDialog();
DoChangeDiscFromFile();
return;
}
else if (index > 0)
{
System::InsertMedia(paths[index - 1].c_str());
}
QueueResetFocus();
CloseChoiceDialog();
ReturnToMainWindow();
};
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), true, std::move(options),
std::move(callback));
return;
}
else if (index > 0)
{
System::SwitchMediaSubImage(static_cast<u32>(index - 1));
}
}
QueueResetFocus();
CloseChoiceDialog();
ReturnToMainWindow();
};
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), true, std::move(options),
std::move(callback));
DoChangeDiscFromFile();
}
void FullscreenUI::DoCheatsMenu()

View File

@ -1033,6 +1033,50 @@ TinyString GameList::FormatTimespan(std::time_t timespan, bool long_format)
return ret;
}
std::vector<std::pair<std::string, const GameList::Entry*>>
GameList::GetMatchingEntriesForSerial(const gsl::span<const std::string> serials)
{
std::vector<std::pair<std::string, const GameList::Entry*>> ret;
ret.reserve(serials.size());
for (const std::string& serial : serials)
{
const Entry* matching_entry = nullptr;
bool has_multiple_entries = false;
for (const Entry& entry : s_entries)
{
if (entry.serial != serial)
continue;
if (!matching_entry)
matching_entry = &entry;
else
has_multiple_entries = true;
}
if (!matching_entry)
continue;
if (!has_multiple_entries)
{
ret.emplace_back(matching_entry->title, matching_entry);
continue;
}
// Have to add all matching files.
for (const Entry& entry : s_entries)
{
if (entry.serial != serial)
continue;
ret.emplace_back(Path::GetFileName(entry.path), &entry);
}
}
return ret;
}
bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial,
ProgressCallback* progress, std::function<void(const Entry*, std::string)> save_callback)
{

View File

@ -10,6 +10,8 @@
#include "common/string.h"
#include "gsl/span"
#include <ctime>
#include <functional>
#include <mutex>
@ -102,6 +104,11 @@ std::string GetCoverImagePathForEntry(const Entry* entry);
std::string GetCoverImagePath(const std::string& path, const std::string& serial, const std::string& title);
std::string GetNewCoverImagePathForEntry(const Entry* entry, const char* new_filename, bool use_serial);
/// Returns a list of (title, entry) for entries matching serials. Titles will match the gamedb title,
/// except when two files have the same serial, in which case the filename will be used instead.
std::vector<std::pair<std::string, const Entry*>>
GetMatchingEntriesForSerial(const gsl::span<const std::string> serials);
/// Downloads covers using the specified URL templates. By default, covers are saved by title, but this can be changed
/// with the use_serial parameter. save_callback optionall takes the entry and the path the new cover is saved to.
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false,

View File

@ -333,6 +333,11 @@ const std::string& System::GetGameTitle()
return s_running_game_title;
}
const GameDatabase::Entry* System::GetGameDatabaseEntry()
{
return s_running_game_entry;
}
System::GameHash System::GetGameHash()
{
return s_running_game_hash;

View File

@ -25,6 +25,11 @@ struct ImageInfo;
struct Hash;
}
namespace GameDatabase
{
struct Entry;
}
struct SystemBootParameters
{
SystemBootParameters();
@ -184,6 +189,7 @@ void FrameDone();
const std::string& GetDiscPath();
const std::string& GetGameSerial();
const std::string& GetGameTitle();
const GameDatabase::Entry* GetGameDatabaseEntry();
GameHash GetGameHash();
bool IsRunningUnknownGame();
bool WasFastBooted();

View File

@ -603,8 +603,9 @@ void MainWindow::onSystemDestroyed()
void MainWindow::onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title)
{
m_current_game_title = game_title.toStdString();
m_current_game_serial = game_serial.toStdString();
m_current_game_path = filename;
m_current_game_title = game_title;
m_current_game_serial = game_serial;
updateWindowTitle();
// updateSaveStateMenus(path, serial, crc);
@ -916,18 +917,34 @@ void MainWindow::populateSaveStateMenu(const char* game_serial, QMenu* menu)
void MainWindow::populateChangeDiscSubImageMenu(QMenu* menu, QActionGroup* action_group)
{
if (!s_system_valid || !System::HasMediaSubImages())
if (!s_system_valid)
return;
const u32 count = System::GetMediaSubImageCount();
const u32 current = System::GetMediaSubImageIndex();
for (u32 i = 0; i < count; i++)
if (System::HasMediaSubImages())
{
QAction* action = action_group->addAction(QString::fromStdString(System::GetMediaSubImageTitle(i)));
action->setCheckable(true);
action->setChecked(i == current);
connect(action, &QAction::triggered, [i]() { g_emu_thread->changeDiscFromPlaylist(i); });
menu->addAction(action);
const u32 count = System::GetMediaSubImageCount();
const u32 current = System::GetMediaSubImageIndex();
for (u32 i = 0; i < count; i++)
{
QAction* action = action_group->addAction(QString::fromStdString(System::GetMediaSubImageTitle(i)));
action->setCheckable(true);
action->setChecked(i == current);
connect(action, &QAction::triggered, [i]() { g_emu_thread->changeDiscFromPlaylist(i); });
menu->addAction(action);
}
}
else if (const GameDatabase::Entry* entry = System::GetGameDatabaseEntry(); entry && !entry->disc_set_serials.empty())
{
auto lock = GameList::GetLock();
for (const auto& [title, glentry] : GameList::GetMatchingEntriesForSerial(entry->disc_set_serials))
{
QAction* action = action_group->addAction(QString::fromStdString(title));
QString path = QString::fromStdString(glentry->path);
action->setCheckable(true);
action->setChecked(path == m_current_game_path);
connect(action, &QAction::triggered, [path = std::move(path)]() { g_emu_thread->changeDisc(path); });
menu->addAction(action);
}
}
}
@ -1155,12 +1172,12 @@ void MainWindow::onChangeDiscMenuAboutToHide()
void MainWindow::onLoadStateMenuAboutToShow()
{
populateLoadStateMenu(m_current_game_serial.c_str(), m_ui.menuLoadState);
populateLoadStateMenu(m_current_game_serial.toUtf8().constData(), m_ui.menuLoadState);
}
void MainWindow::onSaveStateMenuAboutToShow()
{
populateSaveStateMenu(m_current_game_serial.c_str(), m_ui.menuSaveState);
populateSaveStateMenu(m_current_game_serial.toUtf8().constData(), m_ui.menuSaveState);
}
void MainWindow::onCheatsMenuAboutToShow()
@ -1706,9 +1723,9 @@ void MainWindow::updateWindowTitle()
{
QString suffix(QtHost::GetAppConfigSuffix());
QString main_title(QtHost::GetAppNameAndVersion() + suffix);
QString display_title(QString::fromStdString(m_current_game_title) + suffix);
QString display_title(m_current_game_title + suffix);
if (!s_system_valid || m_current_game_title.empty())
if (!s_system_valid || m_current_game_title.isEmpty())
display_title = main_title;
else if (isRenderingToMain())
main_title = display_title;
@ -2464,7 +2481,7 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
return true;
// If we don't have a serial, we can't save state.
allow_save_to_state &= !m_current_game_serial.empty();
allow_save_to_state &= !m_current_game_serial.isEmpty();
save_state &= allow_save_to_state;
// Only confirm on UI thread because we need to display a msgbox.

View File

@ -277,8 +277,9 @@ private:
CheatManagerDialog* m_cheat_manager_dialog = nullptr;
DebuggerWindow* m_debugger_window = nullptr;
std::string m_current_game_title;
std::string m_current_game_serial;
QString m_current_game_path;
QString m_current_game_title;
QString m_current_game_serial;
bool m_was_paused_by_focus_loss = false;
bool m_open_debugger_on_start = false;