From 955ca0e7bc809ab1ae3a97c431bb4c3341194b55 Mon Sep 17 00:00:00 2001 From: OpenSteam001 <248184252+OpenSteam001@users.noreply.github.com> Date: Fri, 29 May 2026 11:13:36 +0800 Subject: [PATCH 1/3] Enable Steam library "Date Added" sorting via .lua file mtime --- src/Hook/Hooks_Package.cpp | 3 ++- src/Hook/Hooks_SteamUI.cpp | 22 +++++++++++++++++++--- src/Steam/Structs.h | 4 +++- src/Utils/LuaConfig.cpp | 35 +++++++++++++++++++++++++++++++++++ src/Utils/LuaConfig.h | 3 ++- 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/Hook/Hooks_Package.cpp b/src/Hook/Hooks_Package.cpp index 2cd33f2..8bfde27 100644 --- a/src/Hook/Hooks_Package.cpp +++ b/src/Hook/Hooks_Package.cpp @@ -117,10 +117,11 @@ namespace { TryProcessPendingLicenseRefresh(); // LOG_PACKAGE_TRACE("CheckAppOwnership: AppId={} result={} {}", appId, result, pOwn->DebugString()); - if (LuaConfig::HasDepot(appId)) { + if (LuaConfig::HasDepot(appId,false)) { if (result && pOwn->ExistInPackageNums > 1) { // Actually owned — record so HasDepot excludes it going forward LuaConfig::MarkOwned(appId); + pOwn->ReleaseState = EAppReleaseState::Released; } else { pOwn->PackageId = kInjectedPackageId; pOwn->ReleaseState = EAppReleaseState::Released; diff --git a/src/Hook/Hooks_SteamUI.cpp b/src/Hook/Hooks_SteamUI.cpp index 691c2da..b03810b 100644 --- a/src/Hook/Hooks_SteamUI.cpp +++ b/src/Hook/Hooks_SteamUI.cpp @@ -5,14 +5,23 @@ #include "steam_messages.pb.h" #include "Utils/VehCommon.h" -#include -#include - namespace { CAPTURE_THIS_FUNC(GetAppByID, CSteamApp*, g_pController,void* pThis, AppId_t appId, bool bCreate); CAPTURE_THIS_FUNC(MarkAppChange,void*,g_pAppChangeSource,void* pThis,AppId_t appId, EAppChangeFlags changeFlags); + HOOK_FUNC(FillInAppOverview,void*,void* pThis,void* pAppOverview,CSteamApp* pApp) + { + if (pApp && LuaConfig::HasDepot(pApp->nAppID,false)) { + uint32_t t = LuaConfig::GetPurchaseTime(pApp->nAppID); + if(t) { + pApp->PurchasedTime = t; + LOG_STEAMUI_TRACE("FillInAppOverview: set PurchasedTime={} for appId={}", + pApp->PurchasedTime, pApp->nAppID); + } + } + return oFillInAppOverview(pThis, pAppOverview, pApp); + } } namespace Hooks_SteamUI { @@ -22,16 +31,23 @@ namespace Hooks_SteamUI { ARM_CAPTURE_U(MarkAppChange); HOOK_BEGIN(); + INSTALL_HOOK_U(FillInAppOverview); HOOK_END(); } void Uninstall() { UNHOOK_BEGIN(); + UNINSTALL_HOOK(FillInAppOverview); UNHOOK_END(); } void RemoveAppAndSendChange(AppId_t appId) { + // Only attempt to remove if LuaConfig is tracking this appId + if(!LuaConfig::HasDepot(appId)){ + LOG_STEAMUI_WARN("RemoveAppAndSendChange: appId={} not tracked in LuaConfig, skipping", appId); + return; + } if(CAPTURE_READY(GetAppByID) && CAPTURE_READY(MarkAppChange)) { CSteamApp* pApp = oGetAppByID(g_pController, appId, false); if(pApp) { diff --git a/src/Steam/Structs.h b/src/Steam/Structs.h index 7a61473..3974e76 100644 --- a/src/Steam/Structs.h +++ b/src/Steam/Structs.h @@ -333,6 +333,7 @@ struct CAppData } }; +#pragma pack(push,1) struct CSteamApp { void** fptr; @@ -345,4 +346,5 @@ struct CSteamApp uint32 _unknown3; uint64 SteamID; uint32 PurchasedTime; -}; \ No newline at end of file +}; +#pragma pack(pop) \ No newline at end of file diff --git a/src/Utils/LuaConfig.cpp b/src/Utils/LuaConfig.cpp index 3345097..dbbb9d0 100644 --- a/src/Utils/LuaConfig.cpp +++ b/src/Utils/LuaConfig.cpp @@ -27,6 +27,11 @@ namespace LuaConfig{ static std::unordered_map> g_fileDepots; // Reference count: how many files provide each depot. static std::unordered_map g_depotRefCount; + // mtime (unix epoch seconds) of each parsed .lua file, captured at ParseFile entry. + static std::unordered_map g_fileMtime; + // Per-appId purchase time: max(mtime) across every file that currently contributes it. + // Simple variant: never lowered on UnloadFile unless the refcount drops to zero. + static std::unordered_map g_purchaseTime; // Depot IDs removed by UnloadFile / added by ParseFile, consumed by NotifyLicenseChanged. static std::vector g_pendingRemovals; static std::vector g_pendingAdditions; @@ -174,6 +179,14 @@ namespace LuaConfig{ if (g_fileDepots[g_currentFile].insert(DepotId).second) { if (++g_depotRefCount[DepotId] == 1) g_pendingAdditions.push_back(DepotId); + + // Update the appId's purchase time with the current file's mtime, + // keeping the maximum across every contributing file. + auto mtIt = g_fileMtime.find(g_currentFile); + if (mtIt != g_fileMtime.end()) { + uint32_t mt = mtIt->second; + auto& slot = g_purchaseTime[DepotId]; + if (mt > slot) slot = mt; } } @@ -453,6 +466,11 @@ namespace LuaConfig{ return kDefaultStatSteamId; } + uint32_t GetPurchaseTime(AppId_t AppId) { + auto it = g_purchaseTime.find(AppId); + return it != g_purchaseTime.end() ? it->second : 0; + } + const std::unordered_map& GetManifestOverrides() { return ManifestOverrides; } @@ -568,12 +586,14 @@ namespace LuaConfig{ if (--g_depotRefCount[id] == 0) { g_depotRefCount.erase(id); DepotKeySet.erase(id); + g_purchaseTime.erase(id); g_pendingRemovals.push_back(id); } } LOG_PACKAGE_INFO("UnloadFile: removed {} depots from {}", it->second.size(), filePath); g_fileDepots.erase(it); + g_fileMtime.erase(filePath); } std::vector TakePendingRemovals() { @@ -602,6 +622,21 @@ namespace LuaConfig{ LOG_WARN("ParseFile: failed to open {}", path.filename().string()); return; } + + // Capture the file's last-modified time (unix epoch, seconds) so + // lua_addappid can stamp it onto every appId this file contributes. + // Portable conversion that does not require C++20 clock_cast. + { + std::error_code ec; + auto ftime = std::filesystem::last_write_time(path, ec); + uint32_t mtime = 0; + if (!ec) { + auto sctp = std::chrono::time_point_cast( + ftime - decltype(ftime)::clock::now() + std::chrono::system_clock::now()); + mtime = static_cast(std::chrono::system_clock::to_time_t(sctp)); + } + g_fileMtime[filePath] = mtime; + } std::string chunk, line; int lineNo = 0; diff --git a/src/Utils/LuaConfig.h b/src/Utils/LuaConfig.h index ae00478..a5b96cb 100644 --- a/src/Utils/LuaConfig.h +++ b/src/Utils/LuaConfig.h @@ -14,7 +14,8 @@ namespace LuaConfig{ uint64_t GetAccessToken(AppId_t appId); uint64_t GetStatSteamId(AppId_t appId); bool pinApp(AppId_t appId); - + uint32_t GetPurchaseTime(AppId_t appId); + struct ManifestOverride { uint64_t gid; uint64_t size; From 52d1181a197ec319fd79a5cb3cf5ab315b4d9d2a Mon Sep 17 00:00:00 2001 From: OpenSteam001 <248184252+OpenSteam001@users.noreply.github.com> Date: Fri, 29 May 2026 11:22:52 +0800 Subject: [PATCH 2/3] Fix missing closing brace in LuaConfig namespace --- src/Utils/LuaConfig.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Utils/LuaConfig.cpp b/src/Utils/LuaConfig.cpp index dbbb9d0..9bc76cc 100644 --- a/src/Utils/LuaConfig.cpp +++ b/src/Utils/LuaConfig.cpp @@ -187,6 +187,7 @@ namespace LuaConfig{ uint32_t mt = mtIt->second; auto& slot = g_purchaseTime[DepotId]; if (mt > slot) slot = mt; + } } } From e254afeced89f6fa00f4739e5c6aa82f32763de9 Mon Sep 17 00:00:00 2001 From: OpenSteam001 <248184252+OpenSteam001@users.noreply.github.com> Date: Fri, 29 May 2026 12:35:41 +0800 Subject: [PATCH 3/3] skip owned apps in RemoveAppAndSendChange --- src/Hook/Hooks_SteamUI.cpp | 6 +++--- src/Utils/LuaConfig.cpp | 6 +++++- src/Utils/LuaConfig.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Hook/Hooks_SteamUI.cpp b/src/Hook/Hooks_SteamUI.cpp index b03810b..3c41947 100644 --- a/src/Hook/Hooks_SteamUI.cpp +++ b/src/Hook/Hooks_SteamUI.cpp @@ -43,9 +43,9 @@ namespace Hooks_SteamUI { } void RemoveAppAndSendChange(AppId_t appId) { - // Only attempt to remove if LuaConfig is tracking this appId - if(!LuaConfig::HasDepot(appId)){ - LOG_STEAMUI_WARN("RemoveAppAndSendChange: appId={} not tracked in LuaConfig, skipping", appId); + // skip on owned apps + if(LuaConfig::IsOwned(appId)){ + LOG_STEAMUI_WARN("RemoveAppAndSendChange: appId={} is owned, skipping", appId); return; } if(CAPTURE_READY(GetAppByID) && CAPTURE_READY(MarkAppChange)) { diff --git a/src/Utils/LuaConfig.cpp b/src/Utils/LuaConfig.cpp index 9bc76cc..404e34f 100644 --- a/src/Utils/LuaConfig.cpp +++ b/src/Utils/LuaConfig.cpp @@ -418,7 +418,11 @@ namespace LuaConfig{ // ── public query API ───────────────────────────────────────── bool HasDepot(AppId_t DepotId,bool checkOwned) { - return DepotKeySet.count(DepotId) && (!checkOwned || !OwnedAppIdSet.count(DepotId)); + return DepotKeySet.count(DepotId) && (!checkOwned || !IsOwned(DepotId)); + } + + bool IsOwned(AppId_t AppId) { + return OwnedAppIdSet.count(AppId); } void MarkOwned(AppId_t AppId) { diff --git a/src/Utils/LuaConfig.h b/src/Utils/LuaConfig.h index a5b96cb..61bb53f 100644 --- a/src/Utils/LuaConfig.h +++ b/src/Utils/LuaConfig.h @@ -8,6 +8,7 @@ namespace LuaConfig{ bool HasDepot(AppId_t appId, bool checkOwned=true); + bool IsOwned(AppId_t appId); void MarkOwned(AppId_t appId); std::vector GetAllDepotIds(); std::vector GetDecryptionKey(AppId_t appId);