Compare commits
4 Commits
6fce063893
...
924b17371d
Author | SHA1 | Date |
---|---|---|
Aneesh Maganti | 924b17371d | |
Aneesh Maganti | d232c689cb | |
aminoa | b54bf24a4e | |
Admiral H. Curtiss | c3086f24c9 |
|
@ -541,6 +541,8 @@ add_library(core
|
|||
SysConf.h
|
||||
System.cpp
|
||||
System.h
|
||||
TimePlayed.cpp
|
||||
TimePlayed.h
|
||||
TitleDatabase.cpp
|
||||
TitleDatabase.h
|
||||
WC24PatchEngine.cpp
|
||||
|
|
|
@ -459,6 +459,8 @@ const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE{{System::Main, "GameList", "Col
|
|||
false};
|
||||
const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION{{System::Main, "GameList", "ColumnCompression"},
|
||||
false};
|
||||
const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED{{System::Main, "GameList", "ColumnTimePlayed"},
|
||||
true};
|
||||
const Info<bool> MAIN_GAMELIST_COLUMN_TAGS{{System::Main, "GameList", "ColumnTags"}, false};
|
||||
|
||||
// Main.FifoPlayer
|
||||
|
|
|
@ -295,6 +295,7 @@ extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_SIZE;
|
|||
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_FORMAT;
|
||||
extern const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE;
|
||||
extern const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION;
|
||||
extern const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED;
|
||||
extern const Info<bool> MAIN_GAMELIST_COLUMN_TAGS;
|
||||
|
||||
// Main.FifoPlayer
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
@ -98,6 +99,42 @@ void SConfig::LoadSettings()
|
|||
Config::Load();
|
||||
}
|
||||
|
||||
const std::string& SConfig::GetGameID() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_metadata_lock);
|
||||
return m_game_id;
|
||||
}
|
||||
|
||||
const std::string& SConfig::GetGameTDBID() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_metadata_lock);
|
||||
return m_gametdb_id;
|
||||
}
|
||||
|
||||
const std::string& SConfig::GetTitleName() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_metadata_lock);
|
||||
return m_title_name;
|
||||
}
|
||||
|
||||
const std::string& SConfig::GetTitleDescription() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_metadata_lock);
|
||||
return m_title_description;
|
||||
}
|
||||
|
||||
u64 SConfig::GetTitleID() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_metadata_lock);
|
||||
return m_title_id;
|
||||
}
|
||||
|
||||
u16 SConfig::GetRevision() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_metadata_lock);
|
||||
return m_revision;
|
||||
}
|
||||
|
||||
void SConfig::ResetRunningGameMetadata()
|
||||
{
|
||||
SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
@ -58,15 +59,16 @@ struct SConfig
|
|||
std::string m_strSRAM;
|
||||
|
||||
std::string m_debugger_game_id;
|
||||
|
||||
// TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped.
|
||||
bool m_disc_booted_from_game_list = false;
|
||||
|
||||
const std::string& GetGameID() const { return m_game_id; }
|
||||
const std::string& GetGameTDBID() const { return m_gametdb_id; }
|
||||
const std::string& GetTitleName() const { return m_title_name; }
|
||||
const std::string& GetTitleDescription() const { return m_title_description; }
|
||||
u64 GetTitleID() const { return m_title_id; }
|
||||
u16 GetRevision() const { return m_revision; }
|
||||
const std::string& GetGameID() const;
|
||||
const std::string& GetGameTDBID() const;
|
||||
const std::string& GetTitleName() const;
|
||||
const std::string& GetTitleDescription() const;
|
||||
u64 GetTitleID() const;
|
||||
u16 GetRevision() const;
|
||||
void ResetRunningGameMetadata();
|
||||
void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition);
|
||||
void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform);
|
||||
|
@ -114,6 +116,7 @@ private:
|
|||
u64 title_id, u16 revision, DiscIO::Region region);
|
||||
|
||||
static SConfig* m_Instance;
|
||||
mutable std::mutex m_metadata_lock;
|
||||
|
||||
std::string m_game_id;
|
||||
std::string m_gametdb_id;
|
||||
|
|
|
@ -10,16 +10,20 @@
|
|||
#include "AudioCommon/AudioCommon.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/CPUThreadConfigCallback.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/PowerPC/GDBStub.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/TimePlayed.h"
|
||||
#include "VideoCommon/Fifo.h"
|
||||
|
||||
namespace CPU
|
||||
{
|
||||
|
||||
CPUManager::CPUManager(Core::System& system) : m_system(system)
|
||||
{
|
||||
}
|
||||
|
@ -63,6 +67,34 @@ void CPUManager::ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock)
|
|||
}
|
||||
}
|
||||
|
||||
void CPUManager::StartTimePlayedTimer()
|
||||
{
|
||||
// Steady clock for greater accuracy of timing
|
||||
std::chrono::steady_clock timer;
|
||||
auto prev_time = timer.now();
|
||||
|
||||
while (true)
|
||||
{
|
||||
const std::string game_id = SConfig::GetInstance().GetGameID();
|
||||
TimePlayed time_played(game_id);
|
||||
auto curr_time = timer.now();
|
||||
|
||||
// Check that emulation is not paused
|
||||
if (m_state == State::Running)
|
||||
{
|
||||
auto diff_time = std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - prev_time);
|
||||
time_played.AddTime(diff_time);
|
||||
}
|
||||
|
||||
prev_time = curr_time;
|
||||
|
||||
if (m_state == State::PowerDown)
|
||||
return;
|
||||
|
||||
m_time_played_finish_sync.WaitFor(std::chrono::seconds(30));
|
||||
}
|
||||
}
|
||||
|
||||
void CPUManager::Run()
|
||||
{
|
||||
auto& power_pc = m_system.GetPowerPC();
|
||||
|
@ -71,6 +103,9 @@ void CPUManager::Run()
|
|||
// We can't rely on PowerPC::Init doing it, since it's called from EmuThread.
|
||||
PowerPC::RoundingModeUpdated(power_pc.GetPPCState());
|
||||
|
||||
// Start a separate time tracker thread
|
||||
auto timing = std::thread(&CPUManager::StartTimePlayedTimer, this);
|
||||
|
||||
std::unique_lock state_lock(m_state_change_lock);
|
||||
while (m_state != State::PowerDown)
|
||||
{
|
||||
|
@ -165,6 +200,11 @@ void CPUManager::Run()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// m_timer_finish.notify_one();
|
||||
m_time_played_finish_sync.Set();
|
||||
timing.join();
|
||||
|
||||
state_lock.unlock();
|
||||
Host_UpdateDisasmDialog();
|
||||
}
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Event.h>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace Common
|
||||
|
||||
{
|
||||
class Event;
|
||||
}
|
||||
|
@ -102,6 +104,7 @@ public:
|
|||
private:
|
||||
void FlushStepSyncEventLocked();
|
||||
void ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock);
|
||||
void StartTimePlayedTimer();
|
||||
void RunAdjacentSystems(bool running);
|
||||
bool SetStateLocked(State s);
|
||||
|
||||
|
@ -133,6 +136,7 @@ private:
|
|||
bool m_state_cpu_step_instruction = false;
|
||||
Common::Event* m_state_cpu_step_instruction_sync = nullptr;
|
||||
std::queue<std::function<void()>> m_pending_jobs;
|
||||
Common::Event m_time_played_finish_sync;
|
||||
|
||||
Core::System& m_system;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "TimePlayed.h"
|
||||
|
||||
// used for QT interface - general access to time played for games
|
||||
TimePlayed::TimePlayed()
|
||||
{
|
||||
m_game_id = "None";
|
||||
ini_path = File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini";
|
||||
ini.Load(ini_path);
|
||||
time_list = ini.GetOrCreateSection("Time Played");
|
||||
}
|
||||
|
||||
void FilterUnsafeCharacters(std::string& game_id)
|
||||
{
|
||||
const std::string forbiddenChars = "\\/:?\"<>|";
|
||||
for (auto& chr : game_id)
|
||||
{
|
||||
if (forbiddenChars.find(chr) != std::string::npos)
|
||||
{
|
||||
chr = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimePlayed::TimePlayed(std::string game_id)
|
||||
{
|
||||
// filter for unsafe characters
|
||||
FilterUnsafeCharacters(game_id);
|
||||
|
||||
m_game_id = game_id;
|
||||
ini_path = File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini";
|
||||
ini.Load(ini_path);
|
||||
time_list = ini.GetOrCreateSection("Time Played");
|
||||
}
|
||||
|
||||
void TimePlayed::AddTime(std::chrono::milliseconds time_emulated)
|
||||
{
|
||||
if (m_game_id == "None")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
u64 previous_time;
|
||||
time_list->Get(m_game_id, &previous_time);
|
||||
time_list->Set(m_game_id, previous_time + u64(time_emulated.count()));
|
||||
ini.Save(ini_path);
|
||||
}
|
||||
|
||||
u64 TimePlayed::GetTimePlayed()
|
||||
{
|
||||
if (m_game_id == "None")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 previous_time;
|
||||
time_list->Get(m_game_id, &previous_time);
|
||||
return previous_time;
|
||||
}
|
||||
|
||||
u64 TimePlayed::GetTimePlayed(std::string game_id)
|
||||
{
|
||||
FilterUnsafeCharacters(game_id);
|
||||
u64 previous_time;
|
||||
time_list->Get(game_id, &previous_time);
|
||||
return previous_time;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IniFile.h"
|
||||
|
||||
class TimePlayed
|
||||
{
|
||||
public:
|
||||
TimePlayed();
|
||||
TimePlayed(std::string game_id);
|
||||
|
||||
void AddTime(std::chrono::milliseconds time_emulated);
|
||||
|
||||
u64 GetTimePlayed();
|
||||
u64 GetTimePlayed(std::string game_id);
|
||||
|
||||
private:
|
||||
std::string m_game_id;
|
||||
Common::IniFile ini;
|
||||
std::string ini_path;
|
||||
|
||||
Common::IniFile::Section* time_list;
|
||||
};
|
|
@ -459,6 +459,7 @@
|
|||
<ClInclude Include="Core\SyncIdentifier.h" />
|
||||
<ClInclude Include="Core\SysConf.h" />
|
||||
<ClInclude Include="Core\System.h" />
|
||||
<ClInclude Include="Core\TimePlayed.h" />
|
||||
<ClInclude Include="Core\TitleDatabase.h" />
|
||||
<ClInclude Include="Core\WC24PatchEngine.h" />
|
||||
<ClInclude Include="Core\WiiRoot.h" />
|
||||
|
@ -1125,6 +1126,7 @@
|
|||
<ClCompile Include="Core\State.cpp" />
|
||||
<ClCompile Include="Core\SysConf.cpp" />
|
||||
<ClCompile Include="Core\System.cpp" />
|
||||
<ClCompile Include="Core\TimePlayed.cpp" />
|
||||
<ClCompile Include="Core\TitleDatabase.cpp" />
|
||||
<ClCompile Include="Core\WiiRoot.cpp" />
|
||||
<ClCompile Include="Core\WiiUtils.cpp" />
|
||||
|
|
|
@ -208,6 +208,7 @@ void GameList::MakeListView()
|
|||
SetResizeMode(Column::FileFormat, Mode::Fixed);
|
||||
SetResizeMode(Column::BlockSize, Mode::Fixed);
|
||||
SetResizeMode(Column::Compression, Mode::Fixed);
|
||||
SetResizeMode(Column::TimePlayed, Mode::Interactive);
|
||||
SetResizeMode(Column::Tags, Mode::Interactive);
|
||||
|
||||
// Cells have 3 pixels of padding, so the width of these needs to be image width + 6. Banners
|
||||
|
@ -273,6 +274,7 @@ void GameList::UpdateColumnVisibility()
|
|||
SetVisiblity(Column::FileFormat, Config::Get(Config::MAIN_GAMELIST_COLUMN_FILE_FORMAT));
|
||||
SetVisiblity(Column::BlockSize, Config::Get(Config::MAIN_GAMELIST_COLUMN_BLOCK_SIZE));
|
||||
SetVisiblity(Column::Compression, Config::Get(Config::MAIN_GAMELIST_COLUMN_COMPRESSION));
|
||||
SetVisiblity(Column::TimePlayed, Config::Get(Config::MAIN_GAMELIST_COLUMN_TIME_PLAYED));
|
||||
SetVisiblity(Column::Tags, Config::Get(Config::MAIN_GAMELIST_COLUMN_TAGS));
|
||||
}
|
||||
|
||||
|
@ -1005,6 +1007,7 @@ void GameList::OnColumnVisibilityToggled(const QString& row, bool visible)
|
|||
{tr("File Format"), Column::FileFormat},
|
||||
{tr("Block Size"), Column::BlockSize},
|
||||
{tr("Compression"), Column::Compression},
|
||||
{tr("Time Played"), Column::TimePlayed},
|
||||
{tr("Tags"), Column::Tags},
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QRegularExpression>
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/TimePlayed.h"
|
||||
|
||||
#include "DiscIO/Enums.h"
|
||||
|
||||
|
@ -57,6 +58,7 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
|
|||
return QVariant();
|
||||
|
||||
const UICommon::GameFile& game = *m_games[index.row()];
|
||||
TimePlayed timer;
|
||||
|
||||
switch (static_cast<Column>(index.column()))
|
||||
{
|
||||
|
@ -187,6 +189,22 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
|
|||
return compression.isEmpty() ? tr("No Compression") : compression;
|
||||
}
|
||||
break;
|
||||
case Column::TimePlayed:
|
||||
if (role == Qt::DisplayRole || role == SORT_ROLE)
|
||||
{
|
||||
std::string game_id = game.GetGameID();
|
||||
std::chrono::milliseconds total_time(timer.GetTimePlayed(game_id));
|
||||
std::chrono::minutes total_minutes =
|
||||
std::chrono::duration_cast<std::chrono::minutes>(total_time);
|
||||
std::chrono::hours total_hours = std::chrono::duration_cast<std::chrono::hours>(total_time);
|
||||
|
||||
// i18n: A time displayed as hours and minutes
|
||||
QString formatted_time = tr("%1h %2m")
|
||||
.arg(total_hours.count())
|
||||
.arg(total_minutes.count() - total_hours.count() * 60);
|
||||
return formatted_time;
|
||||
}
|
||||
break;
|
||||
case Column::Tags:
|
||||
if (role == Qt::DisplayRole || role == SORT_ROLE)
|
||||
{
|
||||
|
@ -232,6 +250,8 @@ QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int
|
|||
return tr("Block Size");
|
||||
case Column::Compression:
|
||||
return tr("Compression");
|
||||
case Column::TimePlayed:
|
||||
return tr("Time Played");
|
||||
case Column::Tags:
|
||||
return tr("Tags");
|
||||
default:
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
FileFormat,
|
||||
BlockSize,
|
||||
Compression,
|
||||
TimePlayed,
|
||||
Tags,
|
||||
Count,
|
||||
};
|
||||
|
|
|
@ -701,6 +701,7 @@ void MenuBar::AddListColumnsMenu(QMenu* view_menu)
|
|||
{tr("File Format"), &Config::MAIN_GAMELIST_COLUMN_FILE_FORMAT},
|
||||
{tr("Block Size"), &Config::MAIN_GAMELIST_COLUMN_BLOCK_SIZE},
|
||||
{tr("Compression"), &Config::MAIN_GAMELIST_COLUMN_COMPRESSION},
|
||||
{tr("Time Played"), &Config::MAIN_GAMELIST_COLUMN_TIME_PLAYED},
|
||||
{tr("Tags"), &Config::MAIN_GAMELIST_COLUMN_TAGS}};
|
||||
|
||||
QActionGroup* column_group = new QActionGroup(this);
|
||||
|
|
Loading…
Reference in New Issue