Added total times played

This commit is contained in:
Jarrod Norwell 2024-05-27 12:15:27 +08:00
parent 1723651511
commit 185c1f0e8f
13 changed files with 106 additions and 15 deletions

View File

@ -102,7 +102,7 @@ void PushInShowController(Core::System& system, AppletStorageChannel& channel) {
// ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
// which sets this to the input param
.style_set = Core::HID::NpadStyleSet::None,
.joy_hold_type = 0,
.joy_hold_type = Frontend::NpadJoyDeviceHoldType::Vertical,
};
std::vector<u8> common_args_data(sizeof(common_args));
std::vector<u8> private_args_data(sizeof(private_args));

View File

@ -53,6 +53,8 @@ enum class ControllerSupportResult : u32 {
Cancel = 2,
};
enum class NpadJoyDeviceHoldType : u32 { Vertical = 0, Horizontal = 1 };
struct ControllerSupportArgPrivate {
u32 arg_private_size{};
u32 arg_size{};
@ -61,7 +63,7 @@ struct ControllerSupportArgPrivate {
ControllerSupportMode mode{};
ControllerSupportCaller caller{};
Core::HID::NpadStyleSet style_set{};
u32 joy_hold_type{};
NpadJoyDeviceHoldType joy_hold_type{};
};
static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
"ControllerSupportArgPrivate has incorrect size.");

View File

