From 1e23974c48ef55efcbc1da40d85f789e59c08936 Mon Sep 17 00:00:00 2001 From: a Date: Tue, 26 Nov 2024 21:52:59 +0200 Subject: [PATCH 1/8] * fix mod details struct access again! * fix querying mod tags count * fix mod preview file URI * copy missing mod min/max branches * use different timing for mod update/create/add dates --- dll/dll/steam_ugc.h | 2 +- dll/settings.cpp | 2 ++ dll/settings_parser.cpp | 16 ++++++---- dll/steam_remote_storage.cpp | 4 +-- dll/steam_ugc.cpp | 57 ++++++++++++++++++++++-------------- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/dll/dll/steam_ugc.h b/dll/dll/steam_ugc.h index ed19c924..84da347f 100644 --- a/dll/dll/steam_ugc.h +++ b/dll/dll/steam_ugc.h @@ -82,7 +82,7 @@ private: std::optional get_query_ugc_tag(UGCQueryHandle_t handle, uint32 index, uint32 indexTag); - std::optional> get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index); + std::vector get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index); void set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, IUgcItfVersion ver); diff --git a/dll/settings.cpp b/dll/settings.cpp index fbabef16..6a6d6609 100644 --- a/dll/settings.cpp +++ b/dll/settings.cpp @@ -220,6 +220,8 @@ void Settings::addModDetails(PublishedFileId_t id, const Mod_entry &details) f->numChildren = details.numChildren; f->previewURL = details.previewURL; f->total_files_sizes = details.total_files_sizes; + f->min_game_branch = details.min_game_branch; + f->max_game_branch = details.max_game_branch; } } diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index 27acdc45..e634c32a 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -940,6 +940,12 @@ static void parse_installed_app_Ids(class Settings *settings_client, class Setti static const auto one_week_ago_epoch = std::chrono::duration_cast( ( startup_time - std::chrono::hours(24 * 7) ).time_since_epoch() ).count(); +static const auto two_week_ago_epoch = std::chrono::duration_cast( + ( startup_time - std::chrono::hours(24 * 7 * 2) ).time_since_epoch() +).count(); +static const auto three_week_ago_epoch = std::chrono::duration_cast( + ( startup_time - std::chrono::hours(24 * 7 * 3) ).time_since_epoch() +).count(); static size_t get_file_size_safe(const std::string &filepath, const std::string &basepath, int32 default_val = 0) { @@ -963,7 +969,7 @@ static std::string get_mod_preview_url(const std::string &previewFileName, const } else { auto settings_folder = std::string(Local_Storage::get_game_settings_path()); std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/'); - return "file://" + settings_folder + "mod_images/" + mod_id + "/" + previewFileName; + return "file:///" + settings_folder + "mod_images/" + mod_id + "/" + previewFileName; } } @@ -993,8 +999,8 @@ static void try_parse_mods_file(class Settings *settings_client, Settings *setti newMod.description = mod.value().value("description", std::string("")); newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64()); newMod.timeCreated = mod.value().value("time_created", (uint32)one_week_ago_epoch); - newMod.timeUpdated = mod.value().value("time_updated", (uint32)one_week_ago_epoch); - newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch); + newMod.timeUpdated = mod.value().value("time_updated", (uint32)two_week_ago_epoch); + newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)three_week_ago_epoch); newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; newMod.banned = false; newMod.acceptedForUse = true; @@ -1077,8 +1083,8 @@ static void try_detect_mods_folder(class Settings *settings_client, Settings *se newMod.description = "mod #" + mod_folder; newMod.steamIDOwner = settings_client->get_local_steam_id().ConvertToUint64(); newMod.timeCreated = (uint32)one_week_ago_epoch; - newMod.timeUpdated = (uint32)one_week_ago_epoch; - newMod.timeAddedToUserList = (uint32)one_week_ago_epoch; + newMod.timeUpdated = (uint32)two_week_ago_epoch; + newMod.timeAddedToUserList = (uint32)three_week_ago_epoch; newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; newMod.banned = false; newMod.acceptedForUse = true; diff --git a/dll/steam_remote_storage.cpp b/dll/steam_remote_storage.cpp index 7ab28fbf..2814e642 100644 --- a/dll/steam_remote_storage.cpp +++ b/dll/steam_remote_storage.cpp @@ -446,7 +446,6 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownload( UGCHandle_t hContent, uint32 u data.m_eResult = k_EResultOK; data.m_ulSteamIDOwner = mod.steamIDOwner; data.m_nSizeInBytes = mod_size; - data.m_ulSteamIDOwner = mod.steamIDOwner; mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); PRINT_DEBUG(" QueryUGCRequest data.m_pchFileName = '%s'", data.m_pchFileName); @@ -497,7 +496,7 @@ bool Steam_Remote_Storage::GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID { PRINT_DEBUG_ENTRY(); std::lock_guard lock(global_mutex); - + return false; } @@ -1157,7 +1156,6 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownloadToLocation( UGCHandle_t hContent data.m_nAppID = settings->get_local_game_id().AppID(); data.m_ulSteamIDOwner = mod.steamIDOwner; data.m_nSizeInBytes = mod_size; - data.m_ulSteamIDOwner = mod.steamIDOwner; mod_name.copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); diff --git a/dll/steam_ugc.cpp b/dll/steam_ugc.cpp index 755f05d5..2e6c7cbd 100644 --- a/dll/steam_ugc.cpp +++ b/dll/steam_ugc.cpp @@ -48,19 +48,25 @@ std::optional Steam_UGC::get_query_ugc(UGCQueryHandle_t handle, uint3 return settings->getMod(file_id); } -std::optional> Steam_UGC::get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index) +std::vector Steam_UGC::get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index) { auto res = get_query_ugc(handle, index); if (!res.has_value()) return std::nullopt; + std::string tmp = res.value().tags; + auto tags_tokens = std::vector{}; - std::stringstream ss(res.value().tags); - std::string tmp{}; - while(ss >> tmp) { - if (tmp.back() == ',') tmp = tmp.substr(0, tmp.size() - 1); - tags_tokens.push_back(tmp); + size_t start = 0; + while (true) { + auto end = tmp.find(',', start); + if (end == std::string::npos) break; + + tags_tokens.push_back(tmp.substr(start, end - start)); + start = end + 1; } + tags_tokens.push_back(tmp.substr(start)); + return tags_tokens; } @@ -68,8 +74,6 @@ std::optional> Steam_UGC::get_query_ugc_tags(UGCQueryHa void Steam_UGC::set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, IUgcItfVersion ver) { if (pDetails) { - memset(pDetails, 0, sizeof(SteamUGCDetails_t)); - pDetails->m_nPublishedFileId = id; if (settings->isModInstalled(id)) { @@ -90,18 +94,29 @@ void Steam_UGC::set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails, I pDetails->m_nPreviewFileSize = mod.previewFileSize; pDetails->m_rtimeCreated = mod.timeCreated; pDetails->m_rtimeUpdated = mod.timeUpdated; - pDetails->m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64(); + pDetails->m_ulSteamIDOwner = mod.steamIDOwner; pDetails->m_rtimeAddedToUserList = mod.timeAddedToUserList; pDetails->m_unVotesUp = mod.votesUp; pDetails->m_unVotesDown = mod.votesDown; pDetails->m_flScore = mod.score; - mod.primaryFileName.copy(pDetails->m_pchFileName, sizeof(pDetails->m_pchFileName) - 1); - mod.description.copy(pDetails->m_rgchDescription, sizeof(pDetails->m_rgchDescription) - 1); - mod.tags.copy(pDetails->m_rgchTags, sizeof(pDetails->m_rgchTags) - 1); - mod.title.copy(pDetails->m_rgchTitle, sizeof(pDetails->m_rgchTitle) - 1); - mod.workshopItemURL.copy(pDetails->m_rgchURL, sizeof(pDetails->m_rgchURL) - 1); + // real steamclient64.dll may set this to null! (ex: item id 3366485326) + auto copied_chars = mod.primaryFileName.copy(pDetails->m_pchFileName, sizeof(pDetails->m_pchFileName) - 1); + pDetails->m_pchFileName[copied_chars] = 0; + + copied_chars = mod.description.copy(pDetails->m_rgchDescription, sizeof(pDetails->m_rgchDescription) - 1); + pDetails->m_rgchDescription[copied_chars] = 0; + + copied_chars = mod.tags.copy(pDetails->m_rgchTags, sizeof(pDetails->m_rgchTags) - 1); + pDetails->m_rgchTags[copied_chars] = 0; + + copied_chars = mod.title.copy(pDetails->m_rgchTitle, sizeof(pDetails->m_rgchTitle) - 1); + pDetails->m_rgchTitle[copied_chars] = 0; + + // real steamclient64.dll may set this to null! (ex: item id 3366485326) + copied_chars = mod.workshopItemURL.copy(pDetails->m_rgchURL, sizeof(pDetails->m_rgchURL) - 1); + pDetails->m_rgchURL[copied_chars] = 0; // TODO should we enable this? // pDetails->m_unNumChildren = mod.numChildren; @@ -362,10 +377,10 @@ bool Steam_UGC::GetQueryUGCResult_old( UGCQueryHandle_t handle, uint32 index, St std::optional Steam_UGC::get_query_ugc_tag(UGCQueryHandle_t handle, uint32 index, uint32 indexTag) { auto res = get_query_ugc_tags(handle, index); - if (!res.has_value()) return std::nullopt; - if (indexTag >= res.value().size()) return std::nullopt; + if (res.empty()) return std::nullopt; + if (indexTag >= res.size()) return std::nullopt; - std::string tmp = res.value()[indexTag]; + std::string tmp = res[indexTag]; if (tmp.back() == ',') { tmp = tmp.substr(0, tmp.size() - 1); } @@ -380,7 +395,7 @@ uint32 Steam_UGC::GetQueryUGCNumTags( UGCQueryHandle_t handle, uint32 index ) if (handle == k_UGCQueryHandleInvalid) return 0; auto res = get_query_ugc_tags(handle, index); - return res.has_value() ? static_cast(res.value().size()) : 0; + return static_cast(res.size()); } bool Steam_UGC::GetQueryUGCTag( UGCQueryHandle_t handle, uint32 index, uint32 indexTag, STEAM_OUT_STRING_COUNT( cchValueSize ) char* pchValue, uint32 cchValueSize ) @@ -994,9 +1009,7 @@ bool Steam_UGC::SetItemVisibility( UGCUpdateHandle_t handle, ERemoteStoragePubli bool Steam_UGC::SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags ) { PRINT_DEBUG("old"); - std::lock_guard lock(global_mutex); - - return false; + return SetItemTags(updateHandle, pTags, false); } bool Steam_UGC::SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags, bool bAllowAdminTags ) @@ -1368,7 +1381,7 @@ bool Steam_UGC::GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 * } if (punSizeOnDisk) *punSizeOnDisk = mod.primaryFileSize; - if (punTimeStamp) *punTimeStamp = mod.timeUpdated; + if (punTimeStamp) *punTimeStamp = mod.timeAddedToUserList; if (pchFolder && cchFolderSize) { // human fall flat doesn't send a nulled buffer, and won't recognize the proper mod path because of that memset(pchFolder, 0, cchFolderSize); From d8e94a373ad9830e93b82fa74f1fa191feaac44d Mon Sep 17 00:00:00 2001 From: a Date: Tue, 26 Nov 2024 22:03:46 +0200 Subject: [PATCH 2/8] **** you visual studio --- dll/steam_ugc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dll/steam_ugc.cpp b/dll/steam_ugc.cpp index 2e6c7cbd..0f45df63 100644 --- a/dll/steam_ugc.cpp +++ b/dll/steam_ugc.cpp @@ -51,7 +51,7 @@ std::optional Steam_UGC::get_query_ugc(UGCQueryHandle_t handle, uint3 std::vector Steam_UGC::get_query_ugc_tags(UGCQueryHandle_t handle, uint32 index) { auto res = get_query_ugc(handle, index); - if (!res.has_value()) return std::nullopt; + if (!res.has_value()) return {}; std::string tmp = res.value().tags; From e93dfc22b190705b7c45711c79809776eb9f4355 Mon Sep 17 00:00:00 2001 From: a Date: Tue, 26 Nov 2024 23:15:04 +0200 Subject: [PATCH 3/8] check tag string size before access --- dll/steam_ugc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dll/steam_ugc.cpp b/dll/steam_ugc.cpp index 0f45df63..66ce3274 100644 --- a/dll/steam_ugc.cpp +++ b/dll/steam_ugc.cpp @@ -381,7 +381,7 @@ std::optional Steam_UGC::get_query_ugc_tag(UGCQueryHandle_t handle, if (indexTag >= res.size()) return std::nullopt; std::string tmp = res[indexTag]; - if (tmp.back() == ',') { + if (!tmp.empty() && tmp.back() == ',') { tmp = tmp.substr(0, tmp.size() - 1); } return tmp; From fd9240989daad766f4574cd6255b7a3a161c01f4 Mon Sep 17 00:00:00 2001 From: a Date: Tue, 26 Nov 2024 23:20:24 +0200 Subject: [PATCH 4/8] * change formmatting (tabs > spaces) * remove spaces between tags (they're comma separated) --- .../steam_settings.EXAMPLE/mods.EXAMPLE.json | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/post_build/steam_settings.EXAMPLE/mods.EXAMPLE.json b/post_build/steam_settings.EXAMPLE/mods.EXAMPLE.json index 2ffe5472..a951d189 100644 --- a/post_build/steam_settings.EXAMPLE/mods.EXAMPLE.json +++ b/post_build/steam_settings.EXAMPLE/mods.EXAMPLE.json @@ -1,51 +1,51 @@ { - "9422": { - "title": "Some Workshop Item", - "description": "This is the prefered way of specifying mod details, primary file must exist in steam_settings/mods/9422 (along with any other mod files), and preview file must exist in steam_settings/mod_images/9422", - "primary_filename": "metadata.json", - "preview_filename": "thumbnail.png" - }, + "9422": { + "title": "Some Workshop Item", + "description": "This is the prefered way of specifying mod details, primary file must exist in steam_settings/mods/9422 (along with any other mod files), and preview file must exist in steam_settings/mod_images/9422", + "primary_filename": "metadata.json", + "preview_filename": "thumbnail.png" + }, - "111111111": { - "title": "Example Workshop Item", - "description": "Example Workshop Item with all Details", - "steam_id_owner": 11111111111111111, - "time_created": 1554997000, - "time_updated": 1554997000, - "time_added": 1554997000, - "tags": "Maps, exampleTag, exampleTag2", - "primary_filename": "test.sav", - "primary_filesize": 1000000, - "preview_filename": "test.png", - "preview_filesize": 1000000, - "total_files_sizes": 9977664411, - "min_game_branch": "1.4.2", - "max_game_branch": "1.5.0", - "workshop_item_url": "https://steamcommunity.com/sharedfiles/filedetails/?id=111111111", - "upvotes": 10, - "downvotes": 1, - "num_children": 0, - "path": "C:\\games\\my_game\\steam_settings\\mods_data\\mod_111111111_data_folder", - "preview_url": "file://C:/games/my_game/steam_settings/mod_images/my_preview.jpg", - "score": 0.7 - }, - - "222222222": { - "title": "Example Workshop Item", - "description": "Example Workshop Item with some Details", - "preview_url": "https://commons.wikimedia.org/wiki/File:Tree_in_Mississippi.jpg", - "score": 1.0 - }, - - "333333333": { - "title": "Example Workshop Item" - }, - - "444444444": { - "title": "Example Workshop Item", - "description": "Example Workshop Item" - }, - - "555555555": { - } + "111111111": { + "title": "Example Workshop Item", + "description": "Example Workshop Item with all Details", + "steam_id_owner": 11111111111111111, + "time_created": 1554997000, + "time_updated": 1554997000, + "time_added": 1554997000, + "tags": "Maps,exampleTag,exampleTag2", + "primary_filename": "test.sav", + "primary_filesize": 1000000, + "preview_filename": "test.png", + "preview_filesize": 1000000, + "total_files_sizes": 9977664411, + "min_game_branch": "1.4.2", + "max_game_branch": "1.5.0", + "workshop_item_url": "https://steamcommunity.com/sharedfiles/filedetails/?id=111111111", + "upvotes": 10, + "downvotes": 1, + "num_children": 0, + "path": "C:\\games\\my_game\\steam_settings\\mods_data\\mod_111111111_data_folder", + "preview_url": "file:///C:/games/my_game/steam_settings/mod_images/my_preview.jpg", + "score": 0.7 + }, + + "222222222": { + "title": "Example Workshop Item", + "description": "Example Workshop Item with some Details", + "preview_url": "https://commons.wikimedia.org/wiki/File:Tree_in_Mississippi.jpg", + "score": 1.0 + }, + + "333333333": { + "title": "Example Workshop Item" + }, + + "444444444": { + "title": "Example Workshop Item", + "description": "Example Workshop Item" + }, + + "555555555": { + } } From 63212a9835da46367fc4fe1015110b8cb7d9c28a Mon Sep 17 00:00:00 2001 From: a Date: Tue, 26 Nov 2024 23:20:46 +0200 Subject: [PATCH 5/8] note on source-engine mods on Windows --- post_build/README.experimental_steamclient.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/post_build/README.experimental_steamclient.md b/post_build/README.experimental_steamclient.md index 12b5014b..0e64e5cb 100644 --- a/post_build/README.experimental_steamclient.md +++ b/post_build/README.experimental_steamclient.md @@ -82,3 +82,14 @@ This dll is meant to be injected during **startup** only, it must **not** be pla ## `GameOverlayRenderer` Some apps verify the existence of this dll, either on disk, or inside their memory space, that's why this dll exists. It is **NOT** recommended to ignore this dll. + +## Mods paths (source-engine games on Windows) +On Windows, the registry key `SourceModInstallPath` is changed to the folder containing the loader. +``` +Registry path: HKEY_CURRENT_USER\SOFTWARE\Valve\Steam +Registry key: SourceModInstallPath +Original value: C:\Program Files (x86)\Steam\steamapps\sourcemods +New value: +``` + +This affects source-engine mods From 1c5e64f03f9792467d84dd07925bc71f7e4aae8a Mon Sep 17 00:00:00 2001 From: a Date: Wed, 27 Nov 2024 21:49:34 +0200 Subject: [PATCH 6/8] fix dates --- dll/settings_parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index e634c32a..a58fc28e 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -998,9 +998,9 @@ static void try_parse_mods_file(class Settings *settings_client, Settings *setti newMod.fileType = k_EWorkshopFileTypeCommunity; newMod.description = mod.value().value("description", std::string("")); newMod.steamIDOwner = mod.value().value("steam_id_owner", settings_client->get_local_steam_id().ConvertToUint64()); - newMod.timeCreated = mod.value().value("time_created", (uint32)one_week_ago_epoch); + newMod.timeCreated = mod.value().value("time_created", (uint32)three_week_ago_epoch); newMod.timeUpdated = mod.value().value("time_updated", (uint32)two_week_ago_epoch); - newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)three_week_ago_epoch); + newMod.timeAddedToUserList = mod.value().value("time_added", (uint32)one_week_ago_epoch); newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; newMod.banned = false; newMod.acceptedForUse = true; @@ -1082,9 +1082,9 @@ static void try_detect_mods_folder(class Settings *settings_client, Settings *se newMod.fileType = k_EWorkshopFileTypeCommunity; newMod.description = "mod #" + mod_folder; newMod.steamIDOwner = settings_client->get_local_steam_id().ConvertToUint64(); - newMod.timeCreated = (uint32)one_week_ago_epoch; + newMod.timeCreated = (uint32)three_week_ago_epoch; newMod.timeUpdated = (uint32)two_week_ago_epoch; - newMod.timeAddedToUserList = (uint32)three_week_ago_epoch; + newMod.timeAddedToUserList = (uint32)one_week_ago_epoch; newMod.visibility = k_ERemoteStoragePublishedFileVisibilityPublic; newMod.banned = false; newMod.acceptedForUse = true; From 6fcd47cd728a6716a6ae3835ae530d194d2645ed Mon Sep 17 00:00:00 2001 From: a Date: Wed, 27 Nov 2024 22:00:08 +0200 Subject: [PATCH 7/8] avoid git conflict --- dll/steam_remote_storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dll/steam_remote_storage.cpp b/dll/steam_remote_storage.cpp index 2814e642..1bcb92c9 100644 --- a/dll/steam_remote_storage.cpp +++ b/dll/steam_remote_storage.cpp @@ -496,7 +496,7 @@ bool Steam_Remote_Storage::GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID { PRINT_DEBUG_ENTRY(); std::lock_guard lock(global_mutex); - + return false; } From 0d35336c575edbb840d994626fdabe1887466260 Mon Sep 17 00:00:00 2001 From: a Date: Thu, 28 Nov 2024 01:26:24 +0200 Subject: [PATCH 8/8] fix preview url path on Linux --- dll/settings_parser.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index a58fc28e..8ad8d842 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -969,7 +969,16 @@ static std::string get_mod_preview_url(const std::string &previewFileName, const } else { auto settings_folder = std::string(Local_Storage::get_game_settings_path()); std::replace(settings_folder.begin(), settings_folder.end(), '\\', '/'); - return "file:///" + settings_folder + "mod_images/" + mod_id + "/" + previewFileName; + + return + +#if defined(__WINDOWS__) + "file:///" +#else // on Linux absolute paths start like this: /my/path, so the 3rd slash is already appended + "file://" +#endif + + + settings_folder + "mod_images/" + mod_id + "/" + previewFileName; } }