diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 3d06281e..30dab1d4 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -530,7 +530,8 @@ namespace CafeSystem { // entries in this list are ordered by initialization order. Shutdown in reverse order iosu::kernel::GetModule(), - iosu::fpd::GetModule() + iosu::fpd::GetModule(), + iosu::pdm::GetModule(), }; // initialize all subsystems which are persistent and don't depend on a game running @@ -571,7 +572,6 @@ namespace CafeSystem iosu::iosuAcp_init(); iosu::boss_init(); iosu::nim::Initialize(); - iosu::pdm::Initialize(); iosu::odm::Initialize(); // init Cafe OS avm::Initialize(); @@ -840,7 +840,6 @@ namespace CafeSystem coreinit::OSSchedulerBegin(3); else coreinit::OSSchedulerBegin(1); - iosu::pdm::StartTrackingTime(GetForegroundTitleId()); } void LaunchForegroundTitle() @@ -970,8 +969,6 @@ namespace CafeSystem RPLLoader_ResetState(); for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) (*it)->TitleStop(); - // stop time tracking - iosu::pdm::Stop(); // reset Cemu subsystems PPCRecompiler_Shutdown(); GraphicPack2::Reset(); diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.cpp b/src/Cafe/IOSU/PDM/iosu_pdm.cpp index 45b4a1d8..e54529a9 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.cpp +++ b/src/Cafe/IOSU/PDM/iosu_pdm.cpp @@ -1,4 +1,5 @@ #include "iosu_pdm.h" +#include "Cafe/CafeSystem.h" #include "config/ActiveSettings.h" #include "Common/FileStream.h" #include "util/helpers/Semaphore.h" @@ -17,7 +18,8 @@ namespace iosu { namespace pdm { - std::mutex sDiaryLock; + std::recursive_mutex sPlaystatsLock; + std::recursive_mutex sDiaryLock; fs::path GetPDFile(const char* filename) { @@ -80,14 +82,16 @@ namespace iosu static_assert((NUM_PLAY_STATS_ENTRIES * sizeof(PlayStatsEntry)) == 0x1400); } - void LoadPlaystats() + void OpenPlaystats() { + std::unique_lock _l(sPlaystatsLock); PlayStats.numEntries = 0; for (size_t i = 0; i < NUM_PLAY_STATS_ENTRIES; i++) { auto& e = PlayStats.entry[i]; memset(&e, 0, sizeof(PlayStatsEntry)); } + cemu_assert_debug(!PlayStats.fs); PlayStats.fs = FileStream::openFile2(GetPDFile("PlayStats.dat"), true); if (!PlayStats.fs) { @@ -98,18 +102,39 @@ namespace iosu { delete PlayStats.fs; PlayStats.fs = nullptr; - cemuLog_log(LogType::Force, "PlayStats.dat malformed"); + cemuLog_log(LogType::Force, "PlayStats.dat malformed. Time tracking wont be used"); // dont delete the existing file in case it could still be salvaged (todo) and instead just dont track play time return; } + PlayStats.numEntries = 0; PlayStats.fs->readData(&PlayStats.numEntries, sizeof(uint32be)); if (PlayStats.numEntries > NUM_PLAY_STATS_ENTRIES) PlayStats.numEntries = NUM_PLAY_STATS_ENTRIES; PlayStats.fs->readData(PlayStats.entry, NUM_PLAY_STATS_ENTRIES * 20); } + void ClosePlaystats() + { + std::unique_lock _l(sPlaystatsLock); + if (PlayStats.fs) + { + delete PlayStats.fs; + PlayStats.fs = nullptr; + } + } + + void UnloadPlaystats() + { + std::unique_lock _l(sPlaystatsLock); + cemu_assert_debug(!PlayStats.fs); // unloading expects that file is closed + PlayStats.numEntries = 0; + for(auto& it : PlayStats.entry) + it = PlayStatsEntry{}; + } + PlayStatsEntry* PlayStats_GetEntry(uint64 titleId) { + std::unique_lock _l(sPlaystatsLock); uint32be titleIdHigh = (uint32)(titleId>>32); uint32be titleIdLow = (uint32)(titleId & 0xFFFFFFFF); size_t numEntries = PlayStats.numEntries; @@ -121,7 +146,7 @@ namespace iosu return nullptr; } - void PlayStats_WriteEntry(PlayStatsEntry* entry, bool writeEntryCount = false) + void PlayStats_WriteEntryNoLock(PlayStatsEntry* entry, bool writeEntryCount = false) { if (!PlayStats.fs) return; @@ -141,8 +166,15 @@ namespace iosu } } + void PlayStats_WriteEntry(PlayStatsEntry* entry, bool writeEntryCount = false) + { + std::unique_lock _l(sPlaystatsLock); + PlayStats_WriteEntryNoLock(entry, writeEntryCount); + } + PlayStatsEntry* PlayStats_CreateEntry(uint64 titleId) { + std::unique_lock _l(sPlaystatsLock); bool entryCountChanged = false; PlayStatsEntry* newEntry; if(PlayStats.numEntries < NUM_PLAY_STATS_ENTRIES) @@ -168,7 +200,7 @@ namespace iosu newEntry->numTimesLaunched = 1; newEntry->totalMinutesPlayed = 0; newEntry->ukn12 = 0; - PlayStats_WriteEntry(newEntry, entryCountChanged); + PlayStats_WriteEntryNoLock(newEntry, entryCountChanged); return newEntry; } @@ -176,6 +208,7 @@ namespace iosu // if it does not exist it creates a new entry with first and last played set to today PlayStatsEntry* PlayStats_BeginNewTracking(uint64 titleId) { + std::unique_lock _l(sPlaystatsLock); PlayStatsEntry* entry = PlayStats_GetEntry(titleId); if (entry) { @@ -189,11 +222,12 @@ namespace iosu void PlayStats_CountAdditionalMinutes(PlayStatsEntry* entry, uint32 additionalMinutes) { + std::unique_lock _l(sPlaystatsLock); if (additionalMinutes == 0) return; entry->totalMinutesPlayed += additionalMinutes; entry->mostRecentDayIndex = GetTodaysDayIndex(); - PlayStats_WriteEntry(entry); + PlayStats_WriteEntryNoLock(entry); } struct PlayDiaryHeader @@ -218,6 +252,7 @@ namespace iosu void CreatePlayDiary() { MakeDirectory(); + cemu_assert_debug(!PlayDiaryData.fs); PlayDiaryData.fs = FileStream::createFile2(GetPDFile("PlayDiary.dat")); if (!PlayDiaryData.fs) { @@ -230,7 +265,7 @@ namespace iosu PlayDiaryData.fs->writeData(&PlayDiaryData.header, sizeof(PlayDiaryHeader)); } - void LoadPlayDiary() + void OpenPlayDiary() { std::unique_lock _lock(sDiaryLock); cemu_assert_debug(!PlayDiaryData.fs); @@ -268,6 +303,26 @@ namespace iosu } } + void ClosePlayDiary() + { + std::unique_lock _lock(sDiaryLock); + if (PlayDiaryData.fs) + { + delete PlayDiaryData.fs; + PlayDiaryData.fs = nullptr; + } + } + + void UnloadDiaryData() + { + std::unique_lock _lock(sDiaryLock); + cemu_assert_debug(!PlayDiaryData.fs); // unloading expects that file is closed + PlayDiaryData.header.readIndex = 0; + PlayDiaryData.header.writeIndex = 0; + for (auto& it : PlayDiaryData.entry) + it = PlayDiaryEntry{}; + } + uint32 GetDiaryEntries(uint8 accountSlot, PlayDiaryEntry* diaryEntries, uint32 maxEntries) { std::unique_lock _lock(sDiaryLock); @@ -352,25 +407,59 @@ namespace iosu } } - void Initialize() + class : public ::IOSUModule { - // todo - add support for per-account handling - LoadPlaystats(); - LoadPlayDiary(); - } - - void StartTrackingTime(uint64 titleId) - { - sPDMRequestExitThread = false; - sPDMTimeTrackingThread = std::thread(TimeTrackingThread, titleId); - } + void PDMLoadAll() + { + OpenPlaystats(); + OpenPlayDiary(); + } - void Stop() + void PDMUnloadAll() + { + UnloadPlaystats(); + UnloadDiaryData(); + } + + void PDMCloseAll() + { + ClosePlaystats(); + ClosePlayDiary(); + } + + void SystemLaunch() override + { + // todo - add support for per-account handling + PDMLoadAll(); + PDMCloseAll(); // close the files again, user may mess with MLC files or change MLC path while no game is running + } + void SystemExit() override + { + PDMCloseAll(); + PDMUnloadAll(); + } + void TitleStart() override + { + // reload data and keep files open + PDMUnloadAll(); + PDMLoadAll(); + auto titleId = CafeSystem::GetForegroundTitleId(); + sPDMRequestExitThread = false; + sPDMTimeTrackingThread = std::thread(TimeTrackingThread, titleId); + } + void TitleStop() override + { + sPDMRequestExitThread.store(true); + sPDMSem.increment(); + if(sPDMTimeTrackingThread.joinable()) + sPDMTimeTrackingThread.join(); + PDMCloseAll(); + } + }sIOSUModuleNNPDM; + + IOSUModule* GetModule() { - sPDMRequestExitThread.store(true); - sPDMSem.increment(); - if(sPDMTimeTrackingThread.joinable()) - sPDMTimeTrackingThread.join(); + return static_cast(&sIOSUModuleNNPDM); } }; diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.h b/src/Cafe/IOSU/PDM/iosu_pdm.h index fbafbc02..0dd8a39d 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.h +++ b/src/Cafe/IOSU/PDM/iosu_pdm.h @@ -1,13 +1,10 @@ #pragma once +#include "Cafe/IOSU/iosu_types_common.h" namespace iosu { namespace pdm { - void Initialize(); - void StartTrackingTime(uint64 titleId); - void Stop(); - inline constexpr size_t NUM_PLAY_STATS_ENTRIES = 256; inline constexpr size_t NUM_PLAY_DIARY_ENTRIES_MAX = 18250; // 0x474A @@ -34,5 +31,7 @@ namespace iosu }; bool GetStatForGamelist(uint64 titleId, GameListStat& stat); + + IOSUModule* GetModule(); }; }; \ No newline at end of file