@ -125,6 +125,8 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
connect(ui->show_play_time, &QCheckBox::stateChanged, this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->show_total_times, &QCheckBox::stateChanged, this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
@ -170,6 +172,7 @@ void ConfigureUi::ApplyConfiguration() {
UISettings::values.show_size = ui->show_size->isChecked();
UISettings::values.show_types = ui->show_types->isChecked();
UISettings::values.show_play_time = ui->show_play_time->isChecked();
UISettings::values.show_total_times = ui->show_total_times->isChecked();
UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
@ -200,6 +203,7 @@ void ConfigureUi::SetConfiguration() {
ui->show_size->setChecked(UISettings::values.show_size.GetValue());
ui->show_types->setChecked(UISettings::values.show_types.GetValue());
ui->show_play_time->setChecked(UISettings::values.show_play_time.GetValue());
ui->show_total_times->setChecked(UISettings::values.show_total_times.GetValue());
ui->game_icon_size_combobox->setCurrentIndex(
ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
ui->folder_icon_size_combobox->setCurrentIndex(

View File

@ -111,6 +111,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="show_total_times">
<property name="text">
<string>Show Total Times Column</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2">
<item>

View File

@ -344,6 +344,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
tree_view->setColumnHidden(COLUMN_TOTAL_TIMES, !UISettings::values.show_total_times);
item_model->setSortRole(GameListItemPath::SortRole);
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
@ -800,6 +801,7 @@ void GameList::RetranslateUI() {
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
item_model->setHeaderData(COLUMN_PLAY_TIME, Qt::Horizontal, tr("Play time"));
item_model->setHeaderData(COLUMN_TOTAL_TIMES, Qt::Horizontal, tr("Total times"));
}
void GameListSearchField::changeEvent(QEvent* event) {
@ -828,6 +830,7 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types);
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
tree_view->setColumnHidden(COLUMN_TOTAL_TIMES, !UISettings::values.show_total_times);
// Cancel any existing worker.
current_worker.reset();

View File

@ -16,9 +16,9 @@
#include "common/common_types.h"
#include "core/core.h"
#include "uisettings.h"
#include "sudachi/compatibility_list.h"
#include "sudachi/play_time_manager.h"
#include "uisettings.h"
namespace Core {
class System;
@ -77,6 +77,7 @@ public:
COLUMN_FILE_TYPE,
COLUMN_SIZE,
COLUMN_PLAY_TIME,
COLUMN_TOTAL_TIMES,
COLUMN_COUNT, // Number of columns
};

View File

@ -247,6 +247,31 @@ public:
}
};
/**
* GameListItem for Play Time values.
* This object stores the play time of a game in seconds, and its readable
* representation in minutes/hours
*/
class GameListItemTotalTimes : public GameListItem {
public:
static constexpr int TotalTimesRole = SortRole;
GameListItemTotalTimes() = default;
explicit GameListItemTotalTimes(const qulonglong times_count) {
setData(times_count, TotalTimesRole);
}
void setData(const QVariant& value, int role) override {
qulonglong times_count = value.toULongLong();
GameListItem::setData(PlayTime::ReadableTotalTimes(times_count), Qt::DisplayRole);
GameListItem::setData(value, TotalTimesRole);
}
bool operator<(const QStandardItem& other) const override {
return data(TotalTimesRole).toULongLong() < other.data(TotalTimesRole).toULongLong();
}
};
class GameListDir : public GameListItem {
public:
static constexpr int GameDirRole = Qt::UserRole + 2;

View File

@ -215,7 +215,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
new GameListItem(file_type_string),
new GameListItemSize(size),
new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)),
};
new GameListItemTotalTimes(play_time_manager.GetTotalTimes(program_id))};
const auto patch_versions = GetGameListCachedObject(
fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {

View File

@ -18,6 +18,7 @@ void PlayTimeManager::SetProgramId(u64 program_id) {
inline void PlayTimeManager::UpdateTimestamp() {
this->last_timestamp = std::chrono::steady_clock::now();
this->last_total_times = GetTotalTimes(this->running_program_id);
}
void PlayTimeManager::Start() {
@ -50,12 +51,12 @@ void PlayTimeManager::Save() {
std::chrono::steady_clock::duration(now - this->last_timestamp))
.count());
UpdateTimestamp();
if (!UpdatePlayTime(running_program_id, duration)) {
if (!UpdatePlayTime(running_program_id, duration, 1)) {
LOG_ERROR(Common, "Failed to update play time");
}
}
bool UpdatePlayTime(u64 program_id, u64 add_play_time) {
bool UpdatePlayTime(u64 program_id, u64 add_play_time, u64 add_total_times) {
std::vector<PlayTimeElement> play_time_elements;
if (!ReadPlayTimeFile(play_time_elements)) {
return false;
@ -63,9 +64,11 @@ bool UpdatePlayTime(u64 program_id, u64 add_play_time) {
const auto it = std::find(play_time_elements.begin(), play_time_elements.end(), program_id);
if (it == play_time_elements.end()) {
play_time_elements.push_back({.program_id = program_id, .play_time = add_play_time});
play_time_elements.push_back(
{.program_id = program_id, .play_time = add_play_time, .total_times = add_total_times});
} else {
play_time_elements.at(it - play_time_elements.begin()).play_time += add_play_time;
play_time_elements.at(it - play_time_elements.begin()).total_times += add_total_times;
}
if (!WritePlayTimeFile(play_time_elements)) {
return false;
@ -86,6 +89,19 @@ u64 GetPlayTime(u64 program_id) {
return play_time_elements.at(it - play_time_elements.begin()).play_time;
}
u64 GetTotalTimes(u64 program_id) {
std::vector<PlayTimeElement> play_time_elements;
if (!ReadPlayTimeFile(play_time_elements)) {
return 0;
}
const auto it = std::find(play_time_elements.begin(), play_time_elements.end(), program_id);
if (it == play_time_elements.end()) {
return 0;
}
return play_time_elements.at(it - play_time_elements.begin()).total_times;
}
bool PlayTimeManager::ResetProgramPlayTime(u64 program_id) {
std::vector<PlayTimeElement> play_time_elements;
@ -174,4 +190,8 @@ QString ReadablePlayTime(qulonglong time_seconds) {
.arg(QString::fromUtf8(units[unit]));
}
QString ReadableTotalTimes(qulonglong times_count) {
return QStringLiteral("%L1").arg((double)times_count, 0, 'f', 0);
}
} // namespace PlayTime

View File

@ -20,6 +20,7 @@ namespace PlayTime {
struct PlayTimeElement {
u64 program_id;
u64 play_time;
u64 total_times;
inline bool operator==(const PlayTimeElement& other) const {
return program_id == other.program_id;
@ -49,6 +50,7 @@ public:
private:
u64 running_program_id;
std::chrono::steady_clock::time_point last_timestamp;
u64 last_total_times;
std::jthread play_time_thread;
void AutoTimestamp(std::stop_token stop_token);
void Save();
@ -56,13 +58,15 @@ private:
std::optional<std::filesystem::path> GetCurrentUserPlayTimePath();
bool UpdatePlayTime(u64 program_id, u64 add_play_time);
bool UpdatePlayTime(u64 program_id, u64 add_play_time, u64 add_total_times);
[[nodiscard]] bool ReadPlayTimeFile(std::vector<PlayTimeElement>& out_play_time_elements);
[[nodiscard]] bool WritePlayTimeFile(const std::vector<PlayTimeElement>& play_time_elements);
u64 GetPlayTime(u64 program_id);
u64 GetTotalTimes(u64 program_id);
QString ReadablePlayTime(qulonglong time_seconds);
QString ReadableTotalTimes(qulonglong times_count);
} // namespace PlayTime

View File

@ -18,6 +18,7 @@ namespace {
struct PlayTimeElement {
ProgramId program_id;
PlayTime play_time;
TotalTimes total_times;
};
std::optional<std::filesystem::path> GetCurrentUserPlayTimePath(
@ -57,9 +58,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath(
return false;
}
for (const auto& [program_id, play_time] : elements) {
for (const auto& [program_id, play_time, total_times] : elements) {
if (program_id != 0) {
out_play_time_db[program_id] = play_time;
out_play_time_db[program_id] = {play_time, total_times};
}
}
}
@ -89,7 +90,7 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath(
for (auto& [program_id, play_time] : play_time_db) {
if (program_id != 0) {
elements.push_back(PlayTimeElement{program_id, play_time});
elements.push_back(PlayTimeElement{program_id, play_time.first, play_time.second});
}
}
@ -139,7 +140,8 @@ void PlayTimeManager::AutoTimestamp(std::stop_token stop_token) {
while (!stop_token.stop_requested()) {
Common::StoppableTimedWait(stop_token, 30s);
database[running_program_id] += GetDuration();
database[running_program_id].first += GetDuration();
database[running_program_id].second += 1;
Save();
}
}
@ -153,7 +155,16 @@ void PlayTimeManager::Save() {
u64 PlayTimeManager::GetPlayTime(u64 program_id) const {
auto it = database.find(program_id);
if (it != database.end()) {
return it->second;
return it->second.first;
} else {
return 0;
}
}
u64 PlayTimeManager::GetTotalTimes(u64 program_id) const {
auto it = database.find(program_id);
if (it != database.end()) {
return it->second.second;
} else {
return 0;
}
@ -179,4 +190,12 @@ QString ReadablePlayTime(qulonglong time_seconds) {
.arg(QString::fromUtf8(unit));
}
QString ReadableTotalTimes(qulonglong times_count) {
if (times_count == 0) {
return {};
}
return QStringLiteral("%L1").arg((double)times_count, 0, 'f', 0);
}
} // namespace PlayTime

View File

@ -19,7 +19,8 @@ namespace PlayTime {
using ProgramId = u64;
using PlayTime = u64;
using PlayTimeDatabase = std::map<ProgramId, PlayTime>;
using TotalTimes = u64;
using PlayTimeDatabase = std::map<ProgramId, std::pair<PlayTime, TotalTimes>>;
class PlayTimeManager {
public:
@ -30,6 +31,7 @@ public:
SUDACHI_NON_MOVEABLE(PlayTimeManager);
u64 GetPlayTime(u64 program_id) const;
u64 GetTotalTimes(u64 program_id) const;
void ResetProgramPlayTime(u64 program_id);
void SetProgramId(u64 program_id);
void Start();
@ -46,5 +48,6 @@ private:
};
QString ReadablePlayTime(qulonglong time_seconds);
QString ReadableTotalTimes(qulonglong times_count);
} // namespace PlayTime

View File

@ -212,6 +212,9 @@ struct Values {
// Play time
Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};
// Total times
Setting<bool> show_total_times{linkage, true, "show_total_times", Category::UiGameList};
bool configuration_applied;
bool reset_to_defaults;
bool shortcut_already_warned{false};