diff --git a/dep/rcheevos/include/rc_client_raintegration.h b/dep/rcheevos/include/rc_client_raintegration.h new file mode 100644 index 000000000..d8a6ef181 --- /dev/null +++ b/dep/rcheevos/include/rc_client_raintegration.h @@ -0,0 +1,35 @@ +#ifndef RC_CLIENT_RAINTEGRATION_H +#define RC_CLIENT_RAINTEGRATION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _WIN32 + #undef RC_CLIENT_SUPPORTS_RAINTEGRATION /* Windows required for RAIntegration */ +#endif + +#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION + +#ifndef RC_CLIENT_SUPPORTS_EXTERNAL + #define RC_CLIENT_SUPPORTS_EXTERNAL /* external rc_client required for RAIntegration */ +#endif + +#include "rc_client.h" + +#include /* HWND */ + +rc_client_async_handle_t* rc_client_begin_load_raintegration(rc_client_t* client, + const wchar_t* search_directory, HWND main_window_handle, + const char* client_name, const char* client_version, + rc_client_callback_t callback, void* callback_userdata); + +void rc_client_unload_raintegration(rc_client_t* client); + +#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */ + +#ifdef __cplusplus +} +#endif + +#endif /* RC_CLIENT_RAINTEGRATION_H */ diff --git a/dep/rcheevos/src/rc_client.c b/dep/rcheevos/src/rc_client.c index 98d8c1fe8..b2a6463f2 100644 --- a/dep/rcheevos/src/rc_client.c +++ b/dep/rcheevos/src/rc_client.c @@ -23,10 +23,6 @@ #define RC_CLIENT_UNKNOWN_GAME_ID (uint32_t)-1 #define RC_CLIENT_RECENT_UNLOCK_DELAY_SECONDS (10 * 60) /* ten minutes */ -struct rc_client_async_handle_t { - uint8_t aborted; -}; - enum { RC_CLIENT_ASYNC_NOT_ABORTED = 0, RC_CLIENT_ASYNC_ABORTED = 1, @@ -131,6 +127,11 @@ void rc_client_destroy(rc_client_t* client) rc_client_unload_game(client); +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->destroy) + client->state.external_client->destroy(); +#endif + rc_buffer_destroy(&client->state.buffer); rc_mutex_destroy(&client->state.mutex); @@ -227,6 +228,11 @@ void rc_client_enable_logging(rc_client_t* client, int level, rc_client_message_ { client->callbacks.log_call = callback; client->state.log_level = callback ? level : RC_CLIENT_LOG_LEVEL_NONE; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->enable_logging) + client->state.external_client->enable_logging(client, level, callback); +#endif } /* ===== Common ===== */ @@ -291,6 +297,22 @@ static rc_clock_t rc_client_clock_get_now_millisecs(const rc_client_t* client) void rc_client_set_get_time_millisecs_function(rc_client_t* client, rc_get_time_millisecs_func_t handler) { client->callbacks.get_time_millisecs = handler ? handler : rc_client_clock_get_now_millisecs; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_get_time_millisecs) + client->state.external_client->set_get_time_millisecs(client, handler); +#endif +} + +int rc_client_async_handle_aborted(rc_client_t* client, rc_client_async_handle_t* async_handle) +{ + int aborted; + + rc_mutex_lock(&client->state.mutex); + aborted = async_handle->aborted; + rc_mutex_unlock(&client->state.mutex); + + return aborted; } static void rc_client_begin_async(rc_client_t* client, rc_client_async_handle_t* async_handle) @@ -333,12 +355,41 @@ static int rc_client_end_async(rc_client_t* client, rc_client_async_handle_t* as void rc_client_abort_async(rc_client_t* client, rc_client_async_handle_t* async_handle) { if (async_handle && client) { +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->abort_async) { + client->state.external_client->abort_async(async_handle); + return; + } +#endif + rc_mutex_lock(&client->state.mutex); async_handle->aborted = RC_CLIENT_ASYNC_ABORTED; rc_mutex_unlock(&client->state.mutex); } } +static int rc_client_async_handle_valid(rc_client_t* client, rc_client_async_handle_t* async_handle) +{ + int valid = 0; + size_t i; + + /* there is a small window of opportunity where the client could have been destroyed before calling + * this function, but this function assumes the possibility that the handle has been destroyed, so + * we can't check it for RC_CLIENT_ASYNC_DESTROYED before attempting to scan the client data */ + rc_mutex_lock(&client->state.mutex); + + for (i = 0; i < sizeof(client->state.async_handles) / sizeof(client->state.async_handles[0]); ++i) { + if (client->state.async_handles[i] == async_handle) { + valid = 1; + break; + } + } + + rc_mutex_unlock(&client->state.mutex); + + return valid; +} + static const char* rc_client_server_error_message(int* result, int http_status_code, const rc_api_response_t* response) { if (!response->succeeded) { @@ -461,6 +512,10 @@ static int rc_client_should_retry(const rc_api_server_response_t* server_respons /* connection to server from cloudfare was dropped before request was completed */ return 1; + case 525: /* 525 SSL Handshake Failed */ + /* web server worker connection pool is exhausted */ + return 1; + case RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR: /* client provided non-HTTP error (explicitly retryable) */ return 1; @@ -614,7 +669,13 @@ static rc_client_async_handle_t* rc_client_begin_login(rc_client_t* client, rc_api_destroy_request(&request); - return &callback_data->async_handle; + /* if the user state has changed, the async operation completed synchronously */ + rc_mutex_lock(&client->state.mutex); + if (client->state.user != RC_CLIENT_USER_STATE_LOGIN_REQUESTED) + callback_data = NULL; + rc_mutex_unlock(&client->state.mutex); + + return callback_data ? &callback_data->async_handle : NULL; } rc_client_async_handle_t* rc_client_begin_login_with_password(rc_client_t* client, @@ -637,6 +698,11 @@ rc_client_async_handle_t* rc_client_begin_login_with_password(rc_client_t* clien return NULL; } +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_login_with_password) + return client->state.external_client->begin_login_with_password(client, username, password, callback, callback_userdata); +#endif + memset(&login_request, 0, sizeof(login_request)); login_request.username = username; login_request.password = password; @@ -665,6 +731,11 @@ rc_client_async_handle_t* rc_client_begin_login_with_token(rc_client_t* client, return NULL; } +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_login_with_token) + return client->state.external_client->begin_login_with_token(client, username, token, callback, callback_userdata); +#endif + memset(&login_request, 0, sizeof(login_request)); login_request.username = username; login_request.api_token = token; @@ -680,6 +751,13 @@ void rc_client_logout(rc_client_t* client) if (!client) return; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->logout) { + client->state.external_client->logout(); + return; + } +#endif + switch (client->state.user) { case RC_CLIENT_USER_STATE_LOGGED_IN: RC_CLIENT_LOG_INFO_FORMATTED(client, "Logging %s out", client->user.display_name); @@ -707,7 +785,15 @@ void rc_client_logout(rc_client_t* client) const rc_client_user_t* rc_client_get_user_info(const rc_client_t* client) { - return (client && client->state.user == RC_CLIENT_USER_STATE_LOGGED_IN) ? &client->user : NULL; + if (!client) + return NULL; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_user_info) + return client->state.external_client->get_user_info(); +#endif + + return (client->state.user == RC_CLIENT_USER_STATE_LOGGED_IN) ? &client->user : NULL; } int rc_client_user_get_image_url(const rc_client_user_t* user, char buffer[], size_t buffer_size) @@ -758,7 +844,17 @@ void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_g return; memset(summary, 0, sizeof(*summary)); - if (!client || !client->game) + if (!client) + return; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_user_game_summary) { + client->state.external_client->get_user_game_summary(summary); + return; + } +#endif + + if (!client->game) return; rc_mutex_lock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */ @@ -2111,6 +2207,11 @@ rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const c return NULL; } +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_load_game) + return client->state.external_client->begin_load_game(client, hash, callback, callback_userdata); +#endif + load_state = (rc_client_load_state_t*)calloc(1, sizeof(*load_state)); if (!load_state) { callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata); @@ -2137,6 +2238,11 @@ rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* cl return NULL; } +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_identify_and_load_game) + return client->state.external_client->begin_identify_and_load_game(client, console_id, file_path, data, data_size, callback, callback_userdata); +#endif + if (data) { if (file_path) { RC_CLIENT_LOG_INFO_FORMATTED(client, "Identifying game: %zu bytes at %p (%s)", data_size, data, file_path); @@ -2241,6 +2347,13 @@ void rc_client_unload_game(rc_client_t* client) if (!client) return; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->unload_game) { + client->state.external_client->unload_game(); + return; + } +#endif + rc_mutex_lock(&client->state.mutex); game = client->game; @@ -2365,6 +2478,11 @@ rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, cons return NULL; } +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_change_media) + return client->state.external_client->begin_change_media(client, file_path, data, data_size, callback, callback_userdata); +#endif + rc_mutex_lock(&client->state.mutex); if (client->state.load) { game = client->state.load->game; @@ -2475,6 +2593,7 @@ rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, cons else { /* call the server to make sure the hash is valid for the loaded game */ rc_client_load_state_t* callback_data; + rc_client_async_handle_t* async_handle; rc_api_resolve_hash_request_t resolve_hash_request; rc_api_request_t request; int result; @@ -2500,18 +2619,28 @@ rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, cons callback_data->hash = game_hash; callback_data->game = game; - rc_client_begin_async(client, &callback_data->async_handle); + async_handle = &callback_data->async_handle; + rc_client_begin_async(client, async_handle); client->callbacks.server_call(&request, rc_client_identify_changed_media_callback, callback_data, client); rc_api_destroy_request(&request); - return &callback_data->async_handle; + /* if handle is no longer valid, the async operation completed synchronously */ + return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL; } } const rc_client_game_t* rc_client_get_game_info(const rc_client_t* client) { - return (client && client->game) ? &client->game->public_ : NULL; + if (!client) + return NULL; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_game_info) + return client->state.external_client->get_game_info(); +#endif + + return client->game ? &client->game->public_ : NULL; } int rc_client_game_get_image_url(const rc_client_game_t* game, char buffer[], size_t buffer_size) @@ -2524,19 +2653,24 @@ int rc_client_game_get_image_url(const rc_client_game_t* game, char buffer[], si /* ===== Subsets ===== */ -void rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata) +rc_client_async_handle_t* rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata) { char buffer[32]; rc_client_load_state_t* load_state; if (!client) { callback(RC_INVALID_STATE, "client is required", client, callback_userdata); - return; + return NULL; } +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_load_subset) + return client->state.external_client->begin_load_subset(client, subset_id, callback, callback_userdata); +#endif + if (!client->game) { callback(RC_NO_GAME_LOADED, rc_error_str(RC_NO_GAME_LOADED), client, callback_userdata); - return; + return NULL; } snprintf(buffer, sizeof(buffer), "[SUBSET%lu]", (unsigned long)subset_id); @@ -2544,7 +2678,7 @@ void rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_cli load_state = (rc_client_load_state_t*)calloc(1, sizeof(*load_state)); if (!load_state) { callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata); - return; + return NULL; } load_state->client = client; @@ -2556,13 +2690,23 @@ void rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_cli client->state.load = load_state; rc_client_begin_fetch_game_data(load_state); + + return (client->state.load == load_state) ? &load_state->async_handle : NULL; } const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_t subset_id) { rc_client_subset_info_t* subset; - if (!client || !client->game) + if (!client) + return NULL; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_subset_info) + return client->state.external_client->get_subset_info(subset_id); +#endif + + if (!client->game) return NULL; for (subset = client->game->subsets; subset; subset = subset->next) { @@ -2722,7 +2866,7 @@ rc_client_achievement_list_t* rc_client_create_achievement_list(rc_client_t* cli rc_client_achievement_t** bucket_achievements; rc_client_achievement_t** achievement_ptr; rc_client_achievement_bucket_t* bucket_ptr; - rc_client_achievement_list_t* list; + rc_client_achievement_list_info_t* list; rc_client_subset_info_t* subset; const uint32_t list_size = RC_ALIGN(sizeof(*list)); uint32_t bucket_counts[16]; @@ -2745,8 +2889,16 @@ rc_client_achievement_list_t* rc_client_create_achievement_list(rc_client_t* cli }; const time_t recent_unlock_time = time(NULL) - RC_CLIENT_RECENT_UNLOCK_DELAY_SECONDS; - if (!client || !client->game) - return calloc(1, sizeof(rc_client_achievement_list_t)); + if (!client) + return (rc_client_achievement_list_t*)calloc(1, sizeof(rc_client_achievement_list_info_t)); + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->create_achievement_list) + return (rc_client_achievement_list_t*)client->state.external_client->create_achievement_list(category, grouping); +#endif + + if (!client->game) + return (rc_client_achievement_list_t*)calloc(1, sizeof(rc_client_achievement_list_info_t)); memset(&bucket_counts, 0, sizeof(bucket_counts)); @@ -2811,8 +2963,8 @@ rc_client_achievement_list_t* rc_client_create_achievement_list(rc_client_t* cli buckets_size = RC_ALIGN(num_buckets * sizeof(rc_client_achievement_bucket_t)); - list = (rc_client_achievement_list_t*)malloc(list_size + buckets_size + num_achievements * sizeof(rc_client_achievement_t*)); - bucket_ptr = list->buckets = (rc_client_achievement_bucket_t*)((uint8_t*)list + list_size); + list = (rc_client_achievement_list_info_t*)malloc(list_size + buckets_size + num_achievements * sizeof(rc_client_achievement_t*)); + bucket_ptr = list->public_.buckets = (rc_client_achievement_bucket_t*)((uint8_t*)list + list_size); achievement_ptr = (rc_client_achievement_t**)((uint8_t*)bucket_ptr + buckets_size); if (grouping == RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS) { @@ -2891,13 +3043,17 @@ rc_client_achievement_list_t* rc_client_create_achievement_list(rc_client_t* cli rc_mutex_unlock(&client->state.mutex); - list->num_buckets = (uint32_t)(bucket_ptr - list->buckets); - return list; + list->destroy_func = NULL; + list->public_.num_buckets = (uint32_t)(bucket_ptr - list->public_.buckets); + return &list->public_; } void rc_client_destroy_achievement_list(rc_client_achievement_list_t* list) { - if (list) + rc_client_achievement_list_info_t* info = (rc_client_achievement_list_info_t*)list; + if (info->destroy_func) + info->destroy_func(info); + else free(list); } @@ -2906,7 +3062,15 @@ int rc_client_has_achievements(rc_client_t* client) rc_client_subset_info_t* subset; int result; - if (!client || !client->game) + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->has_achievements) + return client->state.external_client->has_achievements(); +#endif + + if (!client->game) return 0; rc_mutex_lock(&client->state.mutex); @@ -2952,7 +3116,15 @@ const rc_client_achievement_t* rc_client_get_achievement_info(rc_client_t* clien { rc_client_subset_info_t* subset; - if (!client || !client->game) + if (!client) + return NULL; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_achievement_info) + return client->state.external_client->get_achievement_info(id); +#endif + + if (!client->game) return NULL; for (subset = client->game->subsets; subset; subset = subset->next) { @@ -3229,7 +3401,15 @@ const rc_client_leaderboard_t* rc_client_get_leaderboard_info(const rc_client_t* { rc_client_subset_info_t* subset; - if (!client || !client->game) + if (!client) + return NULL; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_leaderboard_info) + return client->state.external_client->get_leaderboard_info(id); +#endif + + if (!client->game) return NULL; for (subset = client->game->subsets; subset; subset = subset->next) { @@ -3301,7 +3481,7 @@ rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* cli rc_client_leaderboard_t** bucket_leaderboards; rc_client_leaderboard_t** leaderboard_ptr; rc_client_leaderboard_bucket_t* bucket_ptr; - rc_client_leaderboard_list_t* list; + rc_client_leaderboard_list_info_t* list; rc_client_subset_info_t* subset; const uint32_t list_size = RC_ALIGN(sizeof(*list)); uint32_t bucket_counts[8]; @@ -3320,7 +3500,15 @@ rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* cli RC_CLIENT_LEADERBOARD_BUCKET_UNSUPPORTED }; - if (!client || !client->game) + if (!client) + return calloc(1, sizeof(rc_client_leaderboard_list_t)); + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->create_leaderboard_list) + return (rc_client_leaderboard_list_t*)client->state.external_client->create_leaderboard_list(grouping); +#endif + + if (!client->game) return calloc(1, sizeof(rc_client_leaderboard_list_t)); memset(&bucket_counts, 0, sizeof(bucket_counts)); @@ -3385,8 +3573,8 @@ rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* cli buckets_size = RC_ALIGN(num_buckets * sizeof(rc_client_leaderboard_bucket_t)); - list = (rc_client_leaderboard_list_t*)malloc(list_size + buckets_size + num_leaderboards * sizeof(rc_client_leaderboard_t*)); - bucket_ptr = list->buckets = (rc_client_leaderboard_bucket_t*)((uint8_t*)list + list_size); + list = (rc_client_leaderboard_list_info_t*)malloc(list_size + buckets_size + num_leaderboards * sizeof(rc_client_leaderboard_t*)); + bucket_ptr = list->public_.buckets = (rc_client_leaderboard_bucket_t*)((uint8_t*)list + list_size); leaderboard_ptr = (rc_client_leaderboard_t**)((uint8_t*)bucket_ptr + buckets_size); if (grouping == RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING) { @@ -3455,13 +3643,17 @@ rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* cli rc_mutex_unlock(&client->state.mutex); - list->num_buckets = (uint32_t)(bucket_ptr - list->buckets); - return list; + list->destroy_func = NULL; + list->public_.num_buckets = (uint32_t)(bucket_ptr - list->public_.buckets); + return &list->public_; } void rc_client_destroy_leaderboard_list(rc_client_leaderboard_list_t* list) { - if (list) + rc_client_leaderboard_list_info_t* info = (rc_client_leaderboard_list_info_t*)list; + if (info->destroy_func) + info->destroy_func(info); + else free(list); } @@ -3470,7 +3662,15 @@ int rc_client_has_leaderboards(rc_client_t* client) rc_client_subset_info_t* subset; int result; - if (!client || !client->game) + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->has_leaderboards) + return client->state.external_client->has_leaderboards(); +#endif + + if (!client->game) return 0; rc_mutex_lock(&client->state.mutex); @@ -3843,24 +4043,26 @@ static void rc_client_fetch_leaderboard_entries_callback(const rc_api_server_res lbinfo_callback_data->callback(result, error_message, NULL, client, lbinfo_callback_data->callback_userdata); } else { - rc_client_leaderboard_entry_list_t* list; - const size_t list_size = sizeof(*list) + sizeof(rc_client_leaderboard_entry_t) * lbinfo_response.num_entries; + rc_client_leaderboard_entry_list_info_t* info; + const size_t list_size = sizeof(*info) + sizeof(rc_client_leaderboard_entry_t) * lbinfo_response.num_entries; size_t needed_size = list_size; uint32_t i; for (i = 0; i < lbinfo_response.num_entries; i++) needed_size += strlen(lbinfo_response.entries[i].username) + 1; - list = (rc_client_leaderboard_entry_list_t*)malloc(needed_size); - if (!list) { + info = (rc_client_leaderboard_entry_list_info_t*)malloc(needed_size); + if (!info) { lbinfo_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, lbinfo_callback_data->callback_userdata); } else { - rc_client_leaderboard_entry_t* entry = list->entries = (rc_client_leaderboard_entry_t*)((uint8_t*)list + sizeof(*list)); + rc_client_leaderboard_entry_list_t* list = &info->public_; + rc_client_leaderboard_entry_t* entry = list->entries = (rc_client_leaderboard_entry_t*)((uint8_t*)info + sizeof(*info)); char* user = (char*)((uint8_t*)list + list_size); const rc_api_lboard_info_entry_t* lbentry = lbinfo_response.entries; const rc_api_lboard_info_entry_t* stop = lbentry + lbinfo_response.num_entries; const size_t logged_in_user_len = strlen(client->user.display_name) + 1; + info->destroy_func = NULL; list->user_index = -1; for (; lbentry < stop; ++lbentry, ++entry) { @@ -3894,6 +4096,7 @@ static rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_info(rc_clien rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata) { rc_client_fetch_leaderboard_entries_callback_data_t* callback_data; + rc_client_async_handle_t* async_handle; rc_api_request_t request; int result; const char* error_message; @@ -3917,11 +4120,12 @@ static rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_info(rc_clien callback_data->callback_userdata = callback_userdata; callback_data->leaderboard_id = lbinfo_request->leaderboard_id; - rc_client_begin_async(client, &callback_data->async_handle); + async_handle = &callback_data->async_handle; + rc_client_begin_async(client, async_handle); client->callbacks.server_call(&request, rc_client_fetch_leaderboard_entries_callback, callback_data, client); rc_api_destroy_request(&request); - return &callback_data->async_handle; + return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL; } rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_entries(rc_client_t* client, uint32_t leaderboard_id, @@ -3929,6 +4133,11 @@ rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_entries(rc_client_t* { rc_api_fetch_leaderboard_info_request_t lbinfo_request; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_fetch_leaderboard_entries) + return client->state.external_client->begin_fetch_leaderboard_entries(client, leaderboard_id, first_entry, count, callback, callback_userdata); +#endif + memset(&lbinfo_request, 0, sizeof(lbinfo_request)); lbinfo_request.leaderboard_id = leaderboard_id; lbinfo_request.first_entry = first_entry; @@ -3942,6 +4151,11 @@ rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_entries_around_user( { rc_api_fetch_leaderboard_info_request_t lbinfo_request; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->begin_fetch_leaderboard_entries_around_user) + return client->state.external_client->begin_fetch_leaderboard_entries_around_user(client, leaderboard_id, count, callback, callback_userdata); +#endif + memset(&lbinfo_request, 0, sizeof(lbinfo_request)); lbinfo_request.leaderboard_id = leaderboard_id; lbinfo_request.username = client->user.username; @@ -3957,7 +4171,10 @@ rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_entries_around_user( void rc_client_destroy_leaderboard_entry_list(rc_client_leaderboard_entry_list_t* list) { - if (list) + rc_client_leaderboard_entry_list_info_t* info = (rc_client_leaderboard_entry_list_info_t*)list; + if (info->destroy_func) + info->destroy_func(info); + else free(list); } @@ -4022,10 +4239,15 @@ static void rc_client_ping(rc_client_scheduled_callback_data_t* callback_data, r int rc_client_has_rich_presence(rc_client_t* client) { - if (!client || !client->game) + if (!client) return 0; - if (!client->game->runtime.richpresence || !client->game->runtime.richpresence->richpresence) +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->has_rich_presence) + return client->state.external_client->has_rich_presence(); +#endif + + if (!client->game || !client->game->runtime.richpresence || !client->game->runtime.richpresence->richpresence) return 0; return 1; @@ -4035,7 +4257,15 @@ size_t rc_client_get_rich_presence_message(rc_client_t* client, char buffer[], s { int result; - if (!client || !client->game || !buffer) + if (!client || !buffer) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_rich_presence_message) + return client->state.external_client->get_rich_presence_message(buffer, buffer_size); +#endif + + if (!client->game) return 0; rc_mutex_lock(&client->state.mutex); @@ -4045,8 +4275,12 @@ size_t rc_client_get_rich_presence_message(rc_client_t* client, char buffer[], s rc_mutex_unlock(&client->state.mutex); - if (result == 0) + if (result == 0) { result = snprintf(buffer, buffer_size, "Playing %s", client->game->public_.title); + /* snprintf will return the amount of space needed, we want to return the number of chars written */ + if ((size_t)result >= buffer_size) + return (buffer_size - 1); + } return result; } @@ -4055,14 +4289,28 @@ size_t rc_client_get_rich_presence_message(rc_client_t* client, char buffer[], s void rc_client_set_event_handler(rc_client_t* client, rc_client_event_handler_t handler) { - if (client) - client->callbacks.event_handler = handler; + if (!client) + return; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_event_handler) + client->state.external_client->set_event_handler(client, handler); +#endif + + client->callbacks.event_handler = handler; } void rc_client_set_read_memory_function(rc_client_t* client, rc_client_read_memory_func_t handler) { - if (client) - client->callbacks.read_memory = handler; + if (!client) + return; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_read_memory) + client->state.external_client->set_read_memory(client, handler); +#endif + + client->callbacks.read_memory = handler; } static void rc_client_invalidate_processing_memref(rc_client_t* client) @@ -4173,7 +4421,15 @@ void rc_client_set_legacy_peek(rc_client_t* client, int method) int rc_client_is_processing_required(rc_client_t* client) { - if (!client || !client->game) + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->is_processing_required) + return client->state.external_client->is_processing_required(); +#endif + + if (!client->game) return 0; if (client->game->runtime.trigger_count || client->game->runtime.lboard_count) @@ -4594,6 +4850,13 @@ void rc_client_do_frame(rc_client_t* client) if (!client) return; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->do_frame) { + client->state.external_client->do_frame(); + return; + } +#endif + if (client->game && !client->game->waiting_for_reset) { rc_runtime_richpresence_t* richpresence; rc_client_subset_info_t* subset; @@ -4639,6 +4902,13 @@ void rc_client_idle(rc_client_t* client) if (!client) return; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->idle) { + client->state.external_client->idle(); + return; + } +#endif + scheduled_callback = client->state.scheduled_callbacks; if (scheduled_callback) { const rc_clock_t now = client->callbacks.get_time_millisecs(client); @@ -4765,7 +5035,17 @@ static void rc_client_reset_all(rc_client_t* client) void rc_client_reset(rc_client_t* client) { rc_client_game_hash_t* game_hash; - if (!client || !client->game) + if (!client) + return; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->reset) { + client->state.external_client->reset(); + return; + } +#endif + + if (!client->game) return; game_hash = rc_client_find_game_hash(client, client->game->public_.hash); @@ -4796,7 +5076,15 @@ size_t rc_client_progress_size(rc_client_t* client) { size_t result; - if (!client || !client->game) + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->progress_size) + return client->state.external_client->progress_size(); +#endif + + if (!client->game) return 0; rc_mutex_lock(&client->state.mutex); @@ -4810,7 +5098,15 @@ int rc_client_serialize_progress(rc_client_t* client, uint8_t* buffer) { int result; - if (!client || !client->game) + if (!client) + return RC_NO_GAME_LOADED; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->serialize_progress) + return client->state.external_client->serialize_progress(buffer); +#endif + + if (!client->game) return RC_NO_GAME_LOADED; if (!buffer) @@ -4921,7 +5217,15 @@ int rc_client_deserialize_progress(rc_client_t* client, const uint8_t* serialize rc_client_subset_info_t* subset; int result; - if (!client || !client->game) + if (!client) + return RC_NO_GAME_LOADED; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->deserialize_progress) + return client->state.external_client->deserialize_progress(serialized); +#endif + + if (!client->game) return RC_NO_GAME_LOADED; rc_mutex_lock(&client->state.mutex); @@ -4988,6 +5292,13 @@ void rc_client_set_hardcore_enabled(rc_client_t* client, int enabled) if (!client) return; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_hardcore_enabled) { + client->state.external_client->set_hardcore_enabled(enabled); + return; + } +#endif + rc_mutex_lock(&client->state.mutex); enabled = enabled ? 1 : 0; @@ -5022,51 +5333,107 @@ void rc_client_set_hardcore_enabled(rc_client_t* client, int enabled) int rc_client_get_hardcore_enabled(const rc_client_t* client) { - return client && client->state.hardcore; + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_hardcore_enabled) + return client->state.external_client->get_hardcore_enabled(); +#endif + + return client->state.hardcore; } void rc_client_set_unofficial_enabled(rc_client_t* client, int enabled) { - if (client) { - RC_CLIENT_LOG_INFO_FORMATTED(client, "Unofficial %s", enabled ? "enabled" : "disabled"); - client->state.unofficial_enabled = enabled ? 1 : 0; + if (!client) + return; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_unofficial_enabled) { + client->state.external_client->set_unofficial_enabled(enabled); + return; } +#endif + + RC_CLIENT_LOG_INFO_FORMATTED(client, "Unofficial %s", enabled ? "enabled" : "disabled"); + client->state.unofficial_enabled = enabled ? 1 : 0; } int rc_client_get_unofficial_enabled(const rc_client_t* client) { - return client && client->state.unofficial_enabled; + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_unofficial_enabled) + return client->state.external_client->get_unofficial_enabled(); +#endif + + return client->state.unofficial_enabled; } void rc_client_set_encore_mode_enabled(rc_client_t* client, int enabled) { - if (client) { - RC_CLIENT_LOG_INFO_FORMATTED(client, "Encore mode %s", enabled ? "enabled" : "disabled"); - client->state.encore_mode = enabled ? 1 : 0; + if (!client) + return; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_encore_mode_enabled) { + client->state.external_client->set_encore_mode_enabled(enabled); + return; } +#endif + + RC_CLIENT_LOG_INFO_FORMATTED(client, "Encore mode %s", enabled ? "enabled" : "disabled"); + client->state.encore_mode = enabled ? 1 : 0; } int rc_client_get_encore_mode_enabled(const rc_client_t* client) { - return client && client->state.encore_mode; + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_encore_mode_enabled) + return client->state.external_client->get_encore_mode_enabled(); +#endif + + return client->state.encore_mode; } void rc_client_set_spectator_mode_enabled(rc_client_t* client, int enabled) { - if (client) { - if (!enabled && client->state.spectator_mode == RC_CLIENT_SPECTATOR_MODE_LOCKED) { - RC_CLIENT_LOG_WARN(client, "Spectator mode cannot be disabled if it was enabled prior to loading game."); - return; - } + if (!client) + return; - RC_CLIENT_LOG_INFO_FORMATTED(client, "Spectator mode %s", enabled ? "enabled" : "disabled"); - client->state.spectator_mode = enabled ? RC_CLIENT_SPECTATOR_MODE_ON : RC_CLIENT_SPECTATOR_MODE_OFF; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_spectator_mode_enabled) { + client->state.external_client->set_spectator_mode_enabled(enabled); + return; } +#endif + + if (!enabled && client->state.spectator_mode == RC_CLIENT_SPECTATOR_MODE_LOCKED) { + RC_CLIENT_LOG_WARN(client, "Spectator mode cannot be disabled if it was enabled prior to loading game."); + return; + } + + RC_CLIENT_LOG_INFO_FORMATTED(client, "Spectator mode %s", enabled ? "enabled" : "disabled"); + client->state.spectator_mode = enabled ? RC_CLIENT_SPECTATOR_MODE_ON : RC_CLIENT_SPECTATOR_MODE_OFF; } int rc_client_get_spectator_mode_enabled(const rc_client_t* client) { - return client && (client->state.spectator_mode == RC_CLIENT_SPECTATOR_MODE_OFF) ? 0 : 1; + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_spectator_mode_enabled) + return client->state.external_client->get_spectator_mode_enabled(); +#endif + + return (client->state.spectator_mode == RC_CLIENT_SPECTATOR_MODE_OFF) ? 0 : 1; } void rc_client_set_userdata(rc_client_t* client, void* userdata) @@ -5094,4 +5461,9 @@ void rc_client_set_host(const rc_client_t* client, const char* hostname) RC_CLIENT_LOG_VERBOSE_FORMATTED(client, "Using host: %s", hostname); } rc_api_set_host(hostname); + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->set_host) + client->state.external_client->set_host(hostname); +#endif } diff --git a/dep/rcheevos/src/rc_client_external.h b/dep/rcheevos/src/rc_client_external.h new file mode 100644 index 000000000..32258ff39 --- /dev/null +++ b/dep/rcheevos/src/rc_client_external.h @@ -0,0 +1,133 @@ +#ifndef RC_CLIENT_EXTERNAL_H +#define RC_CLIENT_EXTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "rc_client.h" + +/* NOTE: any function that is passed a callback also needs to be passed a client instance to pass + * to the callback, and the external interface has to capture both. */ + +typedef void (*rc_client_external_enable_logging_func_t)(rc_client_t* client, int level, rc_client_message_callback_t callback); +typedef void (*rc_client_external_set_event_handler_func_t)(rc_client_t* client, rc_client_event_handler_t handler); +typedef void (*rc_client_external_set_read_memory_func_t)(rc_client_t* client, rc_client_read_memory_func_t handler); +typedef void (*rc_client_external_set_get_time_millisecs_func_t)(rc_client_t* client, rc_get_time_millisecs_func_t handler); + +typedef void (*rc_client_external_set_int_func_t)(int value); +typedef int (*rc_client_external_get_int_func_t)(void); +typedef void (*rc_client_external_set_string_func_t)(const char* value); +typedef size_t (*rc_client_external_copy_string_func_t)(char buffer[], size_t buffer_size); +typedef void (*rc_client_external_action_func_t)(void); + +typedef void (*rc_client_external_async_handle_func_t)(rc_client_async_handle_t* handle); + +typedef rc_client_async_handle_t* (*rc_client_external_begin_login_func_t)(rc_client_t* client, + const char* username, const char* pass_token, rc_client_callback_t callback, void* callback_userdata); +typedef const rc_client_user_t* (*rc_client_external_get_user_info_func_t)(void); + +typedef rc_client_async_handle_t* (*rc_client_external_begin_identify_and_load_game_func_t)( + rc_client_t* client, uint32_t console_id, const char* file_path, + const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata); +typedef rc_client_async_handle_t* (*rc_client_external_begin_load_game_func_t)(rc_client_t* client, + const char* hash, rc_client_callback_t callback, void* callback_userdata); +typedef rc_client_async_handle_t* (*rc_client_external_begin_load_subset_t)(rc_client_t* client, + uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata); +typedef const rc_client_game_t* (*rc_client_external_get_game_info_func_t)(void); +typedef const rc_client_subset_t* (*rc_client_external_get_subset_info_func_t)(uint32_t subset_id); +typedef void (*rc_client_external_get_user_game_summary_func_t)(rc_client_user_game_summary_t* summary); +typedef rc_client_async_handle_t* (*rc_client_external_begin_change_media_func_t)(rc_client_t* client, const char* file_path, + const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata); + +/* NOTE: rc_client_external_create_achievement_list_func_t returns an internal wrapper structure which contains the public list + * and a destructor function. */ +struct rc_client_achievement_list_info_t; +typedef struct rc_client_achievement_list_info_t* (*rc_client_external_create_achievement_list_func_t)(int category, int grouping); +typedef const rc_client_achievement_t* (*rc_client_external_get_achievement_info_func_t)(uint32_t id); + +/* NOTE: rc_client_external_create_leaderboard_list_func_t returns an internal wrapper structure which contains the public list + * and a destructor function. */ +struct rc_client_leaderboard_list_info_t; +typedef struct rc_client_leaderboard_list_info_t* (*rc_client_external_create_leaderboard_list_func_t)(int grouping); +typedef const rc_client_leaderboard_t* (*rc_client_external_get_leaderboard_info_func_t)(uint32_t id); + +/* NOTE: rc_client_external_begin_fetch_leaderboard_entries_func_t and rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t + * pass an internal wrapper structure around the list, which contains the public list and a destructor function. */ +typedef rc_client_async_handle_t* (*rc_client_external_begin_fetch_leaderboard_entries_func_t)(rc_client_t* client, + uint32_t leaderboard_id, uint32_t first_entry, uint32_t count, + rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata); +typedef rc_client_async_handle_t* (*rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t)(rc_client_t* client, + uint32_t leaderboard_id, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata); + + +typedef size_t (*rc_client_external_progress_size_func_t)(void); +typedef int (*rc_client_external_serialize_progress_func_t)(uint8_t* buffer); +typedef int (*rc_client_external_deserialize_progress_func_t)(const uint8_t* buffer); + +typedef struct rc_client_external_t +{ + rc_client_external_action_func_t destroy; + + rc_client_external_enable_logging_func_t enable_logging; + rc_client_external_set_event_handler_func_t set_event_handler; + rc_client_external_set_read_memory_func_t set_read_memory; + rc_client_external_set_get_time_millisecs_func_t set_get_time_millisecs; + rc_client_external_set_string_func_t set_host; + + rc_client_external_set_int_func_t set_hardcore_enabled; + rc_client_external_get_int_func_t get_hardcore_enabled; + rc_client_external_set_int_func_t set_unofficial_enabled; + rc_client_external_get_int_func_t get_unofficial_enabled; + rc_client_external_set_int_func_t set_encore_mode_enabled; + rc_client_external_get_int_func_t get_encore_mode_enabled; + rc_client_external_set_int_func_t set_spectator_mode_enabled; + rc_client_external_get_int_func_t get_spectator_mode_enabled; + + rc_client_external_async_handle_func_t abort_async; + + rc_client_external_begin_login_func_t begin_login_with_password; + rc_client_external_begin_login_func_t begin_login_with_token; + rc_client_external_action_func_t logout; + rc_client_external_get_user_info_func_t get_user_info; + + rc_client_external_begin_identify_and_load_game_func_t begin_identify_and_load_game; + rc_client_external_begin_load_game_func_t begin_load_game; + rc_client_external_get_game_info_func_t get_game_info; + rc_client_external_begin_load_subset_t begin_load_subset; + rc_client_external_get_subset_info_func_t get_subset_info; + rc_client_external_action_func_t unload_game; + rc_client_external_get_user_game_summary_func_t get_user_game_summary; + rc_client_external_begin_change_media_func_t begin_change_media; + + rc_client_external_create_achievement_list_func_t create_achievement_list; + rc_client_external_get_int_func_t has_achievements; + rc_client_external_get_achievement_info_func_t get_achievement_info; + + rc_client_external_create_leaderboard_list_func_t create_leaderboard_list; + rc_client_external_get_int_func_t has_leaderboards; + rc_client_external_get_leaderboard_info_func_t get_leaderboard_info; + rc_client_external_begin_fetch_leaderboard_entries_func_t begin_fetch_leaderboard_entries; + rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t begin_fetch_leaderboard_entries_around_user; + + rc_client_external_copy_string_func_t get_rich_presence_message; + rc_client_external_get_int_func_t has_rich_presence; + + rc_client_external_action_func_t do_frame; + rc_client_external_action_func_t idle; + rc_client_external_get_int_func_t is_processing_required; + rc_client_external_action_func_t reset; + + rc_client_external_progress_size_func_t progress_size; + rc_client_external_serialize_progress_func_t serialize_progress; + rc_client_external_deserialize_progress_func_t deserialize_progress; + +} rc_client_external_t; + +#define RC_CLIENT_EXTERNAL_VERSION 1 + +#ifdef __cplusplus +} +#endif + +#endif /* RC_CLIENT_EXTERNAL_H */ diff --git a/dep/rcheevos/src/rc_client_internal.h b/dep/rcheevos/src/rc_client_internal.h index c3ff58821..9c5454770 100644 --- a/dep/rcheevos/src/rc_client_internal.h +++ b/dep/rcheevos/src/rc_client_internal.h @@ -7,6 +7,13 @@ extern "C" { #include "rc_client.h" +#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION + #include "rc_client_raintegration_internal.h" +#endif +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + #include "rc_client_external.h" +#endif + #include "rc_compat.h" #include "rc_runtime.h" #include "rc_runtime_types.h" @@ -50,6 +57,12 @@ typedef struct rc_client_scheduled_callback_data_t void rc_client_schedule_callback(rc_client_t* client, rc_client_scheduled_callback_data_t* scheduled_callback); +struct rc_client_async_handle_t { + uint8_t aborted; +}; + +int rc_client_async_handle_aborted(rc_client_t* client, rc_client_async_handle_t* async_handle); + /*****************************************************************************\ | Achievements | \*****************************************************************************/ @@ -78,6 +91,14 @@ typedef struct rc_client_achievement_info_t { time_t updated_time; } rc_client_achievement_info_t; +struct rc_client_achievement_list_info_t; +typedef void (*rc_client_destroy_achievement_list_func_t)(struct rc_client_achievement_list_info_t* list); + +typedef struct rc_client_achievement_list_info_t { + rc_client_achievement_list_t public_; + rc_client_destroy_achievement_list_func_t destroy_func; +} rc_client_achievement_list_info_t; + enum { RC_CLIENT_PROGRESS_TRACKER_ACTION_NONE, RC_CLIENT_PROGRESS_TRACKER_ACTION_SHOW, @@ -145,6 +166,22 @@ typedef struct rc_client_leaderboard_info_t { uint8_t hidden; } rc_client_leaderboard_info_t; +struct rc_client_leaderboard_list_info_t; +typedef void (*rc_client_destroy_leaderboard_list_func_t)(struct rc_client_leaderboard_list_info_t* list); + +typedef struct rc_client_leaderboard_list_info_t { + rc_client_leaderboard_list_t public_; + rc_client_destroy_leaderboard_list_func_t destroy_func; +} rc_client_leaderboard_list_info_t; + +struct rc_client_leaderboard_entry_list_info_t; +typedef void (*rc_client_destroy_leaderboard_entry_list_func_t)(struct rc_client_leaderboard_entry_list_info_t* list); + +typedef struct rc_client_leaderboard_entry_list_info_t { + rc_client_leaderboard_entry_list_t public_; + rc_client_destroy_leaderboard_entry_list_func_t destroy_func; +} rc_client_leaderboard_entry_list_info_t; + uint8_t rc_client_map_leaderboard_format(int format); /*****************************************************************************\ @@ -177,7 +214,7 @@ typedef struct rc_client_subset_info_t { 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); +rc_client_async_handle_t* rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata); /*****************************************************************************\ | Game | @@ -273,6 +310,13 @@ typedef struct rc_client_state_t { rc_client_scheduled_callback_data_t* scheduled_callbacks; +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + rc_client_external_t* external_client; +#endif +#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION + rc_client_raintegration_t* raintegration; +#endif + uint8_t hardcore; uint8_t encore_mode; uint8_t spectator_mode; diff --git a/dep/rcheevos/src/rc_client_raintegration.c b/dep/rcheevos/src/rc_client_raintegration.c new file mode 100644 index 000000000..eac5218fb --- /dev/null +++ b/dep/rcheevos/src/rc_client_raintegration.c @@ -0,0 +1,373 @@ +#include "rc_client_raintegration_internal.h" + +#include "rc_client_internal.h" + +#include "rapi/rc_api_common.h" + +#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION + +static void rc_client_raintegration_load_dll(rc_client_t* client, + const wchar_t* search_directory, rc_client_callback_t callback, void* callback_userdata) +{ + wchar_t sPath[_MAX_PATH]; + const int nPathSize = sizeof(sPath) / sizeof(sPath[0]); + rc_client_raintegration_t* raintegration; + int sPathIndex = 0; + DWORD dwAttrib; + HINSTANCE hDLL; + + if (search_directory) { + sPathIndex = swprintf_s(sPath, nPathSize, L"%s\\", search_directory); + if (sPathIndex > nPathSize - 22) { + callback(RC_INVALID_STATE, "search_directory too long", client, callback_userdata); + return; + } + } + +#if defined(_M_X64) || defined(__amd64__) + wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration-x64.dll"); + dwAttrib = GetFileAttributesW(sPath); + if (dwAttrib == INVALID_FILE_ATTRIBUTES) { + wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration.dll"); + dwAttrib = GetFileAttributesW(sPath); + } +#else + wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration.dll"); + dwAttrib = GetFileAttributesW(sPath); +#endif + + if (dwAttrib == INVALID_FILE_ATTRIBUTES) { + callback(RC_MISSING_VALUE, "RA_Integration.dll not found in search directory", client, callback_userdata); + return; + } + + hDLL = LoadLibraryW(sPath); + if (hDLL == NULL) { + char error_message[512]; + const DWORD last_error = GetLastError(); + int offset = snprintf(error_message, sizeof(error_message), "Failed to load RA_Integration.dll (%u)", last_error); + + if (last_error != 0) { + LPSTR messageBuffer = NULL; + const DWORD size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + + snprintf(&error_message[offset], sizeof(error_message) - offset, ": %.*s", size, messageBuffer); + + LocalFree(messageBuffer); + } + + callback(RC_ABORTED, error_message, client, callback_userdata); + return; + } + + raintegration = (rc_client_raintegration_t*)rc_buffer_alloc(&client->state.buffer, sizeof(rc_client_raintegration_t)); + memset(raintegration, 0, sizeof(*raintegration)); + raintegration->hDLL = hDLL; + + raintegration->get_version = (rc_client_raintegration_get_string_func)GetProcAddress(hDLL, "_RA_IntegrationVersion"); + raintegration->get_host_url = (rc_client_raintegration_get_string_func)GetProcAddress(hDLL, "_RA_HostUrl"); + raintegration->init_client = (rc_client_raintegration_init_client_func)GetProcAddress(hDLL, "_RA_InitClient"); + raintegration->init_client_offline = (rc_client_raintegration_init_client_func)GetProcAddress(hDLL, "_RA_InitOffline"); + raintegration->shutdown = (rc_client_raintegration_action_func)GetProcAddress(hDLL, "_RA_Shutdown"); + + raintegration->get_external_client = (rc_client_raintegration_get_external_client)GetProcAddress(hDLL, "_Rcheevos_GetExternalClient"); + + if (!raintegration->get_version || + !raintegration->init_client || + !raintegration->get_external_client) { + FreeLibrary(hDLL); + + callback(RC_ABORTED, "One or more required exports was not found in RA_Integration.dll", client, callback_userdata); + } + else { + rc_mutex_lock(&client->state.mutex); + client->state.raintegration = raintegration; + rc_mutex_unlock(&client->state.mutex); + + RC_CLIENT_LOG_INFO_FORMATTED(client, "RA_Integration.dll %s loaded", client->state.raintegration->get_version()); + } +} + +typedef struct rc_client_version_validation_callback_data_t { + rc_client_t* client; + rc_client_callback_t callback; + void* callback_userdata; + HWND main_window_handle; + char* client_name; + char* client_version; + rc_client_async_handle_t async_handle; +} rc_client_version_validation_callback_data_t; + +int rc_client_version_less(const char* left, const char* right) +{ + do { + int left_len = 0; + int right_len = 0; + while (*left && *left == '0') + ++left; + while (left[left_len] && left[left_len] != '.') + ++left_len; + while (*right && *right == '0') + ++right; + while (right[right_len] && right[right_len] != '.') + ++right_len; + + if (left_len != right_len) + return (left_len < right_len); + + while (left_len--) { + if (*left != *right) + return (*left < *right); + ++left; + ++right; + } + + if (*left == '.') + ++left; + if (*right == '.') + ++right; + } while (*left || *right); + + return 0; +} + +static void rc_client_init_raintegration(rc_client_t* client, + rc_client_version_validation_callback_data_t* version_validation_callback_data) +{ + rc_client_raintegration_init_client_func init_func = client->state.raintegration->init_client; + + if (client->state.raintegration->get_host_url) { + const char* host_url = client->state.raintegration->get_host_url(); + if (host_url) { + if (strcmp(host_url, "OFFLINE") != 0) { + rc_client_set_host(client, host_url); + } + else if (client->state.raintegration->init_client_offline) { + init_func = client->state.raintegration->init_client_offline; + RC_CLIENT_LOG_INFO(client, "Initializing in offline mode"); + } + } + } + + if (!init_func || !init_func(version_validation_callback_data->main_window_handle, + version_validation_callback_data->client_name, + version_validation_callback_data->client_version)) { + const char* error_message = "RA_Integration initialization failed"; + + rc_client_unload_raintegration(client); + + RC_CLIENT_LOG_ERR(client, error_message); + version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata); + } + else { + rc_client_external_t* external_client = (rc_client_external_t*) + rc_buffer_alloc(&client->state.buffer, sizeof(*external_client)); + memset(external_client, 0, sizeof(*external_client)); + + if (!client->state.raintegration->get_external_client(external_client, RC_CLIENT_EXTERNAL_VERSION)) { + const char* error_message = "RA_Integration external client export failed"; + + rc_client_unload_raintegration(client); + + RC_CLIENT_LOG_ERR(client, error_message); + version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata); + } + else { + /* copy state to the external client */ + if (external_client->enable_logging) + external_client->enable_logging(client, client->state.log_level, client->callbacks.log_call); + + if (external_client->set_event_handler) + external_client->set_event_handler(client, client->callbacks.event_handler); + if (external_client->set_read_memory) + external_client->set_read_memory(client, client->callbacks.read_memory); + + if (external_client->set_hardcore_enabled) + external_client->set_hardcore_enabled(rc_client_get_hardcore_enabled(client)); + if (external_client->set_unofficial_enabled) + external_client->set_unofficial_enabled(rc_client_get_unofficial_enabled(client)); + if (external_client->set_encore_mode_enabled) + external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client)); + if (external_client->set_spectator_mode_enabled) + external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client)); + + /* attach the external client and call the callback */ + client->state.external_client = external_client; + + version_validation_callback_data->callback(RC_OK, NULL, + client, version_validation_callback_data->callback_userdata); + } + } +} + +static void rc_client_version_validation_callback(const rc_api_server_response_t* server_response, void* callback_data) +{ + rc_client_version_validation_callback_data_t* version_validation_callback_data = + (rc_client_version_validation_callback_data_t*)callback_data; + rc_client_t* client = version_validation_callback_data->client; + + if (rc_client_async_handle_aborted(client, &version_validation_callback_data->async_handle)) { + RC_CLIENT_LOG_VERBOSE(client, "Version validation aborted"); + } + else { + rc_api_response_t response; + int result; + const char* current_version; + const char* minimum_version = ""; + + rc_json_field_t fields[] = { + RC_JSON_NEW_FIELD("Success"), + RC_JSON_NEW_FIELD("Error"), + RC_JSON_NEW_FIELD("MinimumVersion"), + }; + + memset(&response, 0, sizeof(response)); + rc_buffer_init(&response.buffer); + + result = rc_json_parse_server_response(&response, server_response, fields, sizeof(fields) / sizeof(fields[0])); + if (result == RC_OK) { + if (!rc_json_get_required_string(&minimum_version, &response, &fields[2], "MinimumVersion")) + result = RC_MISSING_VALUE; + } + + if (result != RC_OK) { + RC_CLIENT_LOG_ERR_FORMATTED(client, "Failed to fetch latest integration version: %.*s", server_response->body_length, server_response->body); + + rc_client_unload_raintegration(client); + + version_validation_callback_data->callback(result, rc_error_str(result), + client, version_validation_callback_data->callback_userdata); + } + else { + current_version = client->state.raintegration->get_version(); + + if (rc_client_version_less(current_version, minimum_version)) { + char error_message[256]; + + rc_client_unload_raintegration(client); + + snprintf(error_message, sizeof(error_message), + "RA_Integration version %s is lower than minimum version %s", current_version, minimum_version); + RC_CLIENT_LOG_WARN(client, error_message); + version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata); + } + else { + RC_CLIENT_LOG_INFO_FORMATTED(client, "Validated RA_Integration version %s (minimum %s)", current_version, minimum_version); + + rc_client_init_raintegration(client, version_validation_callback_data); + } + } + + rc_buffer_destroy(&response.buffer); + } + + free(version_validation_callback_data->client_name); + free(version_validation_callback_data->client_version); + free(version_validation_callback_data); +} + +rc_client_async_handle_t* rc_client_begin_load_raintegration(rc_client_t* client, + const wchar_t* search_directory, HWND main_window_handle, + const char* client_name, const char* client_version, + rc_client_callback_t callback, void* callback_userdata) +{ + rc_client_version_validation_callback_data_t* callback_data; + rc_api_url_builder_t builder; + rc_api_request_t request; + + if (!client) { + callback(RC_INVALID_STATE, "client is required", client, callback_userdata); + return NULL; + } + + if (!client_name) { + callback(RC_INVALID_STATE, "client_name is required", client, callback_userdata); + return NULL; + } + + if (!client_version) { + callback(RC_INVALID_STATE, "client_version is required", client, callback_userdata); + return NULL; + } + + if (client->state.user != RC_CLIENT_USER_STATE_NONE) { + callback(RC_INVALID_STATE, "Cannot initialize RAIntegration after login", client, callback_userdata); + return NULL; + } + + if (!client->state.raintegration) { + if (!main_window_handle) { + callback(RC_INVALID_STATE, "main_window_handle is required", client, callback_userdata); + return NULL; + } + + rc_client_raintegration_load_dll(client, search_directory, callback, callback_userdata); + if (!client->state.raintegration) + return NULL; + } + + if (client->state.raintegration->get_host_url) { + const char* host_url = client->state.raintegration->get_host_url(); + if (host_url && strcmp(host_url, "https://retroachievements.org") != 0 && + strcmp(host_url, "OFFLINE") != 0) { + /* if the DLL specifies a custom host, use it */ + rc_client_set_host(client, host_url); + } + } + + memset(&request, 0, sizeof(request)); + rc_api_url_build_dorequest_url(&request); + rc_url_builder_init(&builder, &request.buffer, 48); + rc_url_builder_append_str_param(&builder, "r", "latestintegration"); + request.post_data = rc_url_builder_finalize(&builder); + + callback_data = calloc(1, sizeof(*callback_data)); + if (!callback_data) { + callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata); + return NULL; + } + + callback_data->client = client; + callback_data->callback = callback; + callback_data->callback_userdata = callback_userdata; + callback_data->client_name = strdup(client_name); + callback_data->client_version = strdup(client_version); + callback_data->main_window_handle = main_window_handle; + + client->callbacks.server_call(&request, rc_client_version_validation_callback, callback_data, client); + return &callback_data->async_handle; +} + +void rc_client_unload_raintegration(rc_client_t* client) +{ + HINSTANCE hDLL; + + if (!client || !client->state.raintegration) + return; + + RC_CLIENT_LOG_INFO(client, "Unloading RA_Integration") + + if (client->state.raintegration->shutdown) { +#ifdef __cplusplus + try { +#endif + client->state.raintegration->shutdown(); +#ifdef __cplusplus + } + catch (std::runtime_error&) { + } +#endif + } + + rc_mutex_lock(&client->state.mutex); + hDLL = client->state.raintegration->hDLL; + client->state.raintegration = NULL; + client->state.external_client = NULL; + rc_mutex_unlock(&client->state.mutex); + + if (hDLL) + FreeLibrary(hDLL); +} + +#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */ diff --git a/dep/rcheevos/src/rc_client_raintegration_internal.h b/dep/rcheevos/src/rc_client_raintegration_internal.h new file mode 100644 index 000000000..e2be7e8de --- /dev/null +++ b/dep/rcheevos/src/rc_client_raintegration_internal.h @@ -0,0 +1,44 @@ +#ifndef RC_CLIENT_RAINTEGRATION_INTERNAL_H +#define RC_CLIENT_RAINTEGRATION_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "rc_client_raintegration.h" + +#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION + +#include "rc_client_external.h" +#include "rc_compat.h" + +#ifndef CCONV + #define CCONV __cdecl +#endif + +typedef void (CCONV* rc_client_raintegration_action_func)(void); +typedef const char* (CCONV* rc_client_raintegration_get_string_func)(void); +typedef int (CCONV* rc_client_raintegration_init_client_func)(HWND hMainWnd, const char* sClientName, const char* sClientVersion); +typedef int (CCONV* rc_client_raintegration_get_external_client)(rc_client_external_t* pClient, int nVersion); + +typedef struct rc_client_raintegration_t +{ + HINSTANCE hDLL; + + rc_client_raintegration_get_string_func get_version; + rc_client_raintegration_get_string_func get_host_url; + rc_client_raintegration_init_client_func init_client; + rc_client_raintegration_init_client_func init_client_offline; + rc_client_raintegration_action_func shutdown; + + rc_client_raintegration_get_external_client get_external_client; + +} rc_client_raintegration_t; + +#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */ + +#ifdef __cplusplus +} +#endif + +#endif /* RC_CLIENT_RAINTEGRATION_INTERNAL_H */ diff --git a/dep/rcheevos/src/rc_libretro.c b/dep/rcheevos/src/rc_libretro.c index 4facc14af..d07cce9eb 100644 --- a/dep/rcheevos/src/rc_libretro.c +++ b/dep/rcheevos/src/rc_libretro.c @@ -96,6 +96,11 @@ static const rc_disallowed_setting_t _rc_disallowed_mesen_s_settings[] = { { NULL, NULL } }; +static const rc_disallowed_setting_t _rc_disallowed_neocd_settings[] = { + { "neocd_bios", "uni-bios*" }, + { NULL, NULL } +}; + static const rc_disallowed_setting_t _rc_disallowed_pcsx_rearmed_settings[] = { { "pcsx_rearmed_region", "pal" }, { NULL, NULL } @@ -152,6 +157,7 @@ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = { { "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings }, { "Mesen", _rc_disallowed_mesen_settings }, { "Mesen-S", _rc_disallowed_mesen_s_settings }, + { "NeoCD", _rc_disallowed_neocd_settings }, { "PPSSPP", _rc_disallowed_ppsspp_settings }, { "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings }, { "PicoDrive", _rc_disallowed_picodrive_settings }, diff --git a/dep/rcheevos/src/rcheevos/alloc.c b/dep/rcheevos/src/rcheevos/alloc.c index 7b43a2c75..7ac3a8f69 100644 --- a/dep/rcheevos/src/rcheevos/alloc.c +++ b/dep/rcheevos/src/rcheevos/alloc.c @@ -5,7 +5,7 @@ void* rc_alloc_scratch(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset) { - rc_scratch_buffer_t* buffer; + void* data; /* if we have a real buffer, then allocate the data there */ if (pointer) @@ -19,49 +19,13 @@ void* rc_alloc_scratch(void* pointer, int32_t* offset, uint32_t size, uint32_t a } /* find a scratch buffer to hold the temporary data */ - buffer = &scratch->buffer; - do { - const uint32_t aligned_buffer_offset = (buffer->offset + alignment - 1) & ~(alignment - 1); - if (aligned_buffer_offset < sizeof(buffer->buffer)) { - const uint32_t remaining = sizeof(buffer->buffer) - aligned_buffer_offset; - - if (remaining >= size) { - /* claim the required space from an existing buffer */ - return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1); - } - } - - if (!buffer->next) - break; - - buffer = buffer->next; - } while (1); - - /* not enough space in any existing buffer, allocate more */ - if (size > (uint32_t)sizeof(buffer->buffer)) { - /* caller is asking for more than we can fit in a standard rc_scratch_buffer_t. - * leverage the fact that the buffer is the last field and extend its size. - * this chunk will be exactly large enough to hold the needed data, and since offset - * will exceed sizeof(buffer->buffer), it will never be eligible to hold anything else. - */ - const size_t needed = sizeof(rc_scratch_buffer_t) - sizeof(buffer->buffer) + size; - buffer->next = (rc_scratch_buffer_t*)malloc(needed); - } - else { - buffer->next = (rc_scratch_buffer_t*)malloc(sizeof(rc_scratch_buffer_t)); - } - - if (!buffer->next) { + data = rc_buffer_alloc(&scratch->buffer, size); + if (!data) { *offset = RC_OUT_OF_MEMORY; return NULL; } - buffer = buffer->next; - buffer->offset = 0; - buffer->next = NULL; - - /* claim the required space from the new buffer */ - return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1); + return data; } void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset) { @@ -137,9 +101,8 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in parse->L = L; parse->funcs_ndx = funcs_ndx; parse->buffer = buffer; - parse->scratch.buffer.offset = 0; - parse->scratch.buffer.next = NULL; parse->scratch.strings = NULL; + rc_buffer_init(&parse->scratch.buffer); memset(&parse->scratch.objs, 0, sizeof(parse->scratch.objs)); parse->first_memref = 0; parse->variables = 0; @@ -151,12 +114,5 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in void rc_destroy_parse_state(rc_parse_state_t* parse) { - rc_scratch_buffer_t* buffer = parse->scratch.buffer.next; - rc_scratch_buffer_t* next; - - while (buffer) { - next = buffer->next; - free(buffer); - buffer = next; - } + rc_buffer_destroy(&parse->scratch.buffer); } diff --git a/dep/rcheevos/src/rcheevos/rc_internal.h b/dep/rcheevos/src/rcheevos/rc_internal.h index 4a2f306a8..2623355bc 100644 --- a/dep/rcheevos/src/rcheevos/rc_internal.h +++ b/dep/rcheevos/src/rcheevos/rc_internal.h @@ -2,6 +2,7 @@ #define RC_INTERNAL_H #include "rc_runtime_types.h" +#include "../rc_util.h" #ifdef __cplusplus extern "C" { @@ -39,15 +40,8 @@ RC_ALLOW_ALIGN(char) /* force alignment to 4 bytes on 32-bit systems, or 8 bytes on 64-bit systems */ #define RC_ALIGN(n) (((n) + (sizeof(void*)-1)) & ~(sizeof(void*)-1)) -typedef struct rc_scratch_buffer { - struct rc_scratch_buffer* next; - int32_t offset; - uint8_t buffer[512 - 16]; -} -rc_scratch_buffer_t; - typedef struct { - rc_scratch_buffer_t buffer; + rc_buffer_t buffer; rc_scratch_string_t* strings; struct objs { diff --git a/dep/rcheevos/src/rcheevos/richpresence.c b/dep/rcheevos/src/rcheevos/richpresence.c index db58c76c0..31df33426 100644 --- a/dep/rcheevos/src/rcheevos/richpresence.c +++ b/dep/rcheevos/src/rcheevos/richpresence.c @@ -279,7 +279,6 @@ static void rc_rebalance_richpresence_lookup_rebuild(rc_richpresence_lookup_item static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** root, rc_parse_state_t* parse) { rc_richpresence_lookup_item_t** items; - rc_scratch_buffer_t* buffer; int index; int size; @@ -288,29 +287,13 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo if (count < 3) return; - /* allocate space for the flattened list - prefer scratch memory if available */ + /* allocate space for the flattened list in scratch memory */ size = count * sizeof(rc_richpresence_lookup_item_t*); - buffer = &parse->scratch.buffer; - do { - const int aligned_offset = RC_ALIGN(buffer->offset); - const int remaining = sizeof(buffer->buffer) - aligned_offset; + items = (rc_richpresence_lookup_item_t**)rc_buffer_alloc(&parse->scratch.buffer, size); - if (remaining >= size) { - items = (rc_richpresence_lookup_item_t**)&buffer->buffer[aligned_offset]; - break; - } - - buffer = buffer->next; - if (buffer == NULL) { - /* could not find large enough block of scratch memory; allocate. if allocation fails, - * we can still use the unbalanced tree, so just bail out */ - items = (rc_richpresence_lookup_item_t**)malloc(size); - if (items == NULL) - return; - - break; - } - } while (1); + /* if allocation fails, we can still use the unbalanced tree, so just bail out */ + if (items == NULL) + return; /* flatten the list */ index = 0; @@ -318,9 +301,6 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo /* and rebuild it as a balanced tree */ rc_rebalance_richpresence_lookup_rebuild(root, items, 0, count - 1); - - if (buffer == NULL) - free(items); } static void rc_insert_richpresence_lookup_item(rc_richpresence_lookup_t* lookup, diff --git a/dep/rcheevos/src/rhash/hash.c b/dep/rcheevos/src/rhash/hash.c index 06fc03f22..0cc5fb4e7 100644 --- a/dep/rcheevos/src/rhash/hash.c +++ b/dep/rcheevos/src/rhash/hash.c @@ -2594,7 +2594,8 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* iterator->consoles[4] = RC_CONSOLE_PSP; iterator->consoles[5] = RC_CONSOLE_PC_ENGINE_CD; iterator->consoles[6] = RC_CONSOLE_3DO; - iterator->consoles[7] = RC_CONSOLE_PCFX; + iterator->consoles[7] = RC_CONSOLE_NEO_GEO_CD; + iterator->consoles[8] = RC_CONSOLE_PCFX; need_path = 1; } else if (rc_path_compare_extension(ext, "col"))