#ifndef RC_CLIENT_INTERNAL_H #define RC_CLIENT_INTERNAL_H #ifdef __cplusplus extern "C" { #endif #include "rc_client.h" #include "rc_compat.h" #include "rc_runtime.h" #include "rc_runtime_types.h" /*****************************************************************************\ | Callbacks | \*****************************************************************************/ struct rc_api_fetch_game_data_response_t; typedef void (*rc_client_post_process_game_data_response_t)(const rc_api_server_response_t* server_response, struct rc_api_fetch_game_data_response_t* game_data_response, rc_client_t* client, void* userdata); typedef int (*rc_client_can_submit_achievement_unlock_t)(uint32_t achievement_id, rc_client_t* client); typedef int (*rc_client_can_submit_leaderboard_entry_t)(uint32_t leaderboard_id, rc_client_t* client); typedef int (*rc_client_rich_presence_override_t)(rc_client_t* client, char buffer[], size_t buffersize); typedef struct rc_client_callbacks_t { rc_client_read_memory_func_t read_memory; rc_client_event_handler_t event_handler; rc_client_server_call_t server_call; rc_client_message_callback_t log_call; rc_get_time_millisecs_func_t get_time_millisecs; rc_client_post_process_game_data_response_t post_process_game_data_response; rc_client_can_submit_achievement_unlock_t can_submit_achievement_unlock; rc_client_can_submit_leaderboard_entry_t can_submit_leaderboard_entry; rc_client_rich_presence_override_t rich_presence_override; void* client_data; } rc_client_callbacks_t; struct rc_client_scheduled_callback_data_t; typedef void (*rc_client_scheduled_callback_t)(struct rc_client_scheduled_callback_data_t* callback_data, rc_client_t* client, rc_clock_t now); typedef struct rc_client_scheduled_callback_data_t { rc_clock_t when; uint32_t related_id; rc_client_scheduled_callback_t callback; void* data; struct rc_client_scheduled_callback_data_t* next; } rc_client_scheduled_callback_data_t; void rc_client_schedule_callback(rc_client_t* client, rc_client_scheduled_callback_data_t* scheduled_callback); /*****************************************************************************\ | Achievements | \*****************************************************************************/ enum { RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_NONE = 0, RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_TRIGGERED = (1 << 1), RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_CHALLENGE_INDICATOR_SHOW = (1 << 2), RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_CHALLENGE_INDICATOR_HIDE = (1 << 3), RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_UPDATE = (1 << 4) /* not a real event, just triggers update */ }; typedef struct rc_client_achievement_info_t { rc_client_achievement_t public_; rc_trigger_t* trigger; uint8_t md5[16]; time_t unlock_time_hardcore; time_t unlock_time_softcore; uint8_t pending_events; const char* author; time_t created_time; time_t updated_time; } rc_client_achievement_info_t; enum { RC_CLIENT_PROGRESS_TRACKER_ACTION_NONE, RC_CLIENT_PROGRESS_TRACKER_ACTION_SHOW, RC_CLIENT_PROGRESS_TRACKER_ACTION_UPDATE, RC_CLIENT_PROGRESS_TRACKER_ACTION_HIDE }; typedef struct rc_client_progress_tracker_t { rc_client_achievement_info_t* achievement; float progress; rc_client_scheduled_callback_data_t* hide_callback; uint8_t action; } rc_client_progress_tracker_t; /*****************************************************************************\ | Leaderboard Trackers | \*****************************************************************************/ enum { RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_NONE = 0, RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_UPDATE = (1 << 1), RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_SHOW = (1 << 2), RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_HIDE = (1 << 3) }; typedef struct rc_client_leaderboard_tracker_info_t { rc_client_leaderboard_tracker_t public_; struct rc_client_leaderboard_tracker_info_t* next; int32_t raw_value; uint32_t value_djb2; uint8_t format; uint8_t pending_events; uint8_t reference_count; uint8_t value_from_hits; } rc_client_leaderboard_tracker_info_t; /*****************************************************************************\ | Leaderboards | \*****************************************************************************/ enum { RC_CLIENT_LEADERBOARD_PENDING_EVENT_NONE = 0, RC_CLIENT_LEADERBOARD_PENDING_EVENT_STARTED = (1 << 1), RC_CLIENT_LEADERBOARD_PENDING_EVENT_FAILED = (1 << 2), RC_CLIENT_LEADERBOARD_PENDING_EVENT_SUBMITTED = (1 << 3) }; typedef struct rc_client_leaderboard_info_t { rc_client_leaderboard_t public_; rc_lboard_t* lboard; uint8_t md5[16]; rc_client_leaderboard_tracker_info_t* tracker; uint32_t value_djb2; int32_t value; uint8_t format; uint8_t pending_events; uint8_t bucket; uint8_t hidden; } rc_client_leaderboard_info_t; uint8_t rc_client_map_leaderboard_format(int format); /*****************************************************************************\ | Subsets | \*****************************************************************************/ enum { RC_CLIENT_SUBSET_PENDING_EVENT_NONE = 0, RC_CLIENT_SUBSET_PENDING_EVENT_ACHIEVEMENT = (1 << 1), RC_CLIENT_SUBSET_PENDING_EVENT_LEADERBOARD = (1 << 2) }; typedef struct rc_client_subset_info_t { rc_client_subset_t public_; rc_client_achievement_info_t* achievements; rc_client_leaderboard_info_t* leaderboards; struct rc_client_subset_info_t* next; const char* all_label; const char* inactive_label; const char* locked_label; const char* unlocked_label; const char* unofficial_label; const char* unsupported_label; uint8_t active; uint8_t mastery; uint8_t pending_events; } rc_client_subset_info_t; void rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata); /*****************************************************************************\ | Game | \*****************************************************************************/ typedef struct rc_client_game_hash_t { char hash[33]; uint32_t game_id; struct rc_client_game_hash_t* next; } rc_client_game_hash_t; rc_client_game_hash_t* rc_client_find_game_hash(rc_client_t* client, const char* hash); typedef struct rc_client_media_hash_t { rc_client_game_hash_t* game_hash; struct rc_client_media_hash_t* next; uint32_t path_djb2; } rc_client_media_hash_t; enum { RC_CLIENT_GAME_PENDING_EVENT_NONE = 0, RC_CLIENT_GAME_PENDING_EVENT_LEADERBOARD_TRACKER = (1 << 1), RC_CLIENT_GAME_PENDING_EVENT_UPDATE_ACTIVE_ACHIEVEMENTS = (1 << 2), RC_CLIENT_GAME_PENDING_EVENT_PROGRESS_TRACKER = (1 << 3) }; typedef struct rc_client_game_info_t { rc_client_game_t public_; rc_client_leaderboard_tracker_info_t* leaderboard_trackers; rc_client_progress_tracker_t progress_tracker; rc_client_subset_info_t* subsets; rc_client_media_hash_t* media_hash; rc_runtime_t runtime; uint32_t max_valid_address; uint8_t waiting_for_reset; uint8_t pending_events; rc_buffer_t buffer; } rc_client_game_info_t; void rc_client_update_active_achievements(rc_client_game_info_t* game); void rc_client_update_active_leaderboards(rc_client_game_info_t* game); /*****************************************************************************\ | Client | \*****************************************************************************/ enum { RC_CLIENT_LOAD_STATE_NONE, RC_CLIENT_LOAD_STATE_IDENTIFYING_GAME, RC_CLIENT_LOAD_STATE_AWAIT_LOGIN, RC_CLIENT_LOAD_STATE_FETCHING_GAME_DATA, RC_CLIENT_LOAD_STATE_STARTING_SESSION, RC_CLIENT_LOAD_STATE_DONE, RC_CLIENT_LOAD_STATE_UNKNOWN_GAME }; enum { RC_CLIENT_USER_STATE_NONE, RC_CLIENT_USER_STATE_LOGIN_REQUESTED, RC_CLIENT_USER_STATE_LOGGED_IN }; enum { RC_CLIENT_MASTERY_STATE_NONE, RC_CLIENT_MASTERY_STATE_PENDING, RC_CLIENT_MASTERY_STATE_SHOWN }; enum { RC_CLIENT_SPECTATOR_MODE_OFF, RC_CLIENT_SPECTATOR_MODE_ON, RC_CLIENT_SPECTATOR_MODE_LOCKED }; enum { RC_CLIENT_DISCONNECT_HIDDEN = 0, RC_CLIENT_DISCONNECT_VISIBLE = (1 << 0), RC_CLIENT_DISCONNECT_SHOW_PENDING = (1 << 1), RC_CLIENT_DISCONNECT_HIDE_PENDING = (1 << 2) }; struct rc_client_load_state_t; typedef struct rc_client_state_t { rc_mutex_t mutex; rc_buffer_t buffer; rc_client_scheduled_callback_data_t* scheduled_callbacks; uint8_t hardcore; uint8_t encore_mode; uint8_t spectator_mode; uint8_t unofficial_enabled; uint8_t log_level; uint8_t user; uint8_t disconnect; struct rc_client_load_state_t* load; struct rc_client_async_handle_t* async_handles[4]; rc_memref_t* processing_memref; rc_peek_t legacy_peek; } rc_client_state_t; struct rc_client_t { rc_client_game_info_t* game; rc_client_game_hash_t* hashes; rc_client_user_t user; rc_client_callbacks_t callbacks; rc_client_state_t state; }; /*****************************************************************************\ | Helpers | \*****************************************************************************/ #ifdef RC_NO_VARIADIC_MACROS void RC_CLIENT_LOG_ERR_FORMATTED(const rc_client_t* client, const char* format, ...); void RC_CLIENT_LOG_WARN_FORMATTED(const rc_client_t* client, const char* format, ...); void RC_CLIENT_LOG_INFO_FORMATTED(const rc_client_t* client, const char* format, ...); void RC_CLIENT_LOG_VERBOSE_FORMATTED(const rc_client_t* client, const char* format, ...); #else void rc_client_log_message_formatted(const rc_client_t* client, const char* format, ...); #define RC_CLIENT_LOG_ERR_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_ERROR) rc_client_log_message_formatted(client, format, __VA_ARGS__); } #define RC_CLIENT_LOG_WARN_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_WARN) rc_client_log_message_formatted(client, format, __VA_ARGS__); } #define RC_CLIENT_LOG_INFO_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO) rc_client_log_message_formatted(client, format, __VA_ARGS__); } #define RC_CLIENT_LOG_VERBOSE_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_VERBOSE) rc_client_log_message_formatted(client, format, __VA_ARGS__); } #endif void rc_client_log_message(const rc_client_t* client, const char* message); #define RC_CLIENT_LOG_ERR(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_ERROR) rc_client_log_message(client, message); } #define RC_CLIENT_LOG_WARN(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_WARN) rc_client_log_message(client, message); } #define RC_CLIENT_LOG_INFO(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO) rc_client_log_message(client, message); } #define RC_CLIENT_LOG_VERBOSE(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_VERBOSE) rc_client_log_message(client, message); } /* internals pulled from runtime.c */ void rc_runtime_checksum(const char* memaddr, uint8_t* md5); int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memref_t* memref); int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t* memref); /* end runtime.c internals */ /* helper functions for unit tests */ struct rc_hash_iterator; struct rc_hash_iterator* rc_client_get_load_state_hash_iterator(rc_client_t* client); /* end helper functions for unit tests */ enum { RC_CLIENT_LEGACY_PEEK_AUTO, RC_CLIENT_LEGACY_PEEK_CONSTRUCTED, RC_CLIENT_LEGACY_PEEK_LITTLE_ENDIAN_READS }; void rc_client_set_legacy_peek(rc_client_t* client, int method); void rc_client_release_leaderboard_tracker(rc_client_game_info_t* game, rc_client_leaderboard_info_t* leaderboard); #ifdef __cplusplus } #endif #endif /* RC_CLIENT_INTERNAL_H */