Skip to content

Commit

Permalink
Qt: Use disc sets for changing discs
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Aug 23, 2023
1 parent 381bd92 commit 7d914a9
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 43 deletions.
95 changes: 70 additions & 25 deletions src/core/fullscreen_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
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)
{
options.reserve(matches.size() + 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);
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));

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));
DoChangeDiscFromFile();
}

void FullscreenUI::DoCheatsMenu()
Expand Down
44 changes: 44 additions & 0 deletions src/core/game_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
7 changes: 7 additions & 0 deletions src/core/game_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include "common/string.h"

#include "gsl/span"

#include <ctime>
#include <functional>
#include <mutex>
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions src/core/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/core/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ struct ImageInfo;
struct Hash;
}

namespace GameDatabase
{
struct Entry;
}

struct SystemBootParameters
{
SystemBootParameters();
Expand Down Expand Up @@ -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();
Expand Down
49 changes: 33 additions & 16 deletions src/duckstation-qt/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions src/duckstation-qt/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ private Q_SLOTS:
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;
Expand Down

0 comments on commit 7d914a9

Please sign in to comment.