diff --git a/src/frontend_common/play_time_manager.cpp b/src/frontend_common/play_time_manager.cpp index 55a8f462e8..b506cd5ae0 100644 --- a/src/frontend_common/play_time_manager.cpp +++ b/src/frontend_common/play_time_manager.cpp @@ -154,6 +154,11 @@ u64 PlayTimeManager::GetPlayTime(u64 program_id) const { } } +void PlayTimeManager::SetPlayTime(u64 program_id, u64 play_time) { + database[program_id] = play_time; + Save(); +} + void PlayTimeManager::ResetProgramPlayTime(u64 program_id) { database.erase(program_id); Save(); diff --git a/src/frontend_common/play_time_manager.h b/src/frontend_common/play_time_manager.h index 5209004896..1a46d17e1f 100644 --- a/src/frontend_common/play_time_manager.h +++ b/src/frontend_common/play_time_manager.h @@ -34,6 +34,7 @@ public: u64 GetPlayTime(u64 program_id) const; void ResetProgramPlayTime(u64 program_id); void SetProgramId(u64 program_id); + void SetPlayTime(u64 program_id, u64 play_time); void Start(); void Stop(); diff --git a/src/qt_common/qt_playtime_manager.cpp b/src/qt_common/qt_playtime_manager.cpp index 3cc472a914..9000a285fa 100644 --- a/src/qt_common/qt_playtime_manager.cpp +++ b/src/qt_common/qt_playtime_manager.cpp @@ -3,6 +3,8 @@ #include "qt_playtime_manager.h" +namespace QtCommon::PlayTimeManager { + QString ReadablePlayTime(qulonglong time_seconds) { if (time_seconds == 0) { return {}; @@ -17,3 +19,24 @@ QString ReadablePlayTime(qulonglong time_seconds) { .arg(value, 0, 'f', !is_minutes && time_seconds % 60 != 0) .arg(QString::fromUtf8(unit)); } + +QString GetPlayTimeUnit(qulonglong time_seconds, TimeUnit unit) { + switch (unit) { + case TimeUnit::Hours: { + const qulonglong hours = time_seconds / 3600; + return QString::number(hours); + } + case TimeUnit::Minutes: { + const qulonglong minutes = (time_seconds % 3600) / 60; + return QString::number(minutes); + } + case TimeUnit::Seconds: { + const qulonglong seconds = time_seconds % 60; + return QString::number(seconds); + } + default: + return QStringLiteral("0"); + } +} + +} // namespace QtCommon::PlayTimeManager \ No newline at end of file diff --git a/src/qt_common/qt_playtime_manager.h b/src/qt_common/qt_playtime_manager.h index dafebf594b..d58793d694 100644 --- a/src/qt_common/qt_playtime_manager.h +++ b/src/qt_common/qt_playtime_manager.h @@ -1,8 +1,20 @@ - // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include +namespace QtCommon::PlayTimeManager { + +enum class TimeUnit { + Hours, + Minutes, + Seconds +}; + // Converts a length of time in seconds into a readable format -QString ReadablePlayTime(qulonglong time_seconds); \ No newline at end of file +QString ReadablePlayTime(qulonglong time_seconds); + +// Returns play time hours/minutes/seconds as a string +QString GetPlayTimeUnit(qulonglong time_seconds, TimeUnit unit); + +} // namespace QtCommon::PlayTimeManager \ No newline at end of file diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index fa61cdfb1f..09aca37d43 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -557,13 +557,15 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); - QAction* remove_play_time_data = remove_menu->addAction(tr("Remove Play Time Data")); QAction* remove_cache_storage = remove_menu->addAction(tr("Remove Cache Storage")); QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache")); QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache")); remove_menu->addSeparator(); QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches")); QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); + QMenu* play_time_menu = context_menu.addMenu(tr("Manage Play Time")); + QAction* set_play_time = play_time_menu->addAction(tr("Set Play Time Data")); + QAction* remove_play_time_data = play_time_menu->addAction(tr("Remove Play Time Data")); QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); @@ -629,6 +631,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path); }); + connect(set_play_time, &QAction::triggered, + [this, program_id]() { emit SetPlayTimeRequested(program_id); }); connect(remove_play_time_data, &QAction::triggered, [this, program_id]() { emit RemovePlayTimeRequested(program_id); }); connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] { diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 3336acca04..88dfa69564 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -104,6 +104,7 @@ signals: void RemoveFileRequested(u64 program_id, QtCommon::Game::GameListRemoveTarget target, const std::string& game_path); void RemovePlayTimeRequested(u64 program_id); + void SetPlayTimeRequested(u64 program_id); void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void VerifyIntegrityRequested(const std::string& game_path); void CopyTIDRequested(u64 program_id); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index d6387dfd00..806fffc292 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -241,7 +241,7 @@ public: void setData(const QVariant& value, int role) override { qulonglong time_seconds = value.toULongLong(); - GameListItem::setData(ReadablePlayTime(time_seconds), Qt::DisplayRole); + GameListItem::setData(QtCommon::PlayTimeManager::ReadablePlayTime(time_seconds), Qt::DisplayRole); GameListItem::setData(value, PlayTimeRole); } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7ade4a2cdc..2111060a6f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1574,6 +1574,8 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); connect(game_list, &GameList::RemovePlayTimeRequested, this, &GMainWindow::OnGameListRemovePlayTimeData); + connect(game_list, &GameList::SetPlayTimeRequested, this, + &GMainWindow::OnGameListSetPlayTime); connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); connect(game_list, &GameList::VerifyIntegrityRequested, this, &GMainWindow::OnGameListVerifyIntegrity); @@ -2634,6 +2636,85 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListR } } +void GMainWindow::OnGameListSetPlayTime(u64 program_id) { + QDialog dialog(this); + dialog.setWindowTitle(tr("Set Play Time Data")); + dialog.setModal(true); + + auto* layout = new QVBoxLayout(&dialog); + auto* input_layout = new QHBoxLayout(); + auto* hours_edit = new QLineEdit(&dialog); + auto* minutes_edit = new QLineEdit(&dialog); + auto* seconds_edit = new QLineEdit(&dialog); + + hours_edit->setValidator(new QIntValidator(0, 9999, hours_edit)); + minutes_edit->setValidator(new QIntValidator(0, 59, minutes_edit)); + seconds_edit->setValidator(new QIntValidator(0, 59, seconds_edit)); + + using QtCommon::PlayTimeManager::TimeUnit; + const u64 current_play_time = play_time_manager->GetPlayTime(program_id); + auto getTimeUnit = [current_play_time](TimeUnit unit) { + return QtCommon::PlayTimeManager::GetPlayTimeUnit(current_play_time, unit); + }; + + hours_edit->setText(getTimeUnit(TimeUnit::Hours)); + minutes_edit->setText(getTimeUnit(TimeUnit::Minutes)); + seconds_edit->setText(getTimeUnit(TimeUnit::Seconds)); + + input_layout->addWidget(new QLabel(tr("Hours:"), &dialog)); + input_layout->addWidget(hours_edit); + input_layout->addWidget(new QLabel(tr("Minutes:"), &dialog)); + input_layout->addWidget(minutes_edit); + input_layout->addWidget(new QLabel(tr("Seconds:"), &dialog)); + input_layout->addWidget(seconds_edit); + + layout->addLayout(input_layout); + + auto* error_label = new QLabel(&dialog); + error_label->setStyleSheet(QStringLiteral("QLabel { color : red; }")); + error_label->setVisible(false); + layout->addWidget(error_label); + + auto* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); + layout->addWidget(button_box); + + connect(button_box, &QDialogButtonBox::accepted, [&]() { + const int hours = hours_edit->text().toInt(); + const int minutes = minutes_edit->text().toInt(); + const int seconds = seconds_edit->text().toInt(); + + if (hours < 0 || hours > 9999) { + error_label->setText(tr("Hours must be between 0 and 9999.")); + error_label->setVisible(true); + return; + } + + if (minutes < 0 || minutes > 59) { + error_label->setText(tr("Minutes must be between 0 and 59.")); + error_label->setVisible(true); + return; + } + + if (seconds < 0 || seconds > 59) { + error_label->setText(tr("Seconds must be between 0 and 59.")); + error_label->setVisible(true); + return; + } + + u64 total_seconds = static_cast(hours) * 3600 + static_cast(minutes) * 60 + static_cast(seconds); + play_time_manager->SetPlayTime(program_id, total_seconds); + game_list->PopulateAsync(UISettings::values.game_dirs); + + dialog.accept(); + }); + + connect(button_box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + dialog.exec(); +} + + + void GMainWindow::OnGameListRemovePlayTimeData(u64 program_id) { if (QMessageBox::question(this, tr("Remove Play Time Data"), tr("Reset play time?"), QMessageBox::Yes | QMessageBox::No, diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e3922759b0..cecd6a93dc 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -345,6 +345,7 @@ private slots: void OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListRemoveTarget target, const std::string& game_path); void OnGameListRemovePlayTimeData(u64 program_id); + void OnGameListSetPlayTime(u64 program_id); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void OnGameListVerifyIntegrity(const std::string& game_path); void OnGameListCopyTID(u64 program_id);