diff --git a/dll/dll/steam_client.h b/dll/dll/steam_client.h index af958afe..5446a92a 100644 --- a/dll/dll/steam_client.h +++ b/dll/dll/steam_client.h @@ -154,6 +154,8 @@ public: Steam_Networking_Messages *steam_gameserver_networking_messages{}; Steam_Game_Coordinator *steam_gameserver_game_coordinator{}; Steam_Masterserver_Updater *steam_masterserver_updater{}; + Steam_GameStats *steam_gameserver_gamestats{}; + Steam_AppTicket *steam_app_ticket{}; Steam_Overlay* steam_overlay{}; diff --git a/dll/dll/steam_gamestats.h b/dll/dll/steam_gamestats.h index e233081c..54b9da67 100644 --- a/dll/dll/steam_gamestats.h +++ b/dll/dll/steam_gamestats.h @@ -26,91 +26,98 @@ class Steam_GameStats : public ISteamGameStats { - enum class AttributeType_t - { - Int, Str, Float, Int64, - }; +private: + // how much time to wait before removing ended sessions + constexpr const static int MAX_DEAD_SESSION_SECONDS = 15; // TODO not sure what would be sensible in this case - struct Attribute_t - { - AttributeType_t type{}; - union { - int32 n_data; - std::string s_data; - float f_data; - int64 ll_data{}; - }; + enum class AttributeType_t + { + Int, Str, Float, Int64, + }; - Attribute_t(); - Attribute_t(const Attribute_t &other); - Attribute_t(Attribute_t &&other); - ~Attribute_t(); - }; + struct Attribute_t + { + const AttributeType_t type; + union { + int32 n_data; + std::string s_data; + float f_data; + int64 ll_data; + }; - struct Row_t - { - bool committed = false; - std::map attributes{}; - }; + Attribute_t(AttributeType_t type); + Attribute_t(const Attribute_t &other); + Attribute_t(Attribute_t &&other); + ~Attribute_t(); + }; - struct Table_t - { - std::vector rows{}; - }; + struct Row_t + { + bool committed = false; + std::map attributes{}; + }; - struct Session_t - { - EGameStatsAccountType nAccountType{}; - RTime32 rtTimeStarted{}; - RTime32 rtTimeEnded{}; - int nReasonCode{}; - bool ended = false; - std::map attributes{}; + struct Table_t + { + std::vector rows{}; + }; - std::vector> tables{}; - }; + struct Session_t + { + EGameStatsAccountType nAccountType{}; + RTime32 rtTimeStarted{}; + RTime32 rtTimeEnded{}; + int nReasonCode{}; + bool ended = false; + std::map attributes{}; - class Settings *settings{}; - class Networking *network{}; - class SteamCallResults *callback_results{}; - class SteamCallBacks *callbacks{}; - class RunEveryRunCB *run_every_runcb{}; - - std::vector sessions{}; + std::vector> tables{}; + }; + + class Settings *settings{}; + class Networking *network{}; + class SteamCallResults *callback_results{}; + class SteamCallBacks *callbacks{}; + class RunEveryRunCB *run_every_runcb{}; + + std::map sessions{}; - bool valid_stats_account_type(int8 nAccountType); - Table_t *get_or_create_session_table(Session_t &session, const char *table_name); - Attribute_t *get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create); - Attribute_t *get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create); + uint64 create_session_id() const; + bool valid_stats_account_type(int8 nAccountType); + Table_t *get_or_create_session_table(Session_t &session, const char *table_name); + Attribute_t *get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create); + Attribute_t *get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create); + Session_t* get_last_active_session(); - void steam_run_callback(); + void steam_run_callback(); - // user connect/disconnect - void network_callback_low_level(Common_Message *msg); + // user connect/disconnect + void network_callback_low_level(Common_Message *msg); + + static void steam_gamestats_network_low_level(void *object, Common_Message *msg); + static void steam_gamestats_run_every_runcb(void *object); - static void steam_gamestats_network_low_level(void *object, Common_Message *msg); - static void steam_gamestats_run_every_runcb(void *object); public: - Steam_GameStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb); - ~Steam_GameStats(); - - SteamAPICall_t GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ); - SteamAPICall_t EndSession( uint64 ulSessionID, RTime32 rtTimeEnded, int nReasonCode ); - EResult AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ); - EResult AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ); - EResult AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ); + Steam_GameStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb); + ~Steam_GameStats(); + + SteamAPICall_t GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ); + SteamAPICall_t EndSession( uint64 ulSessionID, RTime32 rtTimeEnded, int nReasonCode ); + EResult AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ); + EResult AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ); + EResult AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ); - EResult AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ); - EResult CommitRow( uint64 ulRowID ); - EResult CommitOutstandingRows( uint64 ulSessionID ); - EResult AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ); - EResult AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ); - EResult AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ); + EResult AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ); + EResult CommitRow( uint64 ulRowID ); + EResult CommitOutstandingRows( uint64 ulSessionID ); + EResult AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ); + EResult AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ); + EResult AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ); - EResult AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ); - EResult AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ); + EResult AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ); + EResult AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ); }; diff --git a/dll/dll/steam_remote_storage.h b/dll/dll/steam_remote_storage.h index 283c7a57..5083852c 100644 --- a/dll/dll/steam_remote_storage.h +++ b/dll/dll/steam_remote_storage.h @@ -36,30 +36,29 @@ struct Stream_Write { }; struct Downloaded_File { - // --- these are needed due to the usage of union - Downloaded_File(); - ~Downloaded_File(); - // --- - - enum DownloadSource { + enum class DownloadSource { AfterFileShare, // attempted download after a call to Steam_Remote_Storage::FileShare() AfterSendQueryUGCRequest, // attempted download after a call to Steam_UGC::SendQueryUGCRequest() FromUGCDownloadToLocation, // attempted download via Steam_Remote_Storage::UGCDownloadToLocation() - } source{}; + }; + +private: + DownloadSource source; + +public: + Downloaded_File(DownloadSource src); + + DownloadSource get_source() const; // *** used in any case std::string file{}; uint64 total_size{}; - // put any additional data needed by other sources here - - union { - // *** used when source = SendQueryUGCRequest only - Ugc_Remote_Storage_Bridge::QueryInfo mod_query_info; + // *** used when source = AfterSendQueryUGCRequest and FromUGCDownloadToLocation + Ugc_Remote_Storage_Bridge::QueryInfo mod_query_info{}; - // *** used when source = FromUGCDownloadToLocation only - std::string download_to_location_fullpath; - }; + // *** used when source = FromUGCDownloadToLocation only + std::string download_to_location_fullpath{}; }; @@ -85,6 +84,7 @@ private: class Ugc_Remote_Storage_Bridge *ugc_bridge{}; class Local_Storage *local_storage{}; class SteamCallResults *callback_results{}; + class SteamCallBacks *callbacks{}; std::vector async_reads{}; std::vector stream_writes{}; @@ -95,7 +95,7 @@ private: public: - Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results); + Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks); // NOTE // diff --git a/dll/dll/steam_timeline.h b/dll/dll/steam_timeline.h index 8a62dc84..9a4ff440 100644 --- a/dll/dll/steam_timeline.h +++ b/dll/dll/steam_timeline.h @@ -24,6 +24,8 @@ class Steam_Timeline : public ISteamTimeline { private: + constexpr const static float PRIORITY_CLIP_MIN_SEC = 8.0f; + struct TimelineEvent_t { // emu specific: time when this event was added to the list via 'Steam_Timeline::AddTimelineEvent()' @@ -68,7 +70,7 @@ private: class RunEveryRunCB *run_every_runcb{}; std::vector timeline_events{}; - std::vector timeline_states{TimelineState_t{}}; // it seems to always start with a default event + std::vector timeline_states{}; // unconditional periodic callback void RunCallbacks(); diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index b8e70c4d..7a448bc2 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -1114,7 +1114,7 @@ static void try_detect_mods_folder(class Settings *settings_client, Settings *se PRINT_DEBUG(" preview_filename: '%s'", newMod.previewFileName.c_str()); PRINT_DEBUG(" preview_filesize: %i bytes", newMod.previewFileSize); PRINT_DEBUG(" preview file handle: %llu", settings_client->getMod(newMod.id).handlePreviewFile); - PRINT_DEBUG(" total_files_sizes: '%s'", newMod.total_files_sizes); + PRINT_DEBUG(" total_files_sizes: '%llu'", newMod.total_files_sizes); PRINT_DEBUG(" min_game_branch: '%s'", newMod.min_game_branch.c_str()); PRINT_DEBUG(" max_game_branch: '%s'", newMod.max_game_branch.c_str()); PRINT_DEBUG(" workshop_item_url: '%s'", newMod.workshopItemURL.c_str()); @@ -1379,8 +1379,8 @@ static void parse_simple_features(class Settings *settings_client, class Setting settings_client->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_client->matchmaking_server_details_via_source_query); settings_server->matchmaking_server_details_via_source_query = ini.GetBoolValue("main::general", "matchmaking_server_details_via_source_query", settings_server->matchmaking_server_details_via_source_query); - settings_client->matchmaking_server_list_always_lan_type = ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", settings_client->matchmaking_server_list_always_lan_type); - settings_server->matchmaking_server_list_always_lan_type = ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", settings_server->matchmaking_server_list_always_lan_type); + settings_client->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_client->matchmaking_server_list_always_lan_type); + settings_server->matchmaking_server_list_always_lan_type = !ini.GetBoolValue("main::general", "matchmaking_server_list_actual_type", !settings_server->matchmaking_server_list_always_lan_type); // [main::connectivity] diff --git a/dll/steam_client.cpp b/dll/steam_client.cpp index 1aaebc46..dc3a453f 100644 --- a/dll/steam_client.cpp +++ b/dll/steam_client.cpp @@ -100,7 +100,7 @@ Steam_Client::Steam_Client() steam_user_stats = new Steam_User_Stats(settings_client, network, local_storage, callback_results_client, callbacks_client, run_every_runcb, steam_overlay); steam_apps = new Steam_Apps(settings_client, callback_results_client, callbacks_client); steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb); - steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client); + steam_remote_storage = new Steam_Remote_Storage(settings_client, ugc_bridge, local_storage, callback_results_client, callbacks_client); steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client); steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client); steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb); @@ -143,6 +143,7 @@ Steam_Client::Steam_Client() steam_gameserver_networking_messages = new Steam_Networking_Messages(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); steam_gameserver_game_coordinator = new Steam_Game_Coordinator(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); steam_masterserver_updater = new Steam_Masterserver_Updater(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); + steam_gameserver_gamestats = new Steam_GameStats(settings_server, network, callback_results_server, callbacks_server, run_every_runcb); PRINT_DEBUG("init AppTicket"); steam_app_ticket = new Steam_AppTicket(settings_client); diff --git a/dll/steam_client_interface_getter.cpp b/dll/steam_client_interface_getter.cpp index 38f32f03..ef2ee181 100644 --- a/dll/steam_client_interface_getter.cpp +++ b/dll/steam_client_interface_getter.cpp @@ -37,8 +37,16 @@ ISteamGameStats *Steam_Client::GetISteamGameStats( HSteamUser hSteamUser, HSteam PRINT_DEBUG("%s", pchVersion); if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return nullptr; + Steam_GameStats *steam_gamestats_tmp{}; + + if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) { + steam_gamestats_tmp = steam_gameserver_gamestats; + } else { + steam_gamestats_tmp = steam_gamestats; + } + if (strcmp(pchVersion, STEAMGAMESTATS_INTERFACE_VERSION) == 0) { - return reinterpret_cast(static_cast(steam_gamestats)); + return reinterpret_cast(static_cast(steam_gamestats_tmp)); } report_missing_impl_and_exit(pchVersion, EMU_FUNC_NAME); @@ -249,13 +257,15 @@ ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser // returns the a generic interface void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) { - PRINT_DEBUG("%s", pchVersion); + PRINT_DEBUG("'%s' %i %i", pchVersion, hSteamUser, hSteamPipe); if (!steam_pipes.count(hSteamPipe)) return NULL; bool server = false; if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) { + // PRINT_DEBUG("requesting interface with server pipe"); server = true; } else { + // PRINT_DEBUG("requesting interface with client pipe"); // if this is a user pipe, and version != "SteamNetworkingUtils", and version != "SteamUtils" if ((strstr(pchVersion, "SteamNetworkingUtils") != pchVersion) && (strstr(pchVersion, "SteamUtils") != pchVersion)) { if (!hSteamUser) return NULL; diff --git a/dll/steam_friends.cpp b/dll/steam_friends.cpp index efc564e6..a7a3070c 100644 --- a/dll/steam_friends.cpp +++ b/dll/steam_friends.cpp @@ -209,21 +209,16 @@ SteamAPICall_t Steam_Friends::SetPersonaName( const char *pchPersonaName ) PRINT_DEBUG_ENTRY(); std::lock_guard lock(global_mutex); + // send PersonaStateChange_t callbacks + persona_change(settings->get_local_steam_id(), EPersonaChange::k_EPersonaChangeName); + SetPersonaNameResponse_t data{}; data.m_bSuccess = true; data.m_bLocalSuccess = false; data.m_result = k_EResultOK; - persona_change(settings->get_local_steam_id(), k_EPersonaChangeName); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); - - { - PersonaStateChange_t data2{}; - data2.m_nChangeFlags = EPersonaChange::k_EPersonaChangeName; - data2.m_ulSteamID = settings->get_local_steam_id().ConvertToUint64(); - callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2)); - } - + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); return ret; } @@ -1032,7 +1027,10 @@ SteamAPICall_t Steam_Friends::JoinClanChatRoom( CSteamID steamIDClan ) JoinClanChatRoomCompletionResult_t data; data.m_steamIDClanChat = steamIDClan; data.m_eChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } bool Steam_Friends::LeaveClanChatRoom( CSteamID steamIDClan ) diff --git a/dll/steam_gamestats.cpp b/dll/steam_gamestats.cpp index 4ee3b674..9039acb3 100644 --- a/dll/steam_gamestats.cpp +++ b/dll/steam_gamestats.cpp @@ -24,39 +24,54 @@ #include "dll/steam_gamestats.h" -Steam_GameStats::Attribute_t::Attribute_t() -{ } +Steam_GameStats::Attribute_t::Attribute_t(AttributeType_t type) + :type(type) +{ + switch (type) + { + case AttributeType_t::Float: f_data = 0; break; + case AttributeType_t::Int64: ll_data = 0; break; + case AttributeType_t::Int: n_data = 0; break; + case AttributeType_t::Str: new (&s_data) std::string{}; break; + + default: PRINT_DEBUG("[X] invalid type %i", (int)type); break; + } +} Steam_GameStats::Attribute_t::Attribute_t(const Attribute_t &other) + :type(type) { - type = other.type; switch (other.type) { case AttributeType_t::Int: n_data = other.n_data; break; - case AttributeType_t::Str: s_data = other.s_data; break; + case AttributeType_t::Str: new (&s_data) std::string(other.s_data); break; case AttributeType_t::Float: f_data = other.f_data; break; case AttributeType_t::Int64: ll_data = other.ll_data; break; - default: break; + default: PRINT_DEBUG("[X] invalid type %i", (int)other.type); break; } } Steam_GameStats::Attribute_t::Attribute_t(Attribute_t &&other) + :type(type) { - type = other.type; switch (other.type) { case AttributeType_t::Int: n_data = other.n_data; break; - case AttributeType_t::Str: s_data = std::move(other.s_data); break; + case AttributeType_t::Str: new (&s_data) std::string(std::move(other.s_data)); break; case AttributeType_t::Float: f_data = other.f_data; break; case AttributeType_t::Int64: ll_data = other.ll_data; break; - default: break; + default: PRINT_DEBUG("[X] invalid type %i", (int)other.type); break; } } Steam_GameStats::Attribute_t::~Attribute_t() -{ } +{ + if (type == AttributeType_t::Str) { + s_data.~basic_string(); + } +} void Steam_GameStats::steam_gamestats_network_low_level(void *object, Common_Message *msg) @@ -83,18 +98,26 @@ Steam_GameStats::Steam_GameStats(class Settings *settings, class Networking *net this->callbacks = callbacks; this->run_every_runcb = run_every_runcb; - this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this); + // this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this); this->run_every_runcb->add(&Steam_GameStats::steam_gamestats_run_every_runcb, this); } Steam_GameStats::~Steam_GameStats() { - this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this); + // this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_GameStats::steam_gamestats_network_low_level, this); this->run_every_runcb->remove(&Steam_GameStats::steam_gamestats_run_every_runcb, this); } +uint64 Steam_GameStats::create_session_id() const +{ + static uint64 session_id = 0; + session_id++; + if (!session_id) session_id = 1; // not sure if 0 is a good idea + return session_id; +} + bool Steam_GameStats::valid_stats_account_type(int8 nAccountType) { switch ((EGameStatsAccountType)nAccountType) { @@ -113,8 +136,8 @@ Steam_GameStats::Table_t *Steam_GameStats::get_or_create_session_table(Session_t { auto table_it = std::find_if(session.tables.rbegin(), session.tables.rend(), [=](const std::pair &item){ return item.first == table_name; }); if (session.tables.rend() == table_it) { - session.tables.emplace_back(std::pair{}); - table = &session.tables.back().second; + auto& [key, val] = session.tables.emplace_back(std::pair{}); + table = &val; } else { table = &table_it->second; } @@ -125,40 +148,29 @@ Steam_GameStats::Table_t *Steam_GameStats::get_or_create_session_table(Session_t Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_session_att(const char *att_name, Session_t &session, AttributeType_t type_if_create) { - Attribute_t *att{}; - { - auto att_itr = session.attributes.find(att_name); - if (att_itr != session.attributes.end()) { - att = &att_itr->second; - } else { - att = &session.attributes[att_name]; - att->type = type_if_create; - } - } - - return att; + auto [ele_it, _] = session.attributes.emplace(att_name, type_if_create); + return &ele_it->second; } Steam_GameStats::Attribute_t *Steam_GameStats::get_or_create_row_att(uint64 ulRowID, const char *att_name, Table_t &table, AttributeType_t type_if_create) { - Attribute_t *att{}; - { - auto &row = table.rows[static_cast(ulRowID)]; - auto att_itr = row.attributes.find(att_name); - if (att_itr != row.attributes.end()) { - att = &att_itr->second; - } else { - att = &row.attributes[att_name]; - att->type = type_if_create; - } - } + auto &row = table.rows[static_cast(ulRowID)]; + auto [ele_it, _] = row.attributes.emplace(att_name, type_if_create); + return &ele_it->second; +} - return att; +Steam_GameStats::Session_t* Steam_GameStats::get_last_active_session() +{ + auto active_session_it = std::find_if(sessions.rbegin(), sessions.rend(), [](std::pair item){ return !item.second.ended; }); + if (sessions.rend() == active_session_it) return nullptr; // TODO is this correct? + + return &active_session_it->second; } SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccountID, int32 nAppID, RTime32 rtTimeStarted ) { + // appid 550 calls this function once with client account id, and another time with server account id PRINT_DEBUG("%i, %llu, %i, %u", (int)nAccountType, ulAccountID, nAppID, rtTimeStarted); std::lock_guard lock(global_mutex); @@ -166,11 +178,13 @@ SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccou (nAppID < 0) || (settings->get_local_game_id().AppID() != (uint32)nAppID) || !valid_stats_account_type(nAccountType)) { + GameStatsSessionIssued_t data_invalid{}; data_invalid.m_bCollectingAny = false; data_invalid.m_bCollectingDetails = false; data_invalid.m_eResult = EResult::k_EResultInvalidParam; data_invalid.m_ulSessionID = 0; + PRINT_DEBUG("[X] invalid param"); auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid)); // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected @@ -178,16 +192,18 @@ SteamAPICall_t Steam_GameStats::GetNewSession( int8 nAccountType, uint64 ulAccou return ret; } + auto session_id = create_session_id(); Session_t new_session{}; new_session.nAccountType = (EGameStatsAccountType)nAccountType; new_session.rtTimeStarted = rtTimeStarted; - sessions.emplace_back(new_session); + sessions.insert_or_assign(session_id, new_session); GameStatsSessionIssued_t data{}; data.m_bCollectingAny = true; // TODO is this correct? data.m_bCollectingDetails = true; // TODO is this correct? data.m_eResult = EResult::k_EResultOK; - data.m_ulSessionID = (uint64)sessions.size(); // I don't know if 0 is a bad value, so always send count (index + 1) + data.m_ulSessionID = session_id; + PRINT_DEBUG("new session id = %llu", session_id); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected @@ -200,10 +216,12 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn PRINT_DEBUG("%llu, %u, %i", ulSessionID, rtTimeEnded, nReasonCode); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size()) { + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) { GameStatsSessionClosed_t data_invalid{}; data_invalid.m_eResult = EResult::k_EResultInvalidParam; data_invalid.m_ulSessionID = ulSessionID; + PRINT_DEBUG("[X] session doesn't exist"); auto ret = callback_results->addCallResult(data_invalid.k_iCallback, &data_invalid, sizeof(data_invalid)); // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected @@ -211,7 +229,7 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn return ret; } - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) { GameStatsSessionClosed_t data_invalid{}; data_invalid.m_eResult = EResult::k_EResultExpired; // TODO is this correct? @@ -230,6 +248,7 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn GameStatsSessionClosed_t data{}; data.m_eResult = EResult::k_EResultOK; data.m_ulSessionID = ulSessionID; + PRINT_DEBUG("ended session"); auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); // the function returns SteamAPICall_t (call result), but in isteamstats.h you can see that a callback is also expected @@ -239,69 +258,77 @@ SteamAPICall_t Steam_GameStats::EndSession( uint64 ulSessionID, RTime32 rtTimeEn EResult Steam_GameStats::AddSessionAttributeInt( uint64 ulSessionID, const char* pstrName, int32 nData ) { - PRINT_DEBUG("%llu, '%s', %i", ulSessionID, pstrName, nData); + PRINT_DEBUG("%llu, '%s'=%i", ulSessionID, pstrName, nData); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Int); if (att->type != AttributeType_t::Int) return EResult::k_EResultFail; // TODO is this correct? att->n_data = nData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddSessionAttributeString( uint64 ulSessionID, const char* pstrName, const char *pstrData ) { - PRINT_DEBUG("%llu, '%s', '%s'", ulSessionID, pstrName, pstrData); + PRINT_DEBUG("%llu, '%s'='%s'", ulSessionID, pstrName, pstrData); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName || !pstrData) return EResult::k_EResultInvalidParam; // TODO is this correct? + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Str); if (att->type != AttributeType_t::Str) return EResult::k_EResultFail; // TODO is this correct? att->s_data = pstrData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddSessionAttributeFloat( uint64 ulSessionID, const char* pstrName, float fData ) { - PRINT_DEBUG("%llu, '%s', %f", ulSessionID, pstrName, fData); + PRINT_DEBUG("%llu, '%s'=%f", ulSessionID, pstrName, fData); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Float); if (att->type != AttributeType_t::Float) return EResult::k_EResultFail; // TODO is this correct? att->f_data = fData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddNewRow( uint64 *pulRowID, uint64 ulSessionID, const char *pstrTableName ) { - PRINT_DEBUG("%p, %llu, '%s'", pulRowID, ulSessionID, pstrTableName); + PRINT_DEBUG("%p, %llu, ['%s']", pulRowID, ulSessionID, pstrTableName); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrTableName) return EResult::k_EResultInvalidParam; // TODO is this correct? + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? auto table = get_or_create_session_table(session, pstrTableName); table->rows.emplace_back(Row_t{}); if (pulRowID) *pulRowID = (uint64)(table->rows.size() - 1); + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } @@ -310,8 +337,8 @@ EResult Steam_GameStats::CommitRow( uint64 ulRowID ) PRINT_DEBUG("%llu", ulRowID); std::lock_guard lock(global_mutex); - auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + if (!active_session) return EResult::k_EResultFail; // TODO is this correct? if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? auto &table = active_session->tables.back().second; @@ -321,6 +348,7 @@ EResult Steam_GameStats::CommitRow( uint64 ulRowID ) auto& row = table.rows[static_cast(ulRowID)]; row.committed = true; + PRINT_DEBUG("committed successfully"); return EResult::k_EResultOK; } @@ -329,9 +357,10 @@ EResult Steam_GameStats::CommitOutstandingRows( uint64 ulSessionID ) PRINT_DEBUG("%llu", ulSessionID); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? if (session.tables.size()) { @@ -339,18 +368,19 @@ EResult Steam_GameStats::CommitOutstandingRows( uint64 ulSessionID ) row.committed = true; } } + PRINT_DEBUG("committed all successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddRowAttributeInt( uint64 ulRowID, const char *pstrName, int32 nData ) { - PRINT_DEBUG("%llu, '%s', %i", ulRowID, pstrName, nData); + PRINT_DEBUG("%llu, '%s'=%i", ulRowID, pstrName, nData); std::lock_guard lock(global_mutex); if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + if (!active_session) return EResult::k_EResultFail; // TODO is this correct? if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? auto &table = active_session->tables.back().second; @@ -360,18 +390,20 @@ EResult Steam_GameStats::AddRowAttributeInt( uint64 ulRowID, const char *pstrNam if (att->type != AttributeType_t::Int) return EResult::k_EResultFail; // TODO is this correct? att->n_data = nData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddRowAtributeString( uint64 ulRowID, const char *pstrName, const char *pstrData ) { - PRINT_DEBUG("%llu, '%s', '%s'", ulRowID, pstrName, pstrData); + PRINT_DEBUG("%llu, '%s'='%s'", ulRowID, pstrName, pstrData); std::lock_guard lock(global_mutex); if (!pstrName || !pstrData) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + if (!active_session) return EResult::k_EResultFail; // TODO is this correct? + if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? auto &table = active_session->tables.back().second; if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? @@ -380,18 +412,20 @@ EResult Steam_GameStats::AddRowAtributeString( uint64 ulRowID, const char *pstrN if (att->type != AttributeType_t::Str) return EResult::k_EResultFail; // TODO is this correct? att->s_data = pstrData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddRowAttributeFloat( uint64 ulRowID, const char *pstrName, float fData ) { - PRINT_DEBUG("%llu, '%s', %f", ulRowID, pstrName, fData); + PRINT_DEBUG("%llu, '%s'=%f", ulRowID, pstrName, fData); std::lock_guard lock(global_mutex); if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + if (!active_session) return EResult::k_EResultFail; // TODO is this correct? + if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? auto &table = active_session->tables.back().second; if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? @@ -400,36 +434,40 @@ EResult Steam_GameStats::AddRowAttributeFloat( uint64 ulRowID, const char *pstrN if (att->type != AttributeType_t::Float) return EResult::k_EResultFail; // TODO is this correct? att->f_data = fData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddSessionAttributeInt64( uint64 ulSessionID, const char *pstrName, int64 llData ) { - PRINT_DEBUG("%llu, '%s', %lli", ulSessionID, pstrName, llData); + PRINT_DEBUG("%llu, '%s'=%lli", ulSessionID, pstrName, llData); std::lock_guard lock(global_mutex); - if (ulSessionID == 0 || ulSessionID > sessions.size() || !pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? + auto session_it = sessions.find(ulSessionID); + if (sessions.end() == session_it) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto& session = sessions[static_cast(ulSessionID - 1)]; + auto& session = session_it->second; if (session.ended) return EResult::k_EResultExpired; // TODO is this correct? auto att = get_or_create_session_att(pstrName, session, AttributeType_t::Int64); if (att->type != AttributeType_t::Int64) return EResult::k_EResultFail; // TODO is this correct? att->ll_data = llData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrName, int64 llData ) { - PRINT_DEBUG("%llu, '%s', %lli", ulRowID, pstrName, llData); + PRINT_DEBUG("%llu, '%s'=%lli", ulRowID, pstrName, llData); std::lock_guard lock(global_mutex); if (!pstrName) return EResult::k_EResultInvalidParam; // TODO is this correct? - auto active_session = std::find_if(sessions.rbegin(), sessions.rend(), [](const Session_t &item){ return !item.ended; }); - if (sessions.rend() == active_session) return EResult::k_EResultFail; // TODO is this correct? + auto active_session = get_last_active_session(); + if (!active_session) return EResult::k_EResultFail; // TODO is this correct? + if (active_session->tables.empty()) return EResult::k_EResultFail; // TODO is this correct? auto &table = active_session->tables.back().second; if (ulRowID >= table.rows.size()) return EResult::k_EResultInvalidParam; // TODO is this correct? @@ -438,6 +476,7 @@ EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrN if (att->type != AttributeType_t::Int64) return EResult::k_EResultFail; // TODO is this correct? att->ll_data = llData; + PRINT_DEBUG("added successfully"); return EResult::k_EResultOK; } @@ -447,7 +486,28 @@ EResult Steam_GameStats::AddRowAttributeInt64( uint64 ulRowID, const char *pstrN void Steam_GameStats::steam_run_callback() { - // nothing + // remove ended sessions that are inactive + auto session_it = sessions.begin(); + auto now_epoch_sec = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ); + while (sessions.end() != session_it) { + bool should_remove = false; + + auto &session = session_it->second; + if (session.ended) { + if ( (now_epoch_sec.count() - (long long)session.rtTimeEnded) >= MAX_DEAD_SESSION_SECONDS ) { + should_remove = true; + PRINT_DEBUG("removing outdated session id=%llu", session_it->first); + } + } + + if (should_remove) { + session_it = sessions.erase(session_it); + } else { + ++session_it; + } + } } diff --git a/dll/steam_http.cpp b/dll/steam_http.cpp index 8f2adfc8..d78d9c9a 100644 --- a/dll/steam_http.cpp +++ b/dll/steam_http.cpp @@ -180,7 +180,9 @@ void Steam_HTTP::online_http_request(Steam_Http_Request *request, SteamAPICall_t data.m_eStatusCode = k_EHTTPStatusCode200OK; } - if (pCallHandle) *pCallHandle = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); + auto callres = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); + if (pCallHandle) *pCallHandle = callres; + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1); }; @@ -356,7 +358,9 @@ bool Steam_HTTP::SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pC data.m_eStatusCode = k_EHTTPStatusCode200OK; } - if (pCallHandle) *pCallHandle = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); + auto callres = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1); + if (pCallHandle) *pCallHandle = callres; + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1); } diff --git a/dll/steam_networking_socketsserialized.cpp b/dll/steam_networking_socketsserialized.cpp index 468e0472..4f13373d 100644 --- a/dll/steam_networking_socketsserialized.cpp +++ b/dll/steam_networking_socketsserialized.cpp @@ -72,7 +72,9 @@ SteamAPICall_t Steam_Networking_Sockets_Serialized::GetCertAsync() struct SteamNetworkingSocketsCert_t data = {}; data.m_eResult = k_EResultOK; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } int Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner ) diff --git a/dll/steam_remote_storage.cpp b/dll/steam_remote_storage.cpp index c1684b48..7ab28fbf 100644 --- a/dll/steam_remote_storage.cpp +++ b/dll/steam_remote_storage.cpp @@ -18,14 +18,13 @@ #include "dll/steam_remote_storage.h" -Downloaded_File::Downloaded_File() -{ +Downloaded_File::Downloaded_File(DownloadSource src) + :source(src) +{ } -} - -Downloaded_File::~Downloaded_File() +Downloaded_File::DownloadSource Downloaded_File::get_source() const { - + return source; } static void copy_file(const std::string &src_filepath, const std::string &dst_filepath) @@ -43,12 +42,13 @@ static void copy_file(const std::string &src_filepath, const std::string &dst_fi } catch(...) {} } -Steam_Remote_Storage::Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results) +Steam_Remote_Storage::Steam_Remote_Storage(class Settings *settings, class Ugc_Remote_Storage_Bridge *ugc_bridge, class Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks) { this->settings = settings; this->ugc_bridge = ugc_bridge; this->local_storage = local_storage; this->callback_results = callback_results; + this->callbacks = callbacks; steam_cloud_enabled = true; } @@ -101,7 +101,9 @@ SteamAPICall_t Steam_Remote_Storage::FileWriteAsync( const char *pchFile, const RemoteStorageFileWriteAsyncComplete_t data; data.m_eResult = success ? k_EResultOK : k_EResultFail; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.01); + return ret; } @@ -130,7 +132,9 @@ SteamAPICall_t Steam_Remote_Storage::FileReadAsync( const char *pchFile, uint32 a_read.size = size; async_reads.push_back(a_read); + callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data), 0.0); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.0); return data.m_hFileReadAsync; } @@ -161,7 +165,7 @@ bool Steam_Remote_Storage::FileReadAsyncComplete( SteamAPICall_t hReadCall, void bool Steam_Remote_Storage::FileForget( const char *pchFile ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG("'%s'", pchFile); std::lock_guard lock(global_mutex); if (!pchFile || !pchFile[0]) return false; @@ -170,7 +174,7 @@ bool Steam_Remote_Storage::FileForget( const char *pchFile ) bool Steam_Remote_Storage::FileDelete( const char *pchFile ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG("'%s'", pchFile); std::lock_guard lock(global_mutex); if (!pchFile || !pchFile[0]) return false; @@ -180,7 +184,7 @@ bool Steam_Remote_Storage::FileDelete( const char *pchFile ) STEAM_CALL_RESULT( RemoteStorageFileShareResult_t ) SteamAPICall_t Steam_Remote_Storage::FileShare( const char *pchFile ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG("'%s'", pchFile); std::lock_guard lock(global_mutex); if (!pchFile || !pchFile[0]) return k_uAPICallInvalid; @@ -194,12 +198,14 @@ SteamAPICall_t Steam_Remote_Storage::FileShare( const char *pchFile ) data.m_eResult = k_EResultFileNotFound; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } bool Steam_Remote_Storage::SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG("'%s' %i", pchFile, (int)eRemoteStoragePlatform); std::lock_guard lock(global_mutex); if (!pchFile || !pchFile[0]) return false; @@ -210,13 +216,15 @@ bool Steam_Remote_Storage::SetSyncPlatforms( const char *pchFile, ERemoteStorage // file operations that cause network IO UGCFileWriteStreamHandle_t Steam_Remote_Storage::FileWriteStreamOpen( const char *pchFile ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG("'%s'", pchFile); std::lock_guard lock(global_mutex); if (!pchFile || !pchFile[0]) return k_UGCFileStreamHandleInvalid; - static UGCFileWriteStreamHandle_t handle; + static UGCFileWriteStreamHandle_t handle = 0; ++handle; - struct Stream_Write stream_write; + if (!handle) handle = 1; + + struct Stream_Write stream_write{}; stream_write.file_name = std::string(pchFile); stream_write.write_stream_handle = handle; stream_writes.push_back(stream_write); @@ -412,18 +420,20 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownload( UGCHandle_t hContent, uint32 u RemoteStorageDownloadUGCResult_t data{}; data.m_hFile = hContent; + data.m_nAppID = settings->get_local_game_id().AppID(); if (shared_files.count(hContent)) { data.m_eResult = k_EResultOK; - data.m_nAppID = settings->get_local_game_id().AppID(); data.m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64(); data.m_nSizeInBytes = local_storage->file_size(Local_Storage::remote_storage_folder, shared_files[hContent]); shared_files[hContent].copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1); + PRINT_DEBUG(" FileShare data.m_pchFileName = '%s'", data.m_pchFileName); - downloaded_files[hContent].source = Downloaded_File::DownloadSource::AfterFileShare; - downloaded_files[hContent].file = shared_files[hContent]; - downloaded_files[hContent].total_size = data.m_nSizeInBytes; + auto [ele_itr, _] = downloaded_files.insert_or_assign(hContent, Downloaded_File::DownloadSource::AfterFileShare); + auto &ele = ele_itr->second; + ele.file = shared_files[hContent]; + ele.total_size = data.m_nSizeInBytes; } else if (auto query_res = ugc_bridge->get_ugc_query_result(hContent)) { auto mod = settings->getMod(query_res.value().mod_id); auto &mod_name = query_res.value().is_primary_file @@ -434,24 +444,25 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownload( UGCHandle_t hContent, uint32 u : mod.previewFileSize; data.m_eResult = k_EResultOK; - 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); + PRINT_DEBUG(" QueryUGCRequest data.m_pchFileName = '%s'", data.m_pchFileName); - downloaded_files[hContent].source = Downloaded_File::DownloadSource::AfterSendQueryUGCRequest; - downloaded_files[hContent].file = mod_name; - downloaded_files[hContent].total_size = mod_size; - - downloaded_files[hContent].mod_query_info = query_res.value(); - + auto [ele_itr, _] = downloaded_files.insert_or_assign(hContent, Downloaded_File::DownloadSource::AfterSendQueryUGCRequest); + auto &ele = ele_itr->second; + ele.file = mod_name; + ele.total_size = mod_size; + ele.mod_query_info = query_res.value(); } else { data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t ) @@ -512,7 +523,7 @@ int32 Steam_Remote_Storage::UGCRead( UGCHandle_t hContent, void *pvData, int32 c Downloaded_File &dwf = f_itr->second; // depending on the download source, we have to decide where to grab the content/data - switch (dwf.source) + switch (dwf.get_source()) { case Downloaded_File::DownloadSource::AfterFileShare: { PRINT_DEBUG(" source = AfterFileShare '%s'", dwf.file.c_str()); @@ -523,14 +534,14 @@ int32 Steam_Remote_Storage::UGCRead( UGCHandle_t hContent, void *pvData, int32 c case Downloaded_File::DownloadSource::AfterSendQueryUGCRequest: case Downloaded_File::DownloadSource::FromUGCDownloadToLocation: { - PRINT_DEBUG(" source = AfterSendQueryUGCRequest || FromUGCDownloadToLocation [%i]", (int)dwf.source); + PRINT_DEBUG(" source = AfterSendQueryUGCRequest || FromUGCDownloadToLocation [%i]", (int)dwf.get_source()); auto mod = settings->getMod(dwf.mod_query_info.mod_id); auto &mod_name = dwf.mod_query_info.is_primary_file ? mod.primaryFileName : mod.previewFileName; std::string mod_fullpath{}; - if (dwf.source == Downloaded_File::DownloadSource::AfterSendQueryUGCRequest) { + if (dwf.get_source() == Downloaded_File::DownloadSource::AfterSendQueryUGCRequest) { std::string mod_base_path = dwf.mod_query_info.is_primary_file ? mod.path : Local_Storage::get_game_settings_path() + "mod_images" + PATH_SEPARATOR + std::to_string(mod.id); @@ -547,7 +558,7 @@ int32 Steam_Remote_Storage::UGCRead( UGCHandle_t hContent, void *pvData, int32 c break; default: - PRINT_DEBUG(" unhandled download source %i", (int)dwf.source); + PRINT_DEBUG(" unhandled download source %i", (int)dwf.get_source()); return -1; //TODO: is this the right return value? break; } @@ -790,12 +801,13 @@ SteamAPICall_t Steam_Remote_Storage::GetPublishedFileDetails( PublishedFileId_t mod.tags.copy(data.m_rgchTags, sizeof(data.m_rgchTags) - 1); mod.title.copy(data.m_rgchTitle, sizeof(data.m_rgchTitle) - 1); mod.workshopItemURL.copy(data.m_rgchURL, sizeof(data.m_rgchURL) - 1); - } else { data.m_eResult = EResult::k_EResultFail; // TODO is this correct? } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; // return 0; /* @@ -830,6 +842,7 @@ SteamAPICall_t Steam_Remote_Storage::EnumerateUserPublishedFiles( uint32 unStart PRINT_DEBUG("TODO %u", unStartIndex); // TODO is this implementation correct? std::lock_guard lock(global_mutex); + RemoteStorageEnumerateUserPublishedFilesResult_t data{}; // collect all published mods by this user @@ -861,7 +874,9 @@ SteamAPICall_t Steam_Remote_Storage::EnumerateUserPublishedFiles( uint32 unStart data.m_nResultsReturned = iterated; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t ) @@ -882,7 +897,9 @@ SteamAPICall_t Steam_Remote_Storage::SubscribePublishedFile( PublishedFileId_t u data.m_eResult = EResult::k_EResultFail; // TODO is this correct? } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t ) @@ -914,7 +931,9 @@ SteamAPICall_t Steam_Remote_Storage::EnumerateUserSubscribedFiles( uint32 unStar data.m_nResultsReturned = iterated; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t ) @@ -935,7 +954,9 @@ SteamAPICall_t Steam_Remote_Storage::UnsubscribePublishedFile( PublishedFileId_t data.m_eResult = k_EResultFail; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } bool Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription ) @@ -967,7 +988,9 @@ SteamAPICall_t Steam_Remote_Storage::GetPublishedItemVoteDetails( PublishedFileI data.m_eResult = EResult::k_EResultFail; // TODO is this correct? } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t ) @@ -993,7 +1016,9 @@ SteamAPICall_t Steam_Remote_Storage::UpdateUserPublishedItemVote( PublishedFileI data.m_eResult = EResult::k_EResultFail; // TODO is this correct? } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t ) @@ -1022,22 +1047,26 @@ SteamAPICall_t Steam_Remote_Storage::GetUserPublishedItemVoteDetails( PublishedF data.m_eResult = EResult::k_EResultFail; // TODO is this correct? } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); - - return 0; + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t ) SteamAPICall_t Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles( CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG_TODO(); std::lock_guard lock(global_mutex); + RemoteStorageEnumerateUserPublishedFilesResult_t data{}; data.m_eResult = k_EResultOK; data.m_nResultsReturned = 0; data.m_nTotalResultCount = 0; //data.m_rgPublishedFileId; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t ) @@ -1086,12 +1115,15 @@ SteamAPICall_t Steam_Remote_Storage::EnumeratePublishedWorkshopFiles( EWorkshopE PRINT_DEBUG_TODO(); // TODO is this implementation correct? std::lock_guard lock(global_mutex); + RemoteStorageEnumerateWorkshopFilesResult_t data{}; data.m_eResult = EResult::k_EResultOK; data.m_nResultsReturned = 0; data.m_nTotalResultCount = 0; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } @@ -1134,18 +1166,20 @@ SteamAPICall_t Steam_Remote_Storage::UGCDownloadToLocation( UGCHandle_t hContent copy_file(mod_fullpath, pchLocation); // TODO not sure about this though - downloaded_files[hContent].source = Downloaded_File::DownloadSource::FromUGCDownloadToLocation; - downloaded_files[hContent].file = mod_name; - downloaded_files[hContent].total_size = mod_size; - - downloaded_files[hContent].mod_query_info = query_res.value(); - downloaded_files[hContent].download_to_location_fullpath = pchLocation; + auto [ele_itr, _] = downloaded_files.insert_or_assign(hContent, Downloaded_File::DownloadSource::FromUGCDownloadToLocation); + auto &ele = ele_itr->second; + ele.file = mod_name; + ele.total_size = mod_size; + ele.mod_query_info = query_res.value(); + ele.download_to_location_fullpath = pchLocation; } else { data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } // Cloud dynamic state change notification diff --git a/dll/steam_timeline.cpp b/dll/steam_timeline.cpp index 9aa13c6a..21561212 100644 --- a/dll/steam_timeline.cpp +++ b/dll/steam_timeline.cpp @@ -47,6 +47,9 @@ Steam_Timeline::Steam_Timeline(class Settings *settings, class Networking *netwo // this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Timeline::steam_callback, this); this->run_every_runcb->add(&Steam_Timeline::steam_run_every_runcb, this); + + // timeline starts with a default event as seen here: https://www.youtube.com/watch?v=YwBD0E4-EsI + SetTimelineGameMode(ETimelineGameMode::k_ETimelineGameMode_Invalid); } Steam_Timeline::~Steam_Timeline() @@ -55,6 +58,20 @@ Steam_Timeline::~Steam_Timeline() this->run_every_runcb->remove(&Steam_Timeline::steam_run_every_runcb, this); } + +// Sets a description for the current game state in the timeline. These help the user to find specific +// moments in the timeline when saving clips. Setting a new state description replaces any previous +// description. +// +// Examples could include: +// * Where the user is in the world in a single player game +// * Which round is happening in a multiplayer game +// * The current score for a sports game +// +// Parameters: +// - pchDescription: provide a localized string in the language returned by SteamUtils()->GetSteamUILanguage() +// - flTimeDelta: The time offset in seconds to apply to this event. Negative times indicate an +// event that happened in the past. void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, float flTimeDelta ) { PRINT_DEBUG("'%s' %f", pchDescription, flTimeDelta); @@ -78,7 +95,6 @@ void Steam_Timeline::SetTimelineStateDescription( const char *pchDescription, fl } - void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta ) { PRINT_DEBUG("%f", flTimeDelta); @@ -98,10 +114,35 @@ void Steam_Timeline::ClearTimelineStateDescription( float flTimeDelta ) } - +// Use this to mark an event on the Timeline. The event can be instantaneous or take some amount of time +// to complete, depending on the value passed in flDurationSeconds +// +// Examples could include: +// * a boss battle +// * a cut scene +// * a large team fight +// * picking up a new weapon or ammunition +// * scoring a goal +// +// Parameters: +// +// - pchIcon: specify the name of the icon uploaded through the Steamworks Partner Site for your title +// or one of the provided icons that start with steam_ +// - pchTitle & pchDescription: provide a localized string in the language returned by +// SteamUtils()->GetSteamUILanguage() +// - unPriority: specify how important this range is compared to other markers provided by the game. +// Ranges with larger priority values will be displayed more prominently in the UI. This value +// may be between 0 and k_unMaxTimelinePriority. +// - flStartOffsetSeconds: The time that this range started relative to now. Negative times +// indicate an event that happened in the past. +// - flDurationSeconds: How long the time range should be in seconds. For instantaneous events, this +// should be 0 +// - ePossibleClip: By setting this parameter to Featured or Standard, the game indicates to Steam that it +// would be appropriate to offer this range as a clip to the user. For instantaneous events, the +// suggested clip will be for a short time before and after the event itself. void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle, const char *pchDescription, uint32 unPriority, float flStartOffsetSeconds, float flDurationSeconds, ETimelineEventClipPriority ePossibleClip ) { - PRINT_DEBUG("'%s' | '%s' - '%s', %u, [%f, %f) %i", pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, (int)ePossibleClip); + PRINT_DEBUG("icon='%s' | '%s' - '%s', %u, [%f, %f) %i", pchIcon, pchTitle, pchDescription, unPriority, flStartOffsetSeconds, flDurationSeconds, (int)ePossibleClip); std::lock_guard lock(global_mutex); auto &new_event = timeline_events.emplace_back(TimelineEvent_t{}); @@ -112,24 +153,28 @@ void Steam_Timeline::AddTimelineEvent( const char *pchIcon, const char *pchTitle new_event.flStartOffsetSeconds = flStartOffsetSeconds; - // for instantanious event with flDurationSeconds=0 steam creates 8 sec clip - if (static_cast(flDurationSeconds * 1000) <= 100) { // <= 100ms - flDurationSeconds = 8; + // make events last at least 1 sec + if (static_cast(flDurationSeconds * 1000) < 1000) { // < 1000ms + flDurationSeconds = 1; + } + // for events with priority=ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured steam creates ~8 sec clip + // seen here: https://www.youtube.com/watch?v=YwBD0E4-EsI + if (flDurationSeconds < PRIORITY_CLIP_MIN_SEC && ePossibleClip == ETimelineEventClipPriority::k_ETimelineEventClipPriority_Featured) { + flDurationSeconds = PRIORITY_CLIP_MIN_SEC; } new_event.flDurationSeconds = flDurationSeconds; new_event.ePossibleClip = ePossibleClip; } - +// Changes the color of the timeline bar. See ETimelineGameMode comments for how to use each value void Steam_Timeline::SetTimelineGameMode( ETimelineGameMode eMode ) { PRINT_DEBUG("%i", (int)eMode); std::lock_guard lock(global_mutex); - if (timeline_states.empty()) return; - - timeline_states.back().bar_color = eMode; + auto &new_timeline_state = timeline_states.emplace_back(TimelineState_t{}); + new_timeline_state.bar_color = eMode; } diff --git a/dll/steam_ugc.cpp b/dll/steam_ugc.cpp index 3ebe733c..682a033c 100644 --- a/dll/steam_ugc.cpp +++ b/dll/steam_ugc.cpp @@ -1123,7 +1123,10 @@ SteamAPICall_t Steam_UGC::SetUserItemVote( PublishedFileId_t nPublishedFileID, b ++mod.votesDown; } settings->addModDetails(nPublishedFileID, mod); - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } @@ -1141,7 +1144,10 @@ SteamAPICall_t Steam_UGC::GetUserItemVote( PublishedFileId_t nPublishedFileID ) data.m_bVotedDown = mod.votesDown; data.m_bVotedUp = mod.votesUp; data.m_bVoteSkipped = true; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } @@ -1166,7 +1172,9 @@ SteamAPICall_t Steam_UGC::AddItemToFavorites( AppId_t nAppId, PublishedFileId_t data.m_eResult = EResult::k_EResultOK; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } @@ -1191,7 +1199,9 @@ SteamAPICall_t Steam_UGC::RemoveItemFromFavorites( AppId_t nAppId, PublishedFile data.m_eResult = EResult::k_EResultOK; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } @@ -1209,7 +1219,9 @@ SteamAPICall_t Steam_UGC::SubscribeItem( PublishedFileId_t nPublishedFileID ) } else { data.m_eResult = k_EResultFail; } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } // subscribe to this item, will be installed ASAP @@ -1228,7 +1240,9 @@ SteamAPICall_t Steam_UGC::UnsubscribeItem( PublishedFileId_t nPublishedFileID ) ugc_bridge->remove_subbed_mod(nPublishedFileID); } - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } // unsubscribe from this item, will be uninstalled after game quits @@ -1383,7 +1397,10 @@ SteamAPICall_t Steam_UGC::StartPlaytimeTracking( PublishedFileId_t *pvecPublishe std::lock_guard lock(global_mutex); StopPlaytimeTrackingResult_t data; data.m_eResult = k_EResultOK; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t ) @@ -1393,7 +1410,10 @@ SteamAPICall_t Steam_UGC::StopPlaytimeTracking( PublishedFileId_t *pvecPublished std::lock_guard lock(global_mutex); StopPlaytimeTrackingResult_t data; data.m_eResult = k_EResultOK; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t ) @@ -1403,7 +1423,10 @@ SteamAPICall_t Steam_UGC::StopPlaytimeTrackingForAllItems() std::lock_guard lock(global_mutex); StopPlaytimeTrackingResult_t data; data.m_eResult = k_EResultOK; - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } diff --git a/dll/steam_user.cpp b/dll/steam_user.cpp index d0437173..8a380a15 100644 --- a/dll/steam_user.cpp +++ b/dll/steam_user.cpp @@ -126,9 +126,11 @@ void Steam_User::RefreshSteam2Login() bool Steam_User::GetUserDataFolder( char *pchBuffer, int cubBuffer ) { PRINT_DEBUG_ENTRY(); - if (!cubBuffer) return false; + if (!cubBuffer || cubBuffer <= 0) return false; std::string user_data = local_storage->get_path(Local_Storage::user_data_storage); + if (static_cast(cubBuffer) <= user_data.size()) return false; + strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1); pchBuffer[cubBuffer - 1] = 0; return true; @@ -201,6 +203,7 @@ EVoiceResult Steam_User::GetVoice( bool bWantCompressed, void *pDestBuffer, uint { PRINT_DEBUG_ENTRY(); if (!recording) return k_EVoiceResultNotRecording; + double seconds = std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - last_get_voice).count(); if (bWantCompressed) { uint32 towrite = static_cast(seconds * 1024.0 * 64.0 / 8.0); @@ -239,6 +242,7 @@ EVoiceResult Steam_User::DecompressVoice( const void *pCompressed, uint32 cbComp { PRINT_DEBUG_ENTRY(); if (!recording) return k_EVoiceResultNotRecording; + uint32 uncompressed = static_cast((double)cbCompressed * ((double)nDesiredSampleRate / 8192.0)); if(nBytesWritten) *nBytesWritten = uncompressed; if (uncompressed > cbDestBufferSize) uncompressed = cbDestBufferSize; @@ -296,7 +300,7 @@ HAuthTicket Steam_User::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, ui // the ticket will be returned in callback GetTicketForWebApiResponse_t HAuthTicket Steam_User::GetAuthTicketForWebApi( const char *pchIdentity ) { - PRINT_DEBUG("%s", pchIdentity); + PRINT_DEBUG("'%s'", pchIdentity); std::lock_guard lock(global_mutex); return auth_manager->getWebApiTicket(pchIdentity); @@ -377,7 +381,7 @@ void Steam_User::AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, u STEAM_CALL_RESULT( EncryptedAppTicketResponse_t ) SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude ) { - PRINT_DEBUG("%i", cbDataToInclude); + PRINT_DEBUG("%i %p", cbDataToInclude, pDataToInclude); std::lock_guard lock(global_mutex); EncryptedAppTicketResponse_t data; data.m_eResult = k_EResultOK; @@ -420,7 +424,9 @@ SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int encrypted_app_ticket = pb.SerializeAsString(); - return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + return ret; } // retrieve a finished ticket @@ -439,6 +445,7 @@ bool Steam_User::GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 * if (ticket_size > static_cast(cbMaxTicket)) return false; encrypted_app_ticket.copy((char *)pTicket, cbMaxTicket); + PRINT_DEBUG("copied successfully"); return true; } @@ -471,7 +478,7 @@ int Steam_User::GetPlayerSteamLevel() STEAM_CALL_RESULT( StoreAuthURLResponse_t ) SteamAPICall_t Steam_User::RequestStoreAuthURL( const char *pchRedirectURL ) { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG_TODO(); return 0; } @@ -506,7 +513,7 @@ bool Steam_User::BIsPhoneRequiringVerification() STEAM_CALL_RESULT( MarketEligibilityResponse_t ) SteamAPICall_t Steam_User::GetMarketEligibility() { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG_TODO(); return 0; } @@ -514,7 +521,7 @@ SteamAPICall_t Steam_User::GetMarketEligibility() STEAM_CALL_RESULT( DurationControl_t ) SteamAPICall_t Steam_User::GetDurationControl() { - PRINT_DEBUG_ENTRY(); + PRINT_DEBUG_TODO(); return 0; } diff --git a/dll/steam_utils.cpp b/dll/steam_utils.cpp index 050b2543..afa4eb39 100644 --- a/dll/steam_utils.cpp +++ b/dll/steam_utils.cpp @@ -255,11 +255,10 @@ SteamAPICall_t Steam_Utils::CheckFileSignature( const char *szFileName ) { PRINT_DEBUG("'%s'", szFileName); std::lock_guard lock(global_mutex); - CheckFileSignature_t data; + CheckFileSignature_t data{}; data.m_eCheckFileSignature = k_ECheckFileSignatureValidSignature; auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data)); - // TODO callback too? - // callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); return ret; } diff --git a/helpers/common_helpers.cpp b/helpers/common_helpers.cpp index e3f7f9c4..198dc763 100644 --- a/helpers/common_helpers.cpp +++ b/helpers/common_helpers.cpp @@ -290,7 +290,7 @@ std::wstring common_helpers::to_lower(std::wstring_view wstr) { if (wstr.empty()) return {}; - std::wstring _wstr(wstr.size(), '\0'); + std::wstring _wstr(wstr.size(), L'\0'); std::transform(wstr.begin(), wstr.end(), _wstr.begin(), [](wchar_t c) { return std::tolower(c); }); return _wstr; } @@ -308,7 +308,7 @@ std::wstring common_helpers::to_upper(std::wstring_view wstr) { if (wstr.empty()) return {}; - std::wstring _wstr(wstr.size(), '\0'); + std::wstring _wstr(wstr.size(), L'\0'); std::transform(wstr.begin(), wstr.end(), _wstr.begin(), [](wchar_t c) { return std::toupper(c); }); return _wstr; } diff --git a/post_build/steam_settings.EXAMPLE/configs.app.EXAMPLE.ini b/post_build/steam_settings.EXAMPLE/configs.app.EXAMPLE.ini index 3e82e8c3..063d82f8 100644 --- a/post_build/steam_settings.EXAMPLE/configs.app.EXAMPLE.ini +++ b/post_build/steam_settings.EXAMPLE/configs.app.EXAMPLE.ini @@ -4,14 +4,17 @@ [app::general] # by default the emu will report a `non-beta` branch when the game calls `Steam_Apps::GetCurrentBetaName()` -# make the game/app think we're playing on a beta branch +# 1=make the game/app think we're playing on a beta branch +# default=0 is_beta_branch=0 # the name of the current branch, this must also exist in branches.json # otherwise will be ignored by the emu and the default 'public' branch will be used +# default=public branch_name=public [app::dlcs] -# should the emu report all DLCs as unlocked +# 1=report all DLCs as unlocked +# 0=report only the DLCs mentioned # some games check for "hidden" DLCs, hence this should be set to 1 in that case # but other games detect emus by querying for a fake/bad DLC, hence this should be set to 0 in that case # default=1 diff --git a/post_build/steam_settings.EXAMPLE/configs.main.EXAMPLE.ini b/post_build/steam_settings.EXAMPLE/configs.main.EXAMPLE.ini index ff021287..9d688bb9 100644 --- a/post_build/steam_settings.EXAMPLE/configs.main.EXAMPLE.ini +++ b/post_build/steam_settings.EXAMPLE/configs.main.EXAMPLE.ini @@ -3,80 +3,105 @@ # ############################################################################## # [main::general] -# generate new app auth ticket +# 1=generate newer version of auth ticket, used by some modern apps +# default=0 new_app_ticket=1 -# generate/embed GC token inside new App Ticket +# 1=generate/embed Game Coordinator token inside the new auth ticket +# default=0 gc_token=1 -# pretend the app is running on a steam deck +# 1=pretend the app is running on a steam deck +# default=0 steam_deck=0 -# enable avatar functionality +# 1=enable avatar functionality +# default=0 enable_account_avatar=0 -# prevent Steam_User_Stats::FindLeaderboard() from always succeeding and creating the unknown leaderboard -# not recommended to disable this +# 1=prevent `Steam_User_Stats::FindLeaderboard()` from always succeeding and creating the unknown leaderboard +# not recommended to enable this +# default=0 disable_leaderboards_create_unknown=0 # the emu will only save/update stats defined by the user, unknown stats requested or updated by the game will be rejected -# set this to 1 to allow unknown stats +# 1=allow unknown stats to be saved/updated # default=0 allow_unknown_stats=0 # whenever a game updates a stat which is tied to an achievement progress, the emu will save that progress immediately # but some games will update the stat very frequently (with lower & higher values) resulting in a spam of disk writes or overlay notifications -# set this to 0 to save any stat achievement progress value (higher or lower) -# this has no impact on the stat itself, only the achievement progress of a stat which is tied to an achievement +# 0=save any stat achievement progress value (higher or lower) +# 1=save stat achievement progress value only if it is higher than the current one +# this has no impact on the stat itself, only the achievement progress # also has no impact on the functions which directly change stats, achievements, or achievements progress # default=1 save_only_higher_stat_achievement_progress=1 -# synchronize user stats/achievements with game servers as soon as possible instead of caching them until the next call to `Steam_RunCallbacks()` -# not recommended +# 1=synchronize user stats/achievements with game servers as soon as possible instead of caching them until the next call to `Steam_RunCallbacks()` +# not recommended to enable this +# default=0 immediate_gameserver_stats=0 -# use the proper type of the server list (internet, friends, etc...) when requested by the game -# otherwise, the emu will always return the type "LAN server" -# not recommended +# 1=use the proper type of the server list (internet, friends, etc...) when requested by the game +# 0=always return the type of the server list as "LAN server" +# not recommended to enable this +# default=0 matchmaking_server_list_actual_type=0 -# grab the server details for match making using an actual server query -# not recommended +# 1=grab the server details for match making using an actual server query +# 0=use the info from the local network messages shared between client/server +# not recommended to enable this, currently breaks some games +# default=0 matchmaking_server_details_via_source_query=0 # very basic crash logger/printer # this is intended to debug some annoying scenarios, and best used with the debug build of the emu +# default= crash_printer_location=./path/relative/to/dll/crashes.txt [main::connectivity] -# prevent hooking OS networking APIs and allow any external requests -# only used by the experimental builds on Windows +# 1=prevent hooking OS networking APIs and allow any external requests +# only used by the experimental builds on **Windows** +# default=0 disable_lan_only=0 -# disable all steam networking interface functionality +# 1=disable all steam networking interface functionality # this won't prevent games/apps from making external requests # networking related functionality like lobbies or those that launch a server in the background will not work +# default=0 disable_networking=0 # change the UDP/TCP port the emulator listens on, you should probably not change this because everyone needs to use the same port or you won't find yourselves on the network +# default=47584 listen_port=47584 -# pretend steam is running in offline mode +# 1=pretend steam is running in offline mode, mainly affects the function `ISteamUser::BLoggedOn()` # Some games that connect to online servers might only work if the steam emu behaves like steam is in offline mode +# default=0 offline=0 -# prevent sharing stats and achievements with any game server, -# this also disables the interface ISteamGameServerStats +# 1=prevent sharing stats and achievements with any game server, this also disables the interface ISteamGameServerStats +# default=0 disable_sharing_stats_with_gameserver=0 -# do not send server details to the server browser, only works for game servers +# 1=do not send server details to the server browser, only works for game servers +# default=0 disable_source_query=0 -# enable sharing leaderboards scores with people playing the same game on the same network +# 1=enable sharing leaderboards scores with people playing the same game on the same network +# not ideal and synchronization isn't perfect +# default=0 share_leaderboards_over_network=0 -# prevent lobby creation in steam matchmaking interface +# 1=prevent lobby creation in the steam matchmaking interface +# default=0 disable_lobby_creation=0 -# attempt to download external HTTP(S) requests made via Steam_HTTP::SendHTTPRequest() inside "steam_settings/http/" +# 1=attempt to download external HTTP(S) requests made via Steam_HTTP::SendHTTPRequest() inside "steam_settings/http/" # make sure to: # * set disable_lan_only=1 # * set disable_networking=0 # this will **not** work if the app is using native/OS web APIs +# default=0 download_steamhttp_requests=0 # mostly workarounds for specific problems [main::misc] -# force SetAchievement() to always return true +# 1=force `ISteamUserStats::SetAchievement()` to always return true +# this is a workaround for some games that otherwise won't work +# default=0 achievements_bypass=0 -# force the function Steam_HTTP::SendHTTPRequest() to always succeed +# force the function `Steam_HTTP::SendHTTPRequest()` to always succeed +# default=0 force_steamhttp_success=0 -# env var SteamOverlayGameId breaks Steam Input when the game is added to Steam as a non-steam game +# env var `SteamOverlayGameId` breaks Steam Input when the game is added to Steam as a non-steam game +# 1=don't write this env var, allowing Steam Input to work +# default=0 disable_steamoverlaygameid_env_var=0 -# add many Steam apps to the list of owned DLCs and the emu's list of installed app IDs +# 1=add many Steam apps to the list of owned DLCs and the emu's list of installed app IDs # useful for many Source-based games # https://developer.valvesoftware.com/wiki/Steam_Application_IDs # https://developer.valvesoftware.com/wiki/Dedicated_Servers_List diff --git a/post_build/steam_settings.EXAMPLE/configs.overlay.EXAMPLE.ini b/post_build/steam_settings.EXAMPLE/configs.overlay.EXAMPLE.ini index ca7850d3..55e81a5e 100644 --- a/post_build/steam_settings.EXAMPLE/configs.overlay.EXAMPLE.ini +++ b/post_build/steam_settings.EXAMPLE/configs.overlay.EXAMPLE.ini @@ -11,7 +11,8 @@ # ############################################################################## # [overlay::general] -# enable the experimental overlay, might cause crashes +# 1=enable the experimental overlay, might cause crashes +# default=0 enable_experimental_overlay=0 # amount of time to wait before attempting to detect and hook the renderer (DirectX, OpenGL, etc...) # default=0 @@ -19,26 +20,34 @@ hook_delay_sec=0 # timeout for the renderer detector # default=15 renderer_detector_timeout_sec=15 -# disable the achievements notifications +# 1=disable the achievements notifications +# default=0 disable_achievement_notification=0 -# disable friends invitations and messages notifications +# 1=disable friends invitations and messages notifications +# default=0 disable_friend_notification=0 -# disable showing notifications for achievements progress +# 1=disable showing notifications for achievements progress +# default=0 disable_achievement_progress=0 -# disable any warning in the overlay +# 1=disable any warning in the overlay +# default=0 disable_warning_any=0 -# disable the bad app ID warning in the overlay +# 1=disable the bad app ID warning in the overlay +# default=0 disable_warning_bad_appid=0 -# disable the local_save warning in the overlay +# 1=disable the local_save warning in the overlay +# default=0 disable_warning_local_save=0 [overlay::appearance] # load custom TrueType font from a path, it could be absolute, or relative # relative paths will be looked up inside the local folder "steam_settings/fonts" first, # if that wasn't found, it will be looked up inside the global folder "GSE Settings/settings/fonts" +# default= Font_Override=Roboto-Medium.ttf # global font size # for built-in font, multiple of 16 is recommended. e.g. 16 32... +# default=16.0 Font_Size=20.0 # achievement icon size @@ -75,6 +84,7 @@ Notification_Duration_Chat=4.0 # format for the achievement unlock date/time, limited to 79 characters # if the output formatted string exceeded this limit, the builtin format will be used # look for the format here: https://en.cppreference.com/w/cpp/chrono/c/strftime +# default=%Y/%m/%d - %H:%M:%S Achievement_Unlock_Datetime_Format=%Y/%m/%d - %H:%M:%S # main background when you press shift+tab diff --git a/post_build/steam_settings.EXAMPLE/configs.user.EXAMPLE.ini b/post_build/steam_settings.EXAMPLE/configs.user.EXAMPLE.ini index 6c2e5a4d..a3cea590 100644 --- a/post_build/steam_settings.EXAMPLE/configs.user.EXAMPLE.ini +++ b/post_build/steam_settings.EXAMPLE/configs.user.EXAMPLE.ini @@ -4,12 +4,15 @@ [user::general] # user account name +# default=gse orca account_name=gse orca -# Steam64 format +# your account ID in Steam64 format +# if the specified ID is invalid, the emu will ignore it and generate a proper one +# default=randomly generated by the emu only once and saved in the global settings account_steamid=76561197960287930 # the language reported to the app/game +# this must exist in 'supported_languages.txt', otherwise it will be ignored by the emu # look for the column 'API language code' here: https://partner.steamgames.com/doc/store/localization/languages -# this must also exist in 'supported_languages.txt', otherwise it will be ignored by the emu # default=english language=english # report a country IP if the game queries it @@ -22,6 +25,7 @@ ip_country=US # path could be absolute, or relative to the location of the .dll/.so # leading and trailing whitespaces are trimmed # when this option is used, the global settings folder is completely ignored, allowing a full portable behavior +# default= local_save_path=./path/relative/to/dll # name of the base folder used to store save data, leading and trailing whitespaces are trimmed # only useful if 'local_save_path' isn't used diff --git a/tools/steamclient_loader/win/ColdClientLoader.cpp b/tools/steamclient_loader/win/ColdClientLoader.cpp index 3845c4f5..fe16d94b 100644 --- a/tools/steamclient_loader/win/ColdClientLoader.cpp +++ b/tools/steamclient_loader/win/ColdClientLoader.cpp @@ -34,16 +34,16 @@ static dbg_log logger{pe_helpers::get_current_exe_path() + pe_helpers::get_curre constexpr static const wchar_t STEAM_UNIVERSE[] = L"Public"; constexpr static const wchar_t STEAM_URL_PROTOCOL[] = L"URL:steam protocol"; -const bool loader_is_32 = pe_helpers::is_module_32(GetModuleHandleW(NULL)); +const static bool loader_is_32 = pe_helpers::is_module_32(GetModuleHandleW(NULL)); -const std::wstring hklm_path(loader_is_32 +const static std::wstring hklm_path(loader_is_32 ? L"SOFTWARE\\Valve\\Steam" : L"SOFTWARE\\WOW6432Node\\Valve\\Steam" ); // Declare some variables to be used for Steam registry. -const DWORD UserId = 0x03100004771F810D & 0xffffffff; -const DWORD ProcessID = GetCurrentProcessId(); +const static DWORD UserId = 0x03100004771F810D & 0xffffffff; +const static DWORD ProcessID = GetCurrentProcessId(); static std::string IniFile{}; static std::string ClientPath{}; @@ -154,20 +154,26 @@ static std::vector collect_dlls_to_inject(const bool is_exe_32, std } } -bool orig_steam_hkcu = false; -WCHAR OrgSteamCDir_hkcu[8192] = { 0 }; -DWORD Size1_hkcu = _countof(OrgSteamCDir_hkcu); -WCHAR OrgSteamCDir64_hkcu[8192] = { 0 }; -DWORD Size2_hkcu = _countof(OrgSteamCDir64_hkcu); -bool patch_registry_hkcu() +static bool orig_steam_hkcu = false; +static WCHAR OrgSteamCDir_hkcu[8192] = { 0 }; +static DWORD Size1_hkcu = sizeof(OrgSteamCDir_hkcu); +static WCHAR OrgSteamCDir64_hkcu[8192] = { 0 }; +static DWORD Size2_hkcu = sizeof(OrgSteamCDir64_hkcu); +static DWORD OrgSteamActiveUser_hkcu = 0; +static WCHAR OrgSteamUniverse_hkcu[8192] = { 0 }; +static DWORD Size4_hkcu = sizeof(OrgSteamUniverse_hkcu); +static bool patch_registry_hkcu() { HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { orig_steam_hkcu = true; // Get original values to restore later. DWORD keyType = REG_SZ; - RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)&OrgSteamCDir_hkcu, &Size1_hkcu); - RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)&OrgSteamCDir64_hkcu, &Size2_hkcu); + RegQueryValueExW(Registrykey, L"SteamClientDll", 0, &keyType, (LPBYTE)OrgSteamCDir_hkcu, &Size1_hkcu); + RegQueryValueExW(Registrykey, L"SteamClientDll64", 0, &keyType, (LPBYTE)OrgSteamCDir64_hkcu, &Size2_hkcu); + DWORD SizeActiveUser_hkcu = sizeof(DWORD); + RegQueryValueExW(Registrykey, L"ActiveUser", 0, &keyType, (LPBYTE)&OrgSteamActiveUser_hkcu, &SizeActiveUser_hkcu); + RegQueryValueExW(Registrykey, L"Universe", 0, &keyType, (LPBYTE)OrgSteamUniverse_hkcu, &Size4_hkcu); logger.write("Found previous registry entry (HKCU) for Steam"); } else if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { @@ -177,18 +183,18 @@ bool patch_registry_hkcu() return false; } - RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD)); - RegSetValueExW(Registrykey, L"pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); auto client_path = common_helpers::to_wstr(ClientPath); auto client64_path = common_helpers::to_wstr(Client64Path); + RegSetValueExW(Registrykey, L"pid", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE*)(client_path).c_str(), static_cast((client_path.size() + 1) * sizeof(client_path[0]))); RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE*)client64_path.c_str(), static_cast((client64_path.size() + 1) * sizeof(client64_path[0]))); + RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&UserId, sizeof(DWORD)); RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); RegCloseKey(Registrykey); return true; } -void cleanup_registry_hkcu() +static void cleanup_registry_hkcu() { if (!orig_steam_hkcu) return; @@ -196,8 +202,10 @@ void cleanup_registry_hkcu() HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { // Restore the values. - RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)OrgSteamCDir_hkcu, Size1_hkcu); - RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)OrgSteamCDir64_hkcu, Size2_hkcu); + RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (const BYTE *)OrgSteamCDir_hkcu, Size1_hkcu); + RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (const BYTE *)OrgSteamCDir64_hkcu, Size2_hkcu); + RegSetValueExW(Registrykey, L"ActiveUser", NULL, REG_DWORD, (const BYTE *)&OrgSteamActiveUser_hkcu, sizeof(DWORD)); + RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)OrgSteamUniverse_hkcu, Size4_hkcu); // Close the HKEY Handle. RegCloseKey(Registrykey); @@ -207,17 +215,80 @@ void cleanup_registry_hkcu() } -bool orig_steam_hklm = false; -WCHAR OrgInstallPath_hklm[8192] = { 0 }; -DWORD Size1_hklm = _countof(OrgInstallPath_hklm); -bool patch_registry_hklm() +static bool orig_steam_hkcu_2 = false; +static WCHAR OrgSteamModDir_hkcu_2[8192] = { 0 }; +static DWORD Size1_hkcu_2 = sizeof(OrgSteamModDir_hkcu_2); +static WCHAR OrgSteamPath_hkcu_2[8192] = { 0 }; +static DWORD Size2_hkcu_2 = sizeof(OrgSteamPath_hkcu_2); +static WCHAR OrgSteamExe_2[8192] = { 0 }; +static DWORD Size3_hkcu_2 = sizeof(OrgSteamExe_2); +static bool patch_registry_hkcu_2() +{ + HKEY Registrykey = { 0 }; + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { + orig_steam_hkcu_2 = true; + // Get original values to restore later. + DWORD keyType = REG_SZ; + RegQueryValueExW(Registrykey, L"SourceModInstallPath", 0, &keyType, (LPBYTE)OrgSteamModDir_hkcu_2, &Size1_hkcu_2); + RegQueryValueExW(Registrykey, L"SteamPath", 0, &keyType, (LPBYTE)OrgSteamPath_hkcu_2, &Size2_hkcu_2); + RegQueryValueExW(Registrykey, L"SteamExe", 0, &keyType, (LPBYTE)OrgSteamExe_2, &Size3_hkcu_2); + logger.write("Found previous registry entry (HKCU #2) for Steam"); + } else if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, 0, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { + logger.write("Created new registry entry (HKCU #2) for Steam"); + } else { + logger.write("Unable to patch Registry (HKCU #2), error = " + std::to_string(GetLastError())); + return false; + } + + auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path()); + my_path.pop_back(); // remove last '\\' + const auto my_exe = common_helpers::to_wstr(pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name()); + if (AppId.size()) { // empty in persistent mode = 2 + const auto appid_dword = std::stoul(AppId); + RegSetValueExW(Registrykey, L"RunningAppID", NULL, REG_DWORD, (const BYTE*)&appid_dword, sizeof(DWORD)); + } + RegSetValueExW(Registrykey, L"SourceModInstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast((my_path.size() + 1) * sizeof(my_path[0]))); + RegSetValueExW(Registrykey, L"SteamPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast((my_path.size() + 1) * sizeof(my_path[0]))); + RegSetValueExW(Registrykey, L"SteamExe", NULL, REG_SZ, (const BYTE*)my_exe.c_str(), static_cast((my_exe.size() + 1) * sizeof(my_exe[0]))); + RegCloseKey(Registrykey); + return true; +} + +static void cleanup_registry_hkcu_2() +{ + if (!orig_steam_hkcu_2) return; + + logger.write("restoring registry entries (HKCU #2)"); + HKEY Registrykey = { 0 }; + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { + // Restore the values. + RegSetValueExW(Registrykey, L"SourceModInstallPath", NULL, REG_SZ, (LPBYTE)OrgSteamModDir_hkcu_2, Size1_hkcu_2); + RegSetValueExW(Registrykey, L"SteamPath", NULL, REG_SZ, (LPBYTE)OrgSteamPath_hkcu_2, Size2_hkcu_2); + RegSetValueExW(Registrykey, L"SteamExe", NULL, REG_SZ, (LPBYTE)OrgSteamExe_2, Size3_hkcu_2); + + // Close the HKEY Handle. + RegCloseKey(Registrykey); + } else { + logger.write("Unable to restore the original registry entry (HKCU #2), error = " + std::to_string(GetLastError())); + } +} + + +static bool orig_steam_hklm = false; +static WCHAR OrgInstallPath_hklm[8192] = { 0 }; +static DWORD Size1_hklm = sizeof(OrgInstallPath_hklm); +static WCHAR OrgUniverse_hklm[8192] = { 0 }; +static DWORD Size2_hklm = sizeof(OrgUniverse_hklm); +static bool patch_registry_hklm() { HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { orig_steam_hklm = true; // Get original values to restore later. DWORD keyType = REG_SZ; - RegQueryValueExW(Registrykey, L"InstallPath", 0, &keyType, (LPBYTE)&OrgInstallPath_hklm, &Size1_hklm); + RegQueryValueExW(Registrykey, L"InstallPath", 0, &keyType, (LPBYTE)OrgInstallPath_hklm, &Size1_hklm); + RegQueryValueExW(Registrykey, L"Universe", 0, &keyType, (LPBYTE)OrgUniverse_hklm, &Size2_hklm); logger.write("Found previous registry entry (HKLM) for Steam"); } else if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { @@ -227,15 +298,16 @@ bool patch_registry_hklm() return false; } - const auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path()); - RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast((my_path.size() + 1) * sizeof(my_path[0]))); + auto my_path = common_helpers::to_wstr(pe_helpers::get_current_exe_path()); + my_path.pop_back(); // remove last '\\' RegSetValueExW(Registrykey, L"SteamPID", NULL, REG_DWORD, (const BYTE *)&ProcessID, sizeof(DWORD)); + RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE*)my_path.c_str(), static_cast((my_path.size() + 1) * sizeof(my_path[0]))); RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)STEAM_UNIVERSE, (DWORD)sizeof(STEAM_UNIVERSE)); RegCloseKey(Registrykey); return true; } -void cleanup_registry_hklm() +static void cleanup_registry_hklm() { if (!orig_steam_hklm) return; @@ -243,6 +315,7 @@ void cleanup_registry_hklm() HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hklm_path.c_str(), 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { RegSetValueExW(Registrykey, L"InstallPath", NULL, REG_SZ, (const BYTE *)OrgInstallPath_hklm, Size1_hklm); + RegSetValueExW(Registrykey, L"Universe", NULL, REG_SZ, (const BYTE *)OrgUniverse_hklm, Size2_hklm); RegCloseKey(Registrykey); } else { logger.write("Unable to restore the original registry entry (HKLM), error = " + std::to_string(GetLastError())); @@ -250,15 +323,20 @@ void cleanup_registry_hklm() } -bool orig_steam_hkcs_1 = false; -bool orig_steam_hkcs_2 = false; -WCHAR OrgCommand_hkcs[8192] = { 0 }; -DWORD Size1_hkcs = _countof(OrgCommand_hkcs); -bool patch_registry_hkcs() +static bool orig_steam_hkcs_1 = false; +static bool orig_steam_hkcs_2 = false; +static WCHAR OrgCommand_hkcs[8192] = { 0 }; +static DWORD Size1_hkcs = sizeof(OrgCommand_hkcs); +static WCHAR OrgUrlProtocol_hkcs[8192] = { 0 }; +static DWORD Size2_hkcs = sizeof(OrgUrlProtocol_hkcs); +static bool patch_registry_hkcs() { HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { orig_steam_hkcs_1 = true; + // Get original values to restore later. + DWORD keyType = REG_SZ; + RegQueryValueExW(Registrykey, L"URL Protocol", 0, &keyType, (LPBYTE)&OrgUrlProtocol_hkcs, &Size2_hkcs); logger.write("Found previous registry entry (HKCS) #1 for Steam"); } else if (RegCreateKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) == ERROR_SUCCESS) { @@ -288,16 +366,16 @@ bool patch_registry_hkcs() RegSetValueExW(Registrykey, L"URL Protocol", NULL, REG_SZ, (const BYTE *)L"", (DWORD)sizeof(L"")); RegCloseKey(Registrykey); - const auto cmd = common_helpers::to_wstr(pe_helpers::get_current_exe_path() + "steam.exe -- \"%1\""); + const auto cmd = common_helpers::to_wstr("\"" + pe_helpers::get_current_exe_path() + pe_helpers::get_current_exe_name() + "\" -- \"%1\""); RegSetValueExW(Registrykey_2, L"", NULL, REG_SZ, (const BYTE*)cmd.c_str(), static_cast((cmd.size() + 1) * sizeof(cmd[0]))); RegCloseKey(Registrykey_2); return true; } -void cleanup_registry_hkcs() +static void cleanup_registry_hkcs() { if (orig_steam_hkcs_2) { - logger.write("restoring registry entries (HKCS) #1"); + logger.write("restoring registry entries (HKCS) #2"); HKEY Registrykey = { 0 }; if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam\\Shell\\Open\\Command", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { RegSetValueExW(Registrykey, L"", NULL, REG_SZ, (const BYTE *)OrgCommand_hkcs, Size1_hkcs); @@ -307,7 +385,16 @@ void cleanup_registry_hkcs() } } - if (!orig_steam_hkcs_1) { + if (orig_steam_hkcs_1) { + logger.write("restoring registry entries (HKCS) #1"); + HKEY Registrykey = { 0 }; + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"steam", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { + RegSetValueExW(Registrykey, L"URL Protocol", NULL, REG_SZ, (const BYTE *)OrgUrlProtocol_hkcs, Size2_hkcs); + RegCloseKey(Registrykey); + } else { + logger.write("Unable to restore the original registry entry (HKCS) #1, error = " + std::to_string(GetLastError())); + } + } else { logger.write("removing registry entries (HKCS) #2 (added by loader)"); HKEY Registrykey = { 0 }; RegDeleteKeyW(HKEY_CLASSES_ROOT, L"steam"); @@ -316,7 +403,7 @@ void cleanup_registry_hkcs() -void set_steam_env_vars(const std::string &AppId) +static void set_steam_env_vars(const std::string &AppId) { SetEnvironmentVariableA("SteamAppId", AppId.c_str()); SetEnvironmentVariableA("SteamGameId", AppId.c_str()); @@ -552,12 +639,13 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance return 1; } } else { // steam://run/ - constexpr const static wchar_t STEAM_LAUNCH_CMD_1[] = L"steam://run/"; + constexpr const static wchar_t STEAM_LAUNCH_CMD_1[] = L"-- \"steam://run/"; constexpr const static wchar_t STEAM_LAUNCH_CMD_2[] = L"-- \"steam://rungameid/"; AppId.clear(); // we don't care about the app id in the ini auto my_cmd = lpCmdLine && lpCmdLine[0] ? std::wstring(lpCmdLine) : std::wstring(); + //MessageBoxW(NULL, (my_cmd + L" ||| " + std::to_wstring(my_cmd.size())).c_str(), L"DEBUG ME", MB_OK); logger.write(L"persistent mode 2 detecting steam launch cmd from: '" + my_cmd + L"'"); if (my_cmd.find(STEAM_LAUNCH_CMD_1) == 0) { AppId = common_helpers::to_str( my_cmd.substr(sizeof(STEAM_LAUNCH_CMD_1) / sizeof(STEAM_LAUNCH_CMD_1[0]), my_cmd.find_first_of(L" \t")) ); @@ -578,16 +666,37 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance } if (!patch_registry_hkcu()) { + cleanup_registry_hkcu(); + cleanup_registry_hkcu_2(); + cleanup_registry_hklm(); + cleanup_registry_hkcs(); + + logger.write("Unable to patch Registry (HKCU)."); MessageBoxA(NULL, "Unable to patch Registry (HKCU).", "ColdClientLoader", MB_ICONERROR); return 1; } + if (!patch_registry_hkcu_2()) { + cleanup_registry_hkcu(); + cleanup_registry_hkcu_2(); + cleanup_registry_hklm(); + cleanup_registry_hkcs(); + + logger.write("Unable to patch Registry (HKCU #2)."); + MessageBoxA(NULL, "Unable to patch Registry (HKCU #2).", "ColdClientLoader", MB_ICONERROR); + return 1; + } + // this fails due to admin rights when Steam isn't installed, not a big deal // ---------------------------------------------- patch_registry_hklm(); // if (!patch_registry_hklm()) { // cleanup_registry_hkcu(); + // cleanup_registry_hkcu_2(); // cleanup_registry_hklm(); + // cleanup_registry_hkcs(); + // + // logger.write("Unable to patch Registry (HKLM)."); // MessageBoxA(NULL, "Unable to patch Registry (HKLM).", "ColdClientLoader", MB_ICONERROR); // return 1; // } @@ -597,7 +706,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance patch_registry_hkcs(); // if (!patch_registry_hkcs()) { // cleanup_registry_hkcu(); + // cleanup_registry_hkcu_2(); // cleanup_registry_hklm(); + // cleanup_registry_hkcs(); // logger.write("Unable to patch Registry (HKCS)."); // MessageBoxA(NULL, "Unable to patch Registry (HKCS).", "ColdClientLoader", MB_ICONERROR); @@ -621,6 +732,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance if (!CreateProcessW(exe_file.c_str(), (LPWSTR)CommandLine.c_str(), NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, common_helpers::to_wstr(ExeRunDir).c_str(), &info, &processInfo)) { logger.write("Unable to load the requested EXE file, error = " + std::to_string(GetLastError())); cleanup_registry_hkcu(); + cleanup_registry_hkcu_2(); cleanup_registry_hklm(); cleanup_registry_hkcs(); MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR); @@ -645,6 +757,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance CloseHandle(processInfo.hThread); cleanup_registry_hkcu(); + cleanup_registry_hkcu_2(); cleanup_registry_hklm(); cleanup_registry_hkcs(); @@ -684,8 +797,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance MessageBoxA(NULL, "Start the game, then press OK when you have finished playing to close the loader", "Cold Client Loader (waiting)", MB_OK); } - if (PersistentMode != 2 || AppId.empty()) { // persistent mode 0 or 1, or mode 2 without app id + if (PersistentMode != 2 || AppId.empty()) { // persistent mode 0 or 1, or mode 2 without app id cleanup_registry_hkcu(); + cleanup_registry_hkcu_2(); cleanup_registry_hklm(); cleanup_registry_hkcs(); }