Compare commits

...

10 Commits

Author SHA1 Message Date
Aneesh Maganti 6fce063893
Merge d232c689cb into 795e78685f 2025-01-17 23:04:43 +00:00
OatmealDome 795e78685f
Merge pull request #13187 from OatmealDome/flatpak-appinfo
Flatpak: Use ScmRevGen to generate metainfo XML
2025-01-17 17:47:10 -05:00
JMC47 f65465b96c
Merge pull request #13250 from Charlese2/fix-sound-system-crash
GameINI: fix `Summoner: Goddess Reborn` sound system crash during transitions
2025-01-17 16:42:21 -05:00
Aneesh Maganti d232c689cb Add locks to game metadata 2025-01-14 15:50:54 -05:00
aminoa b54bf24a4e used Common::Event, new constructor for TimeTracker 2025-01-13 16:49:46 -05:00
Admiral H. Curtiss c3086f24c9 Track Time Played (Core and QT)
Co-authored-by: aminoa <28660350+aminoa@users.noreply.github.com>
2025-01-13 16:49:31 -05:00
OatmealDome 5578160880 Flatpak: Include link to the manifest within the metainfo for Flathub 2025-01-07 15:35:21 -05:00
OatmealDome 4fc259710f Flatpak: Use ScmRevGen to generate metainfo XML 2025-01-07 02:35:14 -05:00
OatmealDome 89016834cf Flatpak: Don't do an in-tree build 2025-01-07 02:34:45 -05:00
Charlese2 635f588690 fix `Summoner: Goddess Reborn` sound system crash during transitions 2024-12-27 18:33:54 -07:00
20 changed files with 279 additions and 23 deletions

View File

@ -30,6 +30,8 @@ if(GIT_FOUND)
ERROR_QUIET)
endif()
string(TIMESTAMP DOLPHIN_WC_BUILD_DATE "%Y-%m-%d" UTC)
# version number
set(DOLPHIN_VERSION_MAJOR "2412")
set(DOLPHIN_VERSION_MINOR "0")
@ -67,3 +69,7 @@ configure_source_file("Source/Core/Common/scmrev.h")
if(APPLE)
configure_source_file("Source/Core/VersionInfo.plist")
endif()
if(LINUX)
configure_source_file("Flatpak/org.DolphinEmu.dolphin-emu.metainfo.xml")
endif()

View File

@ -789,6 +789,13 @@ if(APPLE)
endif()
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Flatpak)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Flatpak/org.DolphinEmu.dolphin-emu.metainfo.xml)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Flatpak/org.DolphinEmu.dolphin-emu.metainfo.xml)
endif()
endif()
find_package(Git)
if(NOT GIT_FOUND)
set(GIT_EXECUTABLE "")
@ -796,7 +803,7 @@ endif()
add_custom_target(
dolphin_scmrev
${CMAKE_COMMAND} -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR} -DDISTRIBUTOR=${DISTRIBUTOR} -DDOLPHIN_DEFAULT_UPDATE_TRACK=${DOLPHIN_DEFAULT_UPDATE_TRACK} -DGIT_FOUND=${GIT_FOUND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DDOLPHIN_WC_REVISION=${DOLPHIN_WC_REVISION} -DDOLPHIN_WC_DESCRIBE=${DOLPHIN_WC_DESCRIBE} -DDOLPHIN_WC_BRANCH=${DOLPHIN_WC_BRANCH} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/ScmRevGen.cmake
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Source/Core/Common/scmrev.h" "${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist" "${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist"
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Source/Core/Common/scmrev.h" "${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist" "${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist" "${CMAKE_CURRENT_BINARY_DIR}/Flatpak/org.DolphinEmu.dolphin-emu.metainfo.xml"
VERBATIM
)

View File

@ -1,7 +1,48 @@
# GS2E78 - Summoner 2
[OnFrame]
# Add memory patches to be applied every frame here.
# This game will reinitialize the sound system buffers during transitions while
# sounds are still being played. There are still pointers in the sound list to
# sounds in those buffers when the buffers get cleared, so the game crashes with
# a null pointer dereference.
#
# This patch will clean up the pending sound list during sound system
# reinitialization before continuing to the sound system initialization call
# previously at 0x8017E338.
$Fix Sound System Crash
0x8017E338:dword:0x480954C9
0x80213800:dword:0x9421FFE0
0x80213804:dword:0x7C0802A6
0x80213808:dword:0x90010024
0x8021380C:dword:0x93E1001C
0x80213810:dword:0x93C10018
0x80213814:dword:0x93A10014
0x80213818:dword:0x7C7D1B78
0x8021381C:dword:0x4BF7A7D1
0x80213820:dword:0x83CDA0C0
0x80213824:dword:0x7C7F1B78
0x80213828:dword:0x48000010
0x8021382C:dword:0x807E0008
0x80213830:dword:0x4BF8C721
0x80213834:dword:0x83DE0000
0x80213838:dword:0x281E0000
0x8021383C:dword:0x4082FFF0
0x80213840:dword:0x38000000
0x80213844:dword:0x7FE3FB78
0x80213848:dword:0x900DA0C0
0x8021384C:dword:0x4BF7A7C9
0x80213850:dword:0x7FA3EB78
0x80213854:dword:0x4BF6A49D
0x80213858:dword:0x80010024
0x8021385C:dword:0x83E1001C
0x80213860:dword:0x83C10018
0x80213864:dword:0x83A10014
0x80213868:dword:0x7C0803A6
0x8021386C:dword:0x38210020
0x80213870:dword:0x4E800020
[OnFrame_Enabled]
$Fix Sound System Crash
[ActionReplay]
# Add action replay cheats here.

View File

@ -1,8 +0,0 @@
#!/usr/bin/env bash
DATE=$(git log -1 --pretty=%cd --date=iso8601 --date=format:'%Y-%m-%d')
sed -i -e "s/@DATE_PLACEHOLDER/${DATE}/" org.DolphinEmu.dolphin-emu.metainfo.xml
VERSION=$(git describe --tags | sed -E 's/^([0-9]+-[0-9]+).*/\1/')
sed -i -e "s/@VERSION_PLACEHOLDER/${VERSION}/" org.DolphinEmu.dolphin-emu.metainfo.xml

View File

@ -36,7 +36,7 @@
<id>dolphin-emu.desktop</id>
</provides>
<releases>
<release version="@VERSION_PLACEHOLDER" date="@DATE_PLACEHOLDER"/>
<release version="${DOLPHIN_WC_DESCRIBE}" date="${DOLPHIN_WC_BUILD_DATE}"/>
</releases>
<url type="homepage">https://dolphin-emu.org</url>
<url type="bugtracker">https://bugs.dolphin-emu.org/projects/emulator/issues</url>
@ -46,4 +46,7 @@
<url type="contact">https://dolphin-emu.org/docs/faq/#ive-got-idea-make-dolphin-better-how-should-i-tell</url>
<url type="vcs-browser">https://github.com/dolphin-emu/dolphin</url>
<url type="contribute">https://github.com/dolphin-emu/dolphin/blob/master/Contributing.md</url>
<custom>
<value key="flathub::manifest">https://github.com/dolphin-emu/dolphin/blob/${DOLPHIN_WC_REVISION}/Flatpak/org.DolphinEmu.dolphin-emu.yml</value>
</custom>
</component>

View File

@ -52,6 +52,7 @@ modules:
- name: dolphin-emu
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DENABLE_ALSA=OFF
@ -61,17 +62,13 @@ modules:
cleanup:
- /share/man
post-install:
- install -D -t ${FLATPAK_DEST}/bin/ dolphin-emu-wrapper
- "${FLATPAK_BUILDER_BUILDDIR}/Flatpak/fill_release_node.sh"
- install -Dm644 -t ${FLATPAK_DEST}/share/metainfo/ org.DolphinEmu.dolphin-emu.metainfo.xml
- install -D -t ${FLATPAK_DEST}/bin/ ../dolphin-emu-wrapper
- install -Dm644 -t ${FLATPAK_DEST}/share/metainfo/ Flatpak/org.DolphinEmu.dolphin-emu.metainfo.xml
- desktop-file-edit --set-key=Exec --set-value='/app/bin/dolphin-emu-wrapper'
/app/share/applications/dolphin-emu.desktop
sources:
- type: dir
path: ..
- type: file
path: org.DolphinEmu.dolphin-emu.metainfo.xml.in
dest-filename: org.DolphinEmu.dolphin-emu.metainfo.xml
- type: script
commands:
- |

View File

@ -541,6 +541,8 @@ add_library(core
SysConf.h
System.cpp
System.h
TimePlayed.cpp
TimePlayed.h
TitleDatabase.cpp
TitleDatabase.h
WC24PatchEngine.cpp

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

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

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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" />

View File

@ -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},
};

View File

@ -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:

View File

@ -58,6 +58,7 @@ public:
FileFormat,
BlockSize,
Compression,
TimePlayed,
Tags,
Count,
};

View File

@ -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);