mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-14 10:21:21 +00:00
Qt: Add Recent savestates menu
This commit is contained in:
parent
448666c896
commit
ba702509c8
@ -1117,47 +1117,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||
}
|
||||
else
|
||||
{
|
||||
fs::file save{m_path, fs::isfile + fs::read};
|
||||
|
||||
if (m_path.ends_with(".SAVESTAT") && save && save.size() >= 8 && save.read<u64>() == "RPCS3SAV"_u64)
|
||||
{
|
||||
m_ar = std::make_shared<utils::serial>();
|
||||
m_ar->set_reading_state();
|
||||
|
||||
m_ar->m_file_handler = make_uncompressed_serialization_file_handler(std::move(save));
|
||||
}
|
||||
else if (save && m_path.ends_with(".zst"))
|
||||
{
|
||||
m_ar = std::make_shared<utils::serial>();
|
||||
m_ar->set_reading_state();
|
||||
|
||||
m_ar->m_file_handler = make_compressed_zstd_serialization_file_handler(std::move(save));
|
||||
|
||||
if (m_ar->try_read<u64>().second != "RPCS3SAV"_u64)
|
||||
{
|
||||
m_ar.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ar->pos = 0;
|
||||
}
|
||||
}
|
||||
else if (save && m_path.ends_with(".gz"))
|
||||
{
|
||||
m_ar = std::make_shared<utils::serial>();
|
||||
m_ar->set_reading_state();
|
||||
|
||||
m_ar->m_file_handler = make_compressed_serialization_file_handler(std::move(save));
|
||||
|
||||
if (m_ar->try_read<u64>().second != "RPCS3SAV"_u64)
|
||||
{
|
||||
m_ar.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ar->pos = 0;
|
||||
}
|
||||
}
|
||||
m_ar = make_savestate_reader(m_path);
|
||||
|
||||
m_boot_source_type = CELL_GAME_GAMETYPE_SYS;
|
||||
}
|
||||
@ -1189,18 +1149,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||
|
||||
if (m_ar)
|
||||
{
|
||||
struct file_header
|
||||
{
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
nse_t<u64, 1> magic;
|
||||
bool LE_format;
|
||||
bool state_inspection_support;
|
||||
nse_t<u64, 1> offset;
|
||||
b8 flag_versions_is_following_data;
|
||||
};
|
||||
|
||||
const auto header = m_ar->try_read<file_header>().second;
|
||||
const auto header = m_ar->try_read<savestate_header>().second;
|
||||
|
||||
if (header.magic != "RPCS3SAV"_u64)
|
||||
{
|
||||
@ -1329,7 +1278,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||
|
||||
if (m_ar->m_max_data != m_ar->pos)
|
||||
{
|
||||
fmt::throw_exception("TAR desrialization failed: read bytes: 0x%x, expected: 0x%x, path='%s', ar: %s", m_ar->pos - (m_ar->m_max_data - size), size, path, *m_ar);
|
||||
fmt::throw_exception("TAR deserialization failed: read bytes: 0x%x, expected: 0x%x, path='%s', ar: %s", m_ar->pos - (m_ar->m_max_data - size), size, path, *m_ar);
|
||||
}
|
||||
|
||||
m_ar->m_max_data = umax;
|
||||
|
@ -162,6 +162,60 @@ std::vector<version_entry> get_savestate_versioning_data(fs::file&& file, std::s
|
||||
return ver_data;
|
||||
}
|
||||
|
||||
std::shared_ptr<utils::serial> make_savestate_reader(const std::string& path)
|
||||
{
|
||||
std::shared_ptr<utils::serial> ar;
|
||||
|
||||
fs::file save{path, fs::isfile + fs::read};
|
||||
|
||||
if (!save)
|
||||
{
|
||||
return ar;
|
||||
}
|
||||
|
||||
if (path.ends_with(".SAVESTAT") && save.size() >= 8 && save.read<u64>() == "RPCS3SAV"_u64)
|
||||
{
|
||||
ar = std::make_shared<utils::serial>();
|
||||
ar->set_reading_state();
|
||||
|
||||
ar->m_file_handler = make_uncompressed_serialization_file_handler(std::move(save));
|
||||
}
|
||||
else if (path.ends_with(".zst"))
|
||||
{
|
||||
ar = std::make_shared<utils::serial>();
|
||||
ar->set_reading_state();
|
||||
|
||||
ar->m_file_handler = make_compressed_zstd_serialization_file_handler(std::move(save));
|
||||
|
||||
if (ar->try_read<u64>().second != "RPCS3SAV"_u64)
|
||||
{
|
||||
ar.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
ar->pos = 0;
|
||||
}
|
||||
}
|
||||
else if (path.ends_with(".gz"))
|
||||
{
|
||||
ar = std::make_shared<utils::serial>();
|
||||
ar->set_reading_state();
|
||||
|
||||
ar->m_file_handler = make_compressed_serialization_file_handler(std::move(save));
|
||||
|
||||
if (ar->try_read<u64>().second != "RPCS3SAV"_u64)
|
||||
{
|
||||
ar.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
ar->pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
bool is_savestate_version_compatible(const std::vector<version_entry>& data, bool is_boot_check)
|
||||
{
|
||||
if (data.empty())
|
||||
@ -256,6 +310,15 @@ bool is_savestate_compatible(fs::file&& file, std::string_view filepath)
|
||||
return is_savestate_version_compatible(get_savestate_versioning_data(std::move(file), filepath), false);
|
||||
}
|
||||
|
||||
bool is_savestate_compatible(const std::string& filepath)
|
||||
{
|
||||
if (fs::file file{filepath, fs::isfile + fs::read})
|
||||
{
|
||||
return is_savestate_compatible(std::move(file), filepath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<version_entry> read_used_savestate_versions()
|
||||
{
|
||||
std::vector<version_entry> used_serial;
|
||||
|
@ -10,6 +10,16 @@ struct version_entry
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
struct savestate_header
|
||||
{
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
nse_t<u64, 1> magic;
|
||||
bool LE_format;
|
||||
bool state_inspection_support;
|
||||
nse_t<u64, 1> offset;
|
||||
b8 flag_versions_is_following_data;
|
||||
};
|
||||
|
||||
struct hle_locks_t
|
||||
{
|
||||
@ -27,9 +37,11 @@ struct hle_locks_t
|
||||
bool try_finalize(std::function<bool()> test);
|
||||
};
|
||||
|
||||
std::shared_ptr<utils::serial> make_savestate_reader(const std::string& path);
|
||||
bool load_and_check_reserved(utils::serial& ar, usz size);
|
||||
bool is_savestate_version_compatible(const std::vector<version_entry>& data, bool is_boot_check);
|
||||
std::vector<version_entry> get_savestate_versioning_data(fs::file&& file, std::string_view filepath);
|
||||
bool is_savestate_compatible(fs::file&& file, std::string_view filepath);
|
||||
bool is_savestate_compatible(const std::string& filepath);
|
||||
std::vector<version_entry> read_used_savestate_versions();
|
||||
std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id);
|
||||
std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id);
|
||||
|
@ -120,6 +120,9 @@ namespace gui
|
||||
const gui_save rg_freeze = gui_save(main_window, "recentGamesFrozen", false);
|
||||
const gui_save rg_entries = gui_save(main_window, "recentGamesNames", QVariant::fromValue(q_pair_list()));
|
||||
|
||||
const gui_save rs_freeze = gui_save(main_window, "recentSavestatesFrozen", false);
|
||||
const gui_save rs_entries = gui_save(main_window, "recentSavestatesNames", QVariant::fromValue(q_pair_list()));
|
||||
|
||||
const gui_save ib_skip_version = gui_save(main_window, "infoBoxSkipVersion", "");
|
||||
const gui_save ib_pkg_success = gui_save(main_window, "infoBoxEnabledInstallPKG", true);
|
||||
const gui_save ib_pup_success = gui_save(main_window, "infoBoxEnabledInstallPUP", true);
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
|
||||
#include "Crypto/unpkg.h"
|
||||
#include "Crypto/unself.h"
|
||||
@ -234,9 +235,9 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
|
||||
show(); // needs to be done before creating the thumbnail toolbar
|
||||
|
||||
// enable play options if a recent game exists
|
||||
const bool enable_play_last = !m_recent_game_acts.isEmpty() && m_recent_game_acts.first();
|
||||
const bool enable_play_last = !m_recent_game.actions.isEmpty() && m_recent_game.actions.first();
|
||||
|
||||
const QString start_tooltip = enable_play_last ? tr("Play %0").arg(m_recent_game_acts.first()->text()) : tr("Play");
|
||||
const QString start_tooltip = enable_play_last ? tr("Play %0").arg(m_recent_game.actions.first()->text()) : tr("Play");
|
||||
|
||||
if (enable_play_last)
|
||||
{
|
||||
@ -510,9 +511,9 @@ void main_window::OnPlayOrPause()
|
||||
show_boot_error(error);
|
||||
}
|
||||
}
|
||||
else if (!m_recent_game_acts.isEmpty())
|
||||
else if (!m_recent_game.actions.isEmpty())
|
||||
{
|
||||
BootRecentAction(m_recent_game_acts.first());
|
||||
BootRecentAction(m_recent_game.actions.first(), false);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -607,7 +608,7 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo
|
||||
{
|
||||
gui_log.success("Boot successful.");
|
||||
|
||||
AddRecentAction(gui::Recent_Game(qstr(Emu.GetBoot()), qstr(Emu.GetTitleAndTitleID())));
|
||||
AddRecentAction(gui::Recent_Game(qstr(Emu.GetBoot()), qstr(Emu.GetTitleAndTitleID())), is_savestate_compatible(path));
|
||||
|
||||
if (refresh_list)
|
||||
{
|
||||
@ -2190,55 +2191,59 @@ void main_window::OnEnableDiscInsert(bool enabled) const
|
||||
ui->insertDiscAct->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void main_window::BootRecentAction(const QAction* act)
|
||||
void main_window::BootRecentAction(const QAction* act, bool is_savestate)
|
||||
{
|
||||
if (Emu.IsRunning())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game;
|
||||
QMenu* menu = is_savestate ? ui->bootRecentSavestatesMenu : ui->bootRecentMenu;
|
||||
|
||||
const QString pth = act->data().toString();
|
||||
const std::string path = sstr(pth);
|
||||
const std::string path = pth.toStdString();
|
||||
QString name;
|
||||
bool contains_path = false;
|
||||
|
||||
int idx = -1;
|
||||
for (int i = 0; i < m_rg_entries.count(); i++)
|
||||
for (int i = 0; i < rgw.entries.count(); i++)
|
||||
{
|
||||
if (::at32(m_rg_entries, i).first == pth)
|
||||
const auto& entry = rgw.entries[i];
|
||||
if (entry.first == pth)
|
||||
{
|
||||
idx = i;
|
||||
contains_path = true;
|
||||
name = ::at32(m_rg_entries, idx).second;
|
||||
name = entry.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// path is invalid: remove action from list return
|
||||
if ((contains_path && name.isEmpty()) || (!QFileInfo(pth).isDir() && !QFileInfo(pth).isFile()))
|
||||
if ((contains_path && name.isEmpty()) || !fs::exists(path))
|
||||
{
|
||||
if (contains_path)
|
||||
{
|
||||
// clear menu of actions
|
||||
for (QAction* action : m_recent_game_acts)
|
||||
for (QAction* action : rgw.actions)
|
||||
{
|
||||
ui->bootRecentMenu->removeAction(action);
|
||||
menu->removeAction(action);
|
||||
}
|
||||
|
||||
// remove action from list
|
||||
m_rg_entries.removeAt(idx);
|
||||
m_recent_game_acts.removeAt(idx);
|
||||
rgw.entries.removeAt(idx);
|
||||
rgw.actions.removeAt(idx);
|
||||
|
||||
m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(m_rg_entries));
|
||||
m_gui_settings->SetValue(is_savestate ? gui::rs_entries : gui::rg_entries, gui_settings::List2Var(rgw.entries));
|
||||
|
||||
gui_log.error("Recent Game not valid, removed from Boot Recent list: %s", path);
|
||||
|
||||
// refill menu with actions
|
||||
for (int i = 0; i < m_recent_game_acts.count(); i++)
|
||||
for (int i = 0; i < rgw.actions.count(); i++)
|
||||
{
|
||||
m_recent_game_acts[i]->setShortcut(tr("Ctrl+%1").arg(i + 1));
|
||||
m_recent_game_acts[i]->setToolTip(::at32(m_rg_entries, i).second);
|
||||
ui->bootRecentMenu->addAction(m_recent_game_acts[i]);
|
||||
rgw.actions[i]->setShortcut(tr("Ctrl+%1").arg(i + 1));
|
||||
rgw.actions[i]->setToolTip(::at32(rgw.entries, i).second);
|
||||
menu->addAction(rgw.actions[i]);
|
||||
}
|
||||
|
||||
gui_log.warning("Boot Recent list refreshed");
|
||||
@ -2253,19 +2258,21 @@ void main_window::BootRecentAction(const QAction* act)
|
||||
Boot(path, "", true);
|
||||
}
|
||||
|
||||
QAction* main_window::CreateRecentAction(const q_string_pair& entry, const uint& sc_idx)
|
||||
QAction* main_window::CreateRecentAction(const q_string_pair& entry, u32 sc_idx, bool is_savestate)
|
||||
{
|
||||
recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game;
|
||||
|
||||
// if path is not valid remove from list
|
||||
if (entry.second.isEmpty() || (!QFileInfo(entry.first).isDir() && !QFileInfo(entry.first).isFile()))
|
||||
{
|
||||
if (m_rg_entries.contains(entry))
|
||||
if (rgw.entries.contains(entry))
|
||||
{
|
||||
gui_log.warning("Recent Game not valid, removing from Boot Recent list: %s", entry.first);
|
||||
|
||||
const int idx = m_rg_entries.indexOf(entry);
|
||||
m_rg_entries.removeAt(idx);
|
||||
const int idx = rgw.entries.indexOf(entry);
|
||||
rgw.entries.removeAt(idx);
|
||||
|
||||
m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(m_rg_entries));
|
||||
m_gui_settings->SetValue(is_savestate ? gui::rs_entries : gui::rg_entries, gui_settings::List2Var(rgw.entries));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -2290,69 +2297,74 @@ QAction* main_window::CreateRecentAction(const q_string_pair& entry, const uint&
|
||||
}
|
||||
|
||||
// connect boot
|
||||
connect(act, &QAction::triggered, this, [act, this]() {BootRecentAction(act); });
|
||||
connect(act, &QAction::triggered, this, [this, act, is_savestate](){ BootRecentAction(act, is_savestate); });
|
||||
|
||||
return act;
|
||||
}
|
||||
|
||||
void main_window::AddRecentAction(const q_string_pair& entry)
|
||||
void main_window::AddRecentAction(const q_string_pair& entry, bool is_savestate)
|
||||
{
|
||||
QAction* freezeAction = is_savestate ? ui->freezeRecentSavestatesAct : ui->freezeRecentAct;
|
||||
|
||||
// don't change list on freeze
|
||||
if (ui->freezeRecentAct->isChecked())
|
||||
if (freezeAction->isChecked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// create new action, return if not valid
|
||||
QAction* act = CreateRecentAction(entry, 1);
|
||||
QAction* act = CreateRecentAction(entry, 1, is_savestate);
|
||||
if (!act)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game;
|
||||
QMenu* menu = is_savestate ? ui->bootRecentSavestatesMenu : ui->bootRecentMenu;
|
||||
|
||||
// clear menu of actions
|
||||
for (QAction* action : m_recent_game_acts)
|
||||
for (QAction* action : rgw.actions)
|
||||
{
|
||||
ui->bootRecentMenu->removeAction(action);
|
||||
menu->removeAction(action);
|
||||
}
|
||||
|
||||
// If path already exists, remove it in order to get it to beginning. Also remove duplicates.
|
||||
for (int i = m_rg_entries.count() - 1; i >= 0; --i)
|
||||
for (int i = rgw.entries.count() - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_rg_entries[i].first == entry.first)
|
||||
if (rgw.entries[i].first == entry.first)
|
||||
{
|
||||
m_rg_entries.removeAt(i);
|
||||
m_recent_game_acts.removeAt(i);
|
||||
rgw.entries.removeAt(i);
|
||||
rgw.actions.removeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// remove oldest action at the end if needed
|
||||
if (m_rg_entries.count() == 9)
|
||||
if (rgw.entries.count() == 9)
|
||||
{
|
||||
m_rg_entries.removeLast();
|
||||
m_recent_game_acts.removeLast();
|
||||
rgw.entries.removeLast();
|
||||
rgw.actions.removeLast();
|
||||
}
|
||||
else if (m_rg_entries.count() > 9)
|
||||
else if (rgw.entries.count() > 9)
|
||||
{
|
||||
gui_log.error("Recent games entrylist too big");
|
||||
}
|
||||
|
||||
if (m_rg_entries.count() < 9)
|
||||
if (rgw.entries.count() < 9)
|
||||
{
|
||||
// add new action at the beginning
|
||||
m_rg_entries.prepend(entry);
|
||||
m_recent_game_acts.prepend(act);
|
||||
rgw.entries.prepend(entry);
|
||||
rgw.actions.prepend(act);
|
||||
}
|
||||
|
||||
// refill menu with actions
|
||||
for (int i = 0; i < m_recent_game_acts.count(); i++)
|
||||
for (int i = 0; i < rgw.actions.count(); i++)
|
||||
{
|
||||
m_recent_game_acts[i]->setShortcut(tr("Ctrl+%1").arg(i + 1));
|
||||
m_recent_game_acts[i]->setToolTip(::at32(m_rg_entries, i).second);
|
||||
ui->bootRecentMenu->addAction(m_recent_game_acts[i]);
|
||||
rgw.actions[i]->setShortcut(tr("Ctrl+%1").arg(i + 1));
|
||||
rgw.actions[i]->setToolTip(::at32(rgw.entries, i).second);
|
||||
menu->addAction(rgw.actions[i]);
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(m_rg_entries));
|
||||
m_gui_settings->SetValue(is_savestate ? gui::rs_entries : gui::rg_entries, gui_settings::List2Var(rgw.entries));
|
||||
}
|
||||
|
||||
void main_window::UpdateLanguageActions(const QStringList& language_codes, const QString& language_code)
|
||||
@ -2638,26 +2650,59 @@ void main_window::CreateConnects()
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->bootRecentSavestatesMenu, &QMenu::aboutToShow, this, [this]()
|
||||
{
|
||||
// Enable/Disable Recent Savestates List
|
||||
const bool stopped = Emu.IsStopped();
|
||||
for (QAction* act : ui->bootRecentSavestatesMenu->actions())
|
||||
{
|
||||
if (act != ui->freezeRecentSavestatesAct && act != ui->clearRecentSavestatesAct)
|
||||
{
|
||||
act->setEnabled(stopped);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->clearRecentAct, &QAction::triggered, this, [this]()
|
||||
{
|
||||
if (ui->freezeRecentAct->isChecked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_rg_entries.clear();
|
||||
for (QAction* act : m_recent_game_acts)
|
||||
m_recent_game.entries.clear();
|
||||
for (QAction* act : m_recent_game.actions)
|
||||
{
|
||||
ui->bootRecentMenu->removeAction(act);
|
||||
}
|
||||
m_recent_game_acts.clear();
|
||||
m_recent_game.actions.clear();
|
||||
m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(q_pair_list()));
|
||||
});
|
||||
|
||||
connect(ui->clearRecentSavestatesAct, &QAction::triggered, this, [this]()
|
||||
{
|
||||
if (ui->freezeRecentSavestatesAct->isChecked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_recent_save.entries.clear();
|
||||
for (QAction* act : m_recent_save.actions)
|
||||
{
|
||||
ui->bootRecentSavestatesMenu->removeAction(act);
|
||||
}
|
||||
m_recent_save.actions.clear();
|
||||
m_gui_settings->SetValue(gui::rs_entries, gui_settings::List2Var(q_pair_list()));
|
||||
});
|
||||
|
||||
connect(ui->freezeRecentAct, &QAction::triggered, this, [this](bool checked)
|
||||
{
|
||||
m_gui_settings->SetValue(gui::rg_freeze, checked);
|
||||
});
|
||||
|
||||
connect(ui->freezeRecentSavestatesAct, &QAction::triggered, this, [this](bool checked)
|
||||
{
|
||||
m_gui_settings->SetValue(gui::rs_freeze, checked);
|
||||
});
|
||||
|
||||
connect(ui->bootInstallPkgAct, &QAction::triggered, this, [this] {InstallPackages(); });
|
||||
connect(ui->bootInstallPupAct, &QAction::triggered, this, [this] {InstallPup(); });
|
||||
|
||||
@ -3530,9 +3575,9 @@ void main_window::CreateDockWindows()
|
||||
ui->toolbar_start->setIcon(m_icon_restart);
|
||||
ui->toolbar_start->setText(tr("Restart"));
|
||||
}
|
||||
else if (!m_recent_game_acts.isEmpty()) // Get last played game
|
||||
else if (!m_recent_game.actions.isEmpty()) // Get last played game
|
||||
{
|
||||
tooltip = tr("Play %0").arg(m_recent_game_acts.first()->text());
|
||||
tooltip = tr("Play %0").arg(m_recent_game.actions.first()->text());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3584,35 +3629,45 @@ void main_window::ConfigureGuiFromSettings()
|
||||
m_mw->restoreState(m_gui_settings->GetValue(gui::mw_mwState).toByteArray());
|
||||
|
||||
ui->freezeRecentAct->setChecked(m_gui_settings->GetValue(gui::rg_freeze).toBool());
|
||||
m_rg_entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rg_entries));
|
||||
ui->freezeRecentSavestatesAct->setChecked(m_gui_settings->GetValue(gui::rs_freeze).toBool());
|
||||
m_recent_game.entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rg_entries));
|
||||
m_recent_save.entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rs_entries));
|
||||
|
||||
// clear recent games menu of actions
|
||||
for (QAction* act : m_recent_game_acts)
|
||||
const auto update_recent_games_menu = [this](bool is_savestate)
|
||||
{
|
||||
ui->bootRecentMenu->removeAction(act);
|
||||
}
|
||||
m_recent_game_acts.clear();
|
||||
recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game;
|
||||
QMenu* menu = is_savestate ? ui->bootRecentSavestatesMenu : ui->bootRecentMenu;
|
||||
|
||||
// Fill the recent games menu
|
||||
for (int i = 0; i < m_rg_entries.count(); i++)
|
||||
{
|
||||
// adjust old unformatted entries (avoid duplication)
|
||||
m_rg_entries[i] = gui::Recent_Game(m_rg_entries[i].first, m_rg_entries[i].second);
|
||||
|
||||
// create new action
|
||||
QAction* act = CreateRecentAction(m_rg_entries[i], i + 1);
|
||||
|
||||
// add action to menu
|
||||
if (act)
|
||||
// clear recent games menu of actions
|
||||
for (QAction* act : rgw.actions)
|
||||
{
|
||||
m_recent_game_acts.append(act);
|
||||
ui->bootRecentMenu->addAction(act);
|
||||
menu->removeAction(act);
|
||||
}
|
||||
else
|
||||
rgw.actions.clear();
|
||||
|
||||
// Fill the recent games menu
|
||||
for (int i = 0; i < rgw.entries.count(); i++)
|
||||
{
|
||||
i--; // list count is now an entry shorter so we have to repeat the same index in order to load all other entries
|
||||
// adjust old unformatted entries (avoid duplication)
|
||||
rgw.entries[i] = gui::Recent_Game(rgw.entries[i].first, rgw.entries[i].second);
|
||||
|
||||
// create new action
|
||||
QAction* act = CreateRecentAction(rgw.entries[i], i + 1, is_savestate);
|
||||
|
||||
// add action to menu
|
||||
if (act)
|
||||
{
|
||||
rgw.actions.append(act);
|
||||
menu->addAction(act);
|
||||
}
|
||||
else
|
||||
{
|
||||
i--; // list count is now an entry shorter so we have to repeat the same index in order to load all other entries
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
update_recent_games_menu(false);
|
||||
update_recent_games_menu(true);
|
||||
|
||||
ui->showLogAct->setChecked(m_gui_settings->GetValue(gui::mw_logger).toBool());
|
||||
ui->showGameListAct->setChecked(m_gui_settings->GetValue(gui::mw_gamelist).toBool());
|
||||
@ -4147,14 +4202,19 @@ void main_window::dropEvent(QDropEvent* event)
|
||||
|
||||
Emu.GracefulShutdown(false);
|
||||
|
||||
if (const auto error = Emu.BootGame(sstr(drop_paths.first()), "", true); error != game_boot_result::no_errors)
|
||||
const std::string path = drop_paths.first().toStdString();
|
||||
|
||||
if (const auto error = Emu.BootGame(path, "", true); error != game_boot_result::no_errors)
|
||||
{
|
||||
gui_log.error("Boot failed: reason: %s, path: %s", error, drop_paths.first());
|
||||
gui_log.error("Boot failed: reason: %s, path: %s", error, path);
|
||||
show_boot_error(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
gui_log.success("Elf Boot from drag and drop done: %s", drop_paths.first());
|
||||
gui_log.success("Elf Boot from drag and drop done: %s", path);
|
||||
|
||||
AddRecentAction(gui::Recent_Game(QString::fromStdString(path), QString::fromStdString(Emu.GetTitleAndTitleID())), is_savestate_compatible(path));
|
||||
|
||||
m_game_list_frame->Refresh(true);
|
||||
}
|
||||
break;
|
||||
|
@ -175,17 +175,22 @@ private:
|
||||
drop_type IsValidFile(const QMimeData& md, QStringList* drop_paths = nullptr);
|
||||
void AddGamesFromDirs(QStringList&& paths);
|
||||
|
||||
QAction* CreateRecentAction(const q_string_pair& entry, const uint& sc_idx);
|
||||
void BootRecentAction(const QAction* act);
|
||||
void AddRecentAction(const q_string_pair& entry);
|
||||
QAction* CreateRecentAction(const q_string_pair& entry, u32 sc_idx, bool is_savestate);
|
||||
void BootRecentAction(const QAction* act, bool is_savestate);
|
||||
void AddRecentAction(const q_string_pair& entry, bool is_savestate);
|
||||
|
||||
void UpdateLanguageActions(const QStringList& language_codes, const QString& language);
|
||||
void UpdateFilterActions();
|
||||
|
||||
static QString GetCurrentTitle();
|
||||
|
||||
q_pair_list m_rg_entries;
|
||||
QList<QAction*> m_recent_game_acts;
|
||||
struct recent_game_wrapper
|
||||
{
|
||||
q_pair_list entries;
|
||||
QList<QAction*> actions;
|
||||
};
|
||||
recent_game_wrapper m_recent_game {};
|
||||
recent_game_wrapper m_recent_save {};
|
||||
|
||||
std::shared_ptr<gui_game_info> m_selected_game;
|
||||
|
||||
|
@ -202,10 +202,19 @@
|
||||
<addaction name="createFirmwareCacheAct"/>
|
||||
<addaction name="removeFirmwareCacheAct"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="bootRecentSavestatesMenu">
|
||||
<property name="title">
|
||||
<string>Boot Recent Savestate</string>
|
||||
</property>
|
||||
<addaction name="clearRecentSavestatesAct"/>
|
||||
<addaction name="freezeRecentSavestatesAct"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="bootGameAct"/>
|
||||
<addaction name="bootVSHAct"/>
|
||||
<addaction name="bootElfMenu"/>
|
||||
<addaction name="bootSavestateAct"/>
|
||||
<addaction name="bootRecentSavestatesMenu"/>
|
||||
<addaction name="bootRecentMenu"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="addGamesAct"/>
|
||||
@ -1387,6 +1396,16 @@
|
||||
<string>Operating System</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="clearRecentSavestatesAct">
|
||||
<property name="text">
|
||||
<string>List Clear</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="freezeRecentSavestatesAct">
|
||||
<property name="text">
|
||||
<string>List Freeze</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
@ -187,7 +187,6 @@ private:
|
||||
void initialize(utils::serial& ar);
|
||||
void stream_data_prepare_thread_op();
|
||||
void file_writer_thread_op();
|
||||
void blocked_compressed_write(const std::vector<u8>& data);
|
||||
};
|
||||
|
||||
template <typename File> requires (std::is_same_v<std::remove_cvref_t<File>, fs::file>)
|
||||
|
Loading…
x
Reference in New Issue
Block a user