mirror of
https://github.com/libretro/RetroArch.git
synced 2026-01-09 06:34:03 +08:00
update to rcheevos 12.2 (#18554)
This commit is contained in:
parent
bfeac2e82c
commit
3bd71352f8
15
deps/rcheevos/CHANGELOG.md
vendored
15
deps/rcheevos/CHANGELOG.md
vendored
@ -1,3 +1,18 @@
|
|||||||
|
# v12.2.0
|
||||||
|
* add rc_client_create_subset_list
|
||||||
|
* add rc_client_begin_fetch_game_titles
|
||||||
|
* greatly improve performance parsing long AddSource chains
|
||||||
|
* don't send pings if not processing frames; allows server to suspend session while emulator is paused
|
||||||
|
* modify validation logic to return most severe error instead of first error found
|
||||||
|
* improve validation warning when 'PauseIf {recall}' attempts to use non-PauseIf Remember
|
||||||
|
* fix rounding error when subtracting floats from integers
|
||||||
|
* fix infinite loop processing 'Remember {recall}' with no modifiers
|
||||||
|
* fix measured value jumping to 0 if all measured-generating alts are paused
|
||||||
|
* fix buffer overflow converting long user names between rc_client_external versions
|
||||||
|
* fix validation warning when adding differently sized values
|
||||||
|
* fix validation warning when ResetIf only applies to hit count inside an AndNext chain
|
||||||
|
* fix validation warning when only last node of modified memref chain differs
|
||||||
|
|
||||||
# v12.1.0
|
# v12.1.0
|
||||||
* add rc_client_get_user_subset_summary
|
* add rc_client_get_user_subset_summary
|
||||||
* add validation warning for using MeasuredIf without Measured
|
* add validation warning for using MeasuredIf without Measured
|
||||||
|
|||||||
2
deps/rcheevos/include/rc_api_info.h
vendored
2
deps/rcheevos/include/rc_api_info.h
vendored
@ -211,6 +211,8 @@ typedef struct rc_api_game_title_entry_t {
|
|||||||
const char* title;
|
const char* title;
|
||||||
/* The image name for the game badge */
|
/* The image name for the game badge */
|
||||||
const char* image_name;
|
const char* image_name;
|
||||||
|
/* The URL for the game badge image */
|
||||||
|
const char* image_url;
|
||||||
}
|
}
|
||||||
rc_api_game_title_entry_t;
|
rc_api_game_title_entry_t;
|
||||||
|
|
||||||
|
|||||||
54
deps/rcheevos/include/rc_client.h
vendored
54
deps/rcheevos/include/rc_client.h
vendored
@ -364,6 +364,22 @@ RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client
|
|||||||
|
|
||||||
RC_EXPORT void RC_CCONV rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary);
|
RC_EXPORT void RC_CCONV rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary);
|
||||||
|
|
||||||
|
typedef struct rc_client_subset_list_t {
|
||||||
|
const rc_client_subset_t** subsets;
|
||||||
|
uint32_t num_subsets;
|
||||||
|
} rc_client_subset_list_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a list of subsets for the currently loaded game.
|
||||||
|
* Returns an allocated list that must be free'd by calling rc_client_destroy_subset_list.
|
||||||
|
*/
|
||||||
|
RC_EXPORT rc_client_subset_list_t* RC_CCONV rc_client_create_subset_list(rc_client_t* client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys a list allocated by rc_client_create_subset_list_list.
|
||||||
|
*/
|
||||||
|
RC_EXPORT void RC_CCONV rc_client_destroy_subset_list(rc_client_subset_list_t* list);
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
| Fetch Game Hashes |
|
| Fetch Game Hashes |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
@ -398,6 +414,42 @@ RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_hash_library(
|
|||||||
*/
|
*/
|
||||||
RC_EXPORT void RC_CCONV rc_client_destroy_hash_library(rc_client_hash_library_t* list);
|
RC_EXPORT void RC_CCONV rc_client_destroy_hash_library(rc_client_hash_library_t* list);
|
||||||
|
|
||||||
|
/*****************************************************************************\
|
||||||
|
| Fetch Game Titles |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct rc_client_game_title_entry_t {
|
||||||
|
uint32_t game_id;
|
||||||
|
const char* title;
|
||||||
|
char badge_name[16];
|
||||||
|
const char* badge_url;
|
||||||
|
} rc_client_game_title_entry_t;
|
||||||
|
|
||||||
|
typedef struct rc_client_game_title_list_t {
|
||||||
|
rc_client_game_title_entry_t* entries;
|
||||||
|
uint32_t num_entries;
|
||||||
|
} rc_client_game_title_list_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is fired when a game titles request completes. list may be null if the query failed.
|
||||||
|
*/
|
||||||
|
typedef void(RC_CCONV* rc_client_fetch_game_titles_callback_t)(int result, const char* error_message,
|
||||||
|
rc_client_game_title_list_t* list, rc_client_t* client,
|
||||||
|
void* callback_userdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an asynchronous request for titles and badge names for the specified games.
|
||||||
|
* The caller must provide an array of game IDs and the number of IDs in the array.
|
||||||
|
*/
|
||||||
|
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_game_titles(
|
||||||
|
rc_client_t* client, const uint32_t* game_ids, uint32_t num_game_ids,
|
||||||
|
rc_client_fetch_game_titles_callback_t callback, void* callback_userdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys a previously-allocated result from the rc_client_begin_fetch_game_titles() callback.
|
||||||
|
*/
|
||||||
|
RC_EXPORT void RC_CCONV rc_client_destroy_game_title_list(rc_client_game_title_list_t* list);
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
| Achievements |
|
| Achievements |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
@ -503,7 +555,7 @@ enum {
|
|||||||
RC_EXPORT rc_client_achievement_list_t* RC_CCONV rc_client_create_achievement_list(rc_client_t* client, int category, int grouping);
|
RC_EXPORT rc_client_achievement_list_t* RC_CCONV rc_client_create_achievement_list(rc_client_t* client, int category, int grouping);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys a list allocated by rc_client_get_achievement_list.
|
* Destroys a list allocated by rc_client_create_achievement_list.
|
||||||
*/
|
*/
|
||||||
RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement_list_t* list);
|
RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement_list_t* list);
|
||||||
|
|
||||||
|
|||||||
2
deps/rcheevos/include/rc_runtime_types.h
vendored
2
deps/rcheevos/include/rc_runtime_types.h
vendored
@ -173,6 +173,8 @@ enum {
|
|||||||
RC_OPERATOR_SUB,
|
RC_OPERATOR_SUB,
|
||||||
|
|
||||||
RC_OPERATOR_SUB_PARENT, /* internal use */
|
RC_OPERATOR_SUB_PARENT, /* internal use */
|
||||||
|
RC_OPERATOR_ADD_ACCUMULATOR, /* internal use */
|
||||||
|
RC_OPERATOR_SUB_ACCUMULATOR, /* internal use */
|
||||||
RC_OPERATOR_INDIRECT_READ /* internal use */
|
RC_OPERATOR_INDIRECT_READ /* internal use */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
7
deps/rcheevos/src/rapi/rc_api_info.c
vendored
7
deps/rcheevos/src/rapi/rc_api_info.c
vendored
@ -448,7 +448,8 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re
|
|||||||
rc_json_field_t entry_fields[] = {
|
rc_json_field_t entry_fields[] = {
|
||||||
RC_JSON_NEW_FIELD("ID"),
|
RC_JSON_NEW_FIELD("ID"),
|
||||||
RC_JSON_NEW_FIELD("Title"),
|
RC_JSON_NEW_FIELD("Title"),
|
||||||
RC_JSON_NEW_FIELD("ImageIcon")
|
RC_JSON_NEW_FIELD("ImageIcon"),
|
||||||
|
RC_JSON_NEW_FIELD("ImageUrl")
|
||||||
};
|
};
|
||||||
|
|
||||||
memset(response, 0, sizeof(*response));
|
memset(response, 0, sizeof(*response));
|
||||||
@ -482,6 +483,10 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re
|
|||||||
if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon"))
|
if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon"))
|
||||||
return RC_MISSING_VALUE;
|
return RC_MISSING_VALUE;
|
||||||
|
|
||||||
|
rc_json_get_optional_string(&entry->image_url, &response->response, &entry_fields[3], "ImageUrl", "");
|
||||||
|
if (!entry->image_url[0])
|
||||||
|
entry->image_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_GAME, entry->image_name);
|
||||||
|
|
||||||
++entry;
|
++entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
325
deps/rcheevos/src/rc_client.c
vendored
325
deps/rcheevos/src/rc_client.c
vendored
@ -1686,6 +1686,67 @@ static void rc_client_free_pending_media(rc_client_pending_media_t* pending_medi
|
|||||||
free(pending_media);
|
free(pending_media);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rc_client_log_active_assets(rc_client_t* client)
|
||||||
|
{
|
||||||
|
uint32_t num_achievements;
|
||||||
|
uint32_t num_active_achievements;
|
||||||
|
uint32_t num_unsupported_achievements;
|
||||||
|
uint32_t num_leaderboards;
|
||||||
|
uint32_t num_unsupported_leaderboards;
|
||||||
|
const rc_client_achievement_info_t* ach;
|
||||||
|
const rc_client_achievement_info_t* ach_stop;
|
||||||
|
const rc_client_leaderboard_info_t* lbd;
|
||||||
|
const rc_client_leaderboard_info_t* lbd_stop;
|
||||||
|
|
||||||
|
const rc_client_subset_info_t* subset = client->game->subsets;
|
||||||
|
for (; subset; subset = subset->next) {
|
||||||
|
num_achievements = 0;
|
||||||
|
num_active_achievements = 0;
|
||||||
|
num_unsupported_achievements = 0;
|
||||||
|
num_leaderboards = 0;
|
||||||
|
num_unsupported_leaderboards = 0;
|
||||||
|
|
||||||
|
ach = subset->achievements;
|
||||||
|
ach_stop = ach + subset->public_.num_achievements;
|
||||||
|
for (; ach < ach_stop; ++ach) {
|
||||||
|
if (ach->public_.category == RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE) {
|
||||||
|
++num_achievements;
|
||||||
|
if (ach->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE)
|
||||||
|
++num_active_achievements;
|
||||||
|
else if (ach->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_DISABLED)
|
||||||
|
++num_unsupported_achievements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lbd = subset->leaderboards;
|
||||||
|
lbd_stop = lbd + subset->public_.num_leaderboards;
|
||||||
|
for (; lbd < lbd_stop; ++lbd) {
|
||||||
|
++num_leaderboards;
|
||||||
|
if (lbd->public_.state == RC_CLIENT_LEADERBOARD_STATE_DISABLED)
|
||||||
|
++num_unsupported_leaderboards;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_unsupported_achievements) {
|
||||||
|
if (num_unsupported_leaderboards) {
|
||||||
|
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active (%u unsupported), %u leaderboards (%u unsupported)",
|
||||||
|
subset->public_.id, num_active_achievements, num_achievements, num_unsupported_achievements, num_leaderboards, num_unsupported_leaderboards);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active (%u unsupported), %u leaderboards",
|
||||||
|
subset->public_.id, num_active_achievements, num_achievements, num_unsupported_achievements, num_leaderboards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (num_unsupported_leaderboards) {
|
||||||
|
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active, %u leaderboards (%u unsupported)",
|
||||||
|
subset->public_.id, num_active_achievements, num_achievements, num_leaderboards, num_unsupported_leaderboards);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active, %u leaderboards",
|
||||||
|
subset->public_.id, num_active_achievements, num_achievements, num_leaderboards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* NOTE: address validation uses the read_memory callback to make sure the client
|
/* NOTE: address validation uses the read_memory callback to make sure the client
|
||||||
* will return data for the requested address. As such, this function must
|
* will return data for the requested address. As such, this function must
|
||||||
* respect the `client->state.allow_background_memory_reads setting. Use
|
* respect the `client->state.allow_background_memory_reads setting. Use
|
||||||
@ -1720,10 +1781,13 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
|
|||||||
|
|
||||||
/* make the loaded game active if another game is not aleady being loaded. */
|
/* make the loaded game active if another game is not aleady being loaded. */
|
||||||
rc_mutex_lock(&client->state.mutex);
|
rc_mutex_lock(&client->state.mutex);
|
||||||
if (client->state.load == load_state)
|
if (client->state.load == load_state) {
|
||||||
client->game = load_state->game;
|
client->game = load_state->game;
|
||||||
else
|
client->state.frames_processed = client->state.frames_at_last_ping = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
load_state->progress = RC_CLIENT_LOAD_GAME_STATE_ABORTED;
|
load_state->progress = RC_CLIENT_LOAD_GAME_STATE_ABORTED;
|
||||||
|
}
|
||||||
rc_mutex_unlock(&client->state.mutex);
|
rc_mutex_unlock(&client->state.mutex);
|
||||||
|
|
||||||
if (load_state->progress != RC_CLIENT_LOAD_GAME_STATE_ABORTED) {
|
if (load_state->progress != RC_CLIENT_LOAD_GAME_STATE_ABORTED) {
|
||||||
@ -1807,6 +1871,9 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
|
|||||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Game %u loaded, hardcore %s%s", load_state->game->public_.id,
|
RC_CLIENT_LOG_INFO_FORMATTED(client, "Game %u loaded, hardcore %s%s", load_state->game->public_.id,
|
||||||
client->state.hardcore ? "enabled" : "disabled",
|
client->state.hardcore ? "enabled" : "disabled",
|
||||||
(client->state.spectator_mode != RC_CLIENT_SPECTATOR_MODE_OFF) ? ", spectating" : "");
|
(client->state.spectator_mode != RC_CLIENT_SPECTATOR_MODE_OFF) ? ", spectating" : "");
|
||||||
|
|
||||||
|
if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO)
|
||||||
|
rc_client_log_active_assets(client);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Subset %u loaded", load_state->subset->public_.id);
|
RC_CLIENT_LOG_INFO_FORMATTED(client, "Subset %u loaded", load_state->subset->public_.id);
|
||||||
@ -2352,6 +2419,7 @@ static int rc_client_attach_load_state(rc_client_t* client, rc_client_load_state
|
|||||||
|
|
||||||
rc_mutex_lock(&client->state.mutex);
|
rc_mutex_lock(&client->state.mutex);
|
||||||
client->state.load = load_state;
|
client->state.load = load_state;
|
||||||
|
client->state.frames_processed = client->state.frames_at_last_ping = 0;
|
||||||
rc_mutex_unlock(&client->state.mutex);
|
rc_mutex_unlock(&client->state.mutex);
|
||||||
}
|
}
|
||||||
else if (client->state.load != load_state) {
|
else if (client->state.load != load_state) {
|
||||||
@ -3483,6 +3551,58 @@ const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc_client_subset_list_t* rc_client_create_subset_list(rc_client_t* client)
|
||||||
|
{
|
||||||
|
rc_client_subset_list_info_t* list;
|
||||||
|
const rc_client_subset_info_t* subset;
|
||||||
|
const rc_client_subset_t** subset_ptr;
|
||||||
|
const uint32_t list_size = RC_ALIGN(sizeof(*list));
|
||||||
|
uint32_t num_subsets = 0;
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return (rc_client_subset_list_t*)calloc(1, list_size);
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||||
|
if (client->state.external_client && client->state.external_client->create_subset_list)
|
||||||
|
return (rc_client_subset_list_t*)client->state.external_client->create_subset_list();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!client->game)
|
||||||
|
return (rc_client_subset_list_t*)calloc(1, list_size);
|
||||||
|
|
||||||
|
rc_mutex_lock(&client->state.mutex);
|
||||||
|
|
||||||
|
subset = client->game->subsets;
|
||||||
|
for (; subset; subset = subset->next) {
|
||||||
|
if (subset->active)
|
||||||
|
num_subsets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
list = (rc_client_subset_list_info_t*)malloc(list_size + num_subsets * sizeof(rc_client_subset_t*));
|
||||||
|
list->public_.subsets = subset_ptr = (const rc_client_subset_t**)((uint8_t*)list + list_size);
|
||||||
|
|
||||||
|
subset = client->game->subsets;
|
||||||
|
for (; subset; subset = subset->next) {
|
||||||
|
if (subset->active)
|
||||||
|
*subset_ptr++ = &subset->public_;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_mutex_unlock(&client->state.mutex);
|
||||||
|
|
||||||
|
list->destroy_func = NULL;
|
||||||
|
list->public_.num_subsets = (uint32_t)(subset_ptr - list->public_.subsets);
|
||||||
|
return &list->public_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rc_client_destroy_subset_list(rc_client_subset_list_t* list)
|
||||||
|
{
|
||||||
|
rc_client_subset_list_info_t* info = (rc_client_subset_list_info_t*)list;
|
||||||
|
if (info->destroy_func)
|
||||||
|
info->destroy_func(info);
|
||||||
|
else
|
||||||
|
free(list);
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== Fetch Game Hashes ===== */
|
/* ===== Fetch Game Hashes ===== */
|
||||||
|
|
||||||
typedef struct rc_client_fetch_hash_library_callback_data_t {
|
typedef struct rc_client_fetch_hash_library_callback_data_t {
|
||||||
@ -3595,6 +3715,158 @@ void rc_client_destroy_hash_library(rc_client_hash_library_t* list)
|
|||||||
free(list);
|
free(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Fetch Game Titles ===== */
|
||||||
|
|
||||||
|
typedef struct rc_client_fetch_game_titles_callback_data_t {
|
||||||
|
rc_client_t* client;
|
||||||
|
rc_client_fetch_game_titles_callback_t callback;
|
||||||
|
void* callback_userdata;
|
||||||
|
rc_client_async_handle_t async_handle;
|
||||||
|
} rc_client_fetch_game_titles_callback_data_t;
|
||||||
|
|
||||||
|
static void rc_client_fetch_game_titles_callback(const rc_api_server_response_t* server_response, void* callback_data)
|
||||||
|
{
|
||||||
|
rc_client_fetch_game_titles_callback_data_t* titles_callback_data =
|
||||||
|
(rc_client_fetch_game_titles_callback_data_t*)callback_data;
|
||||||
|
rc_client_t* client = titles_callback_data->client;
|
||||||
|
rc_api_fetch_game_titles_response_t titles_response;
|
||||||
|
const char* error_message;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = rc_client_end_async(client, &titles_callback_data->async_handle);
|
||||||
|
if (result) {
|
||||||
|
if (result != RC_CLIENT_ASYNC_DESTROYED)
|
||||||
|
RC_CLIENT_LOG_VERBOSE(client, "Fetch game titles aborted");
|
||||||
|
|
||||||
|
free(titles_callback_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = rc_api_process_fetch_game_titles_server_response(&titles_response, server_response);
|
||||||
|
error_message =
|
||||||
|
rc_client_server_error_message(&result, server_response->http_status_code, &titles_response.response);
|
||||||
|
if (error_message) {
|
||||||
|
RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch game titles failed: %s", error_message);
|
||||||
|
titles_callback_data->callback(result, error_message, NULL, client, titles_callback_data->callback_userdata);
|
||||||
|
} else {
|
||||||
|
rc_client_game_title_list_t* list;
|
||||||
|
size_t strings_size = 0;
|
||||||
|
const rc_api_game_title_entry_t* src;
|
||||||
|
const rc_api_game_title_entry_t* stop;
|
||||||
|
size_t list_size;
|
||||||
|
|
||||||
|
/* calculate string buffer size */
|
||||||
|
for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src) {
|
||||||
|
if (src->title)
|
||||||
|
strings_size += strlen(src->title) + 1;
|
||||||
|
if (src->image_url)
|
||||||
|
strings_size += strlen(src->image_url) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_size = sizeof(*list) + sizeof(rc_client_game_title_entry_t) * titles_response.num_entries + strings_size;
|
||||||
|
list = (rc_client_game_title_list_t*)malloc(list_size);
|
||||||
|
if (!list) {
|
||||||
|
titles_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client,
|
||||||
|
titles_callback_data->callback_userdata);
|
||||||
|
} else {
|
||||||
|
rc_client_game_title_entry_t* entry = list->entries =
|
||||||
|
(rc_client_game_title_entry_t*)((uint8_t*)list + sizeof(*list));
|
||||||
|
char* strings = (char*)((uint8_t*)list + sizeof(*list) +
|
||||||
|
sizeof(rc_client_game_title_entry_t) * titles_response.num_entries);
|
||||||
|
|
||||||
|
for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src, ++entry) {
|
||||||
|
entry->game_id = src->id;
|
||||||
|
|
||||||
|
if (src->title) {
|
||||||
|
const size_t len = strlen(src->title) + 1;
|
||||||
|
entry->title = strings;
|
||||||
|
memcpy(strings, src->title, len);
|
||||||
|
strings += len;
|
||||||
|
} else {
|
||||||
|
entry->title = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src->image_name)
|
||||||
|
snprintf(entry->badge_name, sizeof(entry->badge_name), "%s", src->image_name);
|
||||||
|
else
|
||||||
|
entry->badge_name[0] = '\0';
|
||||||
|
|
||||||
|
if (src->image_url) {
|
||||||
|
const size_t len = strlen(src->image_url) + 1;
|
||||||
|
entry->badge_url = strings;
|
||||||
|
memcpy(strings, src->image_url, len);
|
||||||
|
strings += len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entry->badge_url = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list->num_entries = titles_response.num_entries;
|
||||||
|
|
||||||
|
titles_callback_data->callback(RC_OK, NULL, list, client, titles_callback_data->callback_userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_api_destroy_fetch_game_titles_response(&titles_response);
|
||||||
|
free(titles_callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_client_async_handle_t* rc_client_begin_fetch_game_titles(rc_client_t* client, const uint32_t* game_ids,
|
||||||
|
uint32_t num_game_ids,
|
||||||
|
rc_client_fetch_game_titles_callback_t callback,
|
||||||
|
void* callback_userdata)
|
||||||
|
{
|
||||||
|
rc_api_fetch_game_titles_request_t api_params;
|
||||||
|
rc_client_fetch_game_titles_callback_data_t* callback_data;
|
||||||
|
rc_client_async_handle_t* async_handle;
|
||||||
|
rc_api_request_t request;
|
||||||
|
int result;
|
||||||
|
const char* error_message;
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!game_ids || num_game_ids == 0) {
|
||||||
|
callback(RC_INVALID_STATE, "game_ids is required", NULL, client, callback_userdata);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
api_params.game_ids = game_ids;
|
||||||
|
api_params.num_game_ids = num_game_ids;
|
||||||
|
result = rc_api_init_fetch_game_titles_request_hosted(&request, &api_params, &client->state.host);
|
||||||
|
|
||||||
|
if (result != RC_OK) {
|
||||||
|
error_message = rc_error_str(result);
|
||||||
|
callback(result, error_message, NULL, client, callback_userdata);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_data = (rc_client_fetch_game_titles_callback_data_t*)calloc(1, sizeof(*callback_data));
|
||||||
|
if (!callback_data) {
|
||||||
|
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_data->client = client;
|
||||||
|
callback_data->callback = callback;
|
||||||
|
callback_data->callback_userdata = callback_userdata;
|
||||||
|
|
||||||
|
async_handle = &callback_data->async_handle;
|
||||||
|
rc_client_begin_async(client, async_handle);
|
||||||
|
client->callbacks.server_call(&request, rc_client_fetch_game_titles_callback, callback_data, client);
|
||||||
|
rc_api_destroy_request(&request);
|
||||||
|
|
||||||
|
return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rc_client_destroy_game_title_list(rc_client_game_title_list_t* list)
|
||||||
|
{
|
||||||
|
free(list);
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== Achievements ===== */
|
/* ===== Achievements ===== */
|
||||||
|
|
||||||
static void rc_client_update_achievement_display_information(rc_client_t* client, rc_client_achievement_info_t* achievement, time_t recent_unlock_time)
|
static void rc_client_update_achievement_display_information(rc_client_t* client, rc_client_achievement_info_t* achievement, time_t recent_unlock_time)
|
||||||
@ -5160,30 +5432,37 @@ static void rc_client_ping(rc_client_scheduled_callback_data_t* callback_data, r
|
|||||||
char buffer[256];
|
char buffer[256];
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (!client->callbacks.rich_presence_override ||
|
/* if no frames have been processed since the last ping, the emulator is idle. let the
|
||||||
!client->callbacks.rich_presence_override(client, buffer, sizeof(buffer))) {
|
* server session expire. it will be resumed/restarted once frames start getting
|
||||||
rc_mutex_lock(&client->state.mutex);
|
* processed again. */
|
||||||
|
if (client->state.frames_processed != client->state.frames_at_last_ping) {
|
||||||
|
client->state.frames_at_last_ping = client->state.frames_processed;
|
||||||
|
|
||||||
rc_runtime_get_richpresence(&client->game->runtime, buffer, sizeof(buffer),
|
if (!client->callbacks.rich_presence_override ||
|
||||||
client->state.legacy_peek, client, NULL);
|
!client->callbacks.rich_presence_override(client, buffer, sizeof(buffer))) {
|
||||||
|
rc_mutex_lock(&client->state.mutex);
|
||||||
|
|
||||||
rc_mutex_unlock(&client->state.mutex);
|
rc_runtime_get_richpresence(&client->game->runtime, buffer, sizeof(buffer),
|
||||||
}
|
client->state.legacy_peek, client, NULL);
|
||||||
|
|
||||||
memset(&api_params, 0, sizeof(api_params));
|
rc_mutex_unlock(&client->state.mutex);
|
||||||
api_params.username = client->user.username;
|
}
|
||||||
api_params.api_token = client->user.token;
|
|
||||||
api_params.game_id = client->game->public_.id;
|
|
||||||
api_params.rich_presence = buffer;
|
|
||||||
api_params.game_hash = client->game->public_.hash;
|
|
||||||
api_params.hardcore = client->state.hardcore;
|
|
||||||
|
|
||||||
result = rc_api_init_ping_request_hosted(&request, &api_params, &client->state.host);
|
memset(&api_params, 0, sizeof(api_params));
|
||||||
if (result != RC_OK) {
|
api_params.username = client->user.username;
|
||||||
RC_CLIENT_LOG_WARN_FORMATTED(client, "Error generating ping request: %s", rc_error_str(result));
|
api_params.api_token = client->user.token;
|
||||||
}
|
api_params.game_id = client->game->public_.id;
|
||||||
else {
|
api_params.rich_presence = buffer;
|
||||||
client->callbacks.server_call(&request, rc_client_ping_callback, client, client);
|
api_params.game_hash = client->game->public_.hash;
|
||||||
|
api_params.hardcore = client->state.hardcore;
|
||||||
|
|
||||||
|
result = rc_api_init_ping_request_hosted(&request, &api_params, &client->state.host);
|
||||||
|
if (result != RC_OK) {
|
||||||
|
RC_CLIENT_LOG_WARN_FORMATTED(client, "Error generating ping request: %s", rc_error_str(result));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
client->callbacks.server_call(&request, rc_client_ping_callback, client, client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback_data->when = now + 120 * 1000;
|
callback_data->when = now + 120 * 1000;
|
||||||
@ -5885,6 +6164,8 @@ void rc_client_do_frame(rc_client_t* client)
|
|||||||
rc_mutex_unlock(&client->state.mutex);
|
rc_mutex_unlock(&client->state.mutex);
|
||||||
|
|
||||||
rc_client_raise_pending_events(client, client->game);
|
rc_client_raise_pending_events(client, client->game);
|
||||||
|
|
||||||
|
++client->state.frames_processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we've processed a frame. if there's a pause delay in effect, process it */
|
/* we've processed a frame. if there's a pause delay in effect, process it */
|
||||||
|
|||||||
36
deps/rcheevos/src/rc_client_external.c
vendored
36
deps/rcheevos/src/rc_client_external.c
vendored
@ -9,13 +9,15 @@
|
|||||||
|
|
||||||
/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */
|
/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */
|
||||||
#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64
|
#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64
|
||||||
|
/* https://media.retroachievements.org/UserPic/TwentyCharUserNameXX.png is 69 with null terminator */
|
||||||
|
#define RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE 80
|
||||||
|
|
||||||
typedef struct rc_client_external_conversions_t {
|
typedef struct rc_client_external_conversions_t {
|
||||||
rc_client_user_t user;
|
rc_client_user_t user;
|
||||||
rc_client_game_t game;
|
rc_client_game_t game;
|
||||||
rc_client_subset_t subsets[4];
|
rc_client_subset_t subsets[4];
|
||||||
rc_client_achievement_t achievements[16];
|
rc_client_achievement_t achievements[16];
|
||||||
char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
char user_avatar_url[RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE];
|
||||||
char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||||
char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||||
char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||||
@ -24,7 +26,7 @@ typedef struct rc_client_external_conversions_t {
|
|||||||
uint32_t next_achievement_index;
|
uint32_t next_achievement_index;
|
||||||
} rc_client_external_conversions_t;
|
} rc_client_external_conversions_t;
|
||||||
|
|
||||||
static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name)
|
static const char* rc_client_external_build_avatar_url(char buffer[], size_t buffer_size, uint32_t image_type, const char* image_name)
|
||||||
{
|
{
|
||||||
rc_api_fetch_image_request_t image_request;
|
rc_api_fetch_image_request_t image_request;
|
||||||
rc_api_request_t request;
|
rc_api_request_t request;
|
||||||
@ -38,7 +40,7 @@ static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t i
|
|||||||
if (result != RC_OK)
|
if (result != RC_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url);
|
snprintf(buffer, buffer_size, "%s", request.url);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +71,9 @@ const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* cl
|
|||||||
RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);
|
RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);
|
||||||
|
|
||||||
converted->avatar_url = rc_client_external_build_avatar_url(
|
converted->avatar_url = rc_client_external_build_avatar_url(
|
||||||
client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username);
|
client->state.external_client_conversions->user_avatar_url,
|
||||||
|
sizeof(client->state.external_client_conversions->user_avatar_url),
|
||||||
|
RC_IMAGE_TYPE_USER, v1_user->username);
|
||||||
|
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
@ -88,7 +92,9 @@ const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* cl
|
|||||||
RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);
|
RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);
|
||||||
|
|
||||||
converted->badge_url = rc_client_external_build_avatar_url(
|
converted->badge_url = rc_client_external_build_avatar_url(
|
||||||
client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
client->state.external_client_conversions->game_badge_url,
|
||||||
|
sizeof(client->state.external_client_conversions->game_badge_url),
|
||||||
|
RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
||||||
|
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
@ -123,7 +129,9 @@ const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t
|
|||||||
memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
|
memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
|
||||||
RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);
|
RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);
|
||||||
|
|
||||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
converted->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||||
|
sizeof(client->state.external_client_conversions->subset_badge_url[0]),
|
||||||
|
RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
||||||
|
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
@ -161,8 +169,12 @@ const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const r
|
|||||||
memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
|
memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
|
||||||
RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);
|
RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);
|
||||||
|
|
||||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
converted->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||||
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
sizeof(client->state.external_client_conversions->achievement_badge_url[0]),
|
||||||
|
RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
||||||
|
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url,
|
||||||
|
sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),
|
||||||
|
RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
||||||
|
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
@ -246,9 +258,13 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con
|
|||||||
*achievement = &new_list->achievements[num_achievements++];
|
*achievement = &new_list->achievements[num_achievements++];
|
||||||
memcpy(*achievement, *src_achievement, sizeof(**src_achievement));
|
memcpy(*achievement, *src_achievement, sizeof(**src_achievement));
|
||||||
|
|
||||||
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||||
|
sizeof(client->state.external_client_conversions->achievement_badge_url[0]),
|
||||||
|
RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
||||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||||
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url,
|
||||||
|
sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),
|
||||||
|
RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
||||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
deps/rcheevos/src/rc_client_external.h
vendored
8
deps/rcheevos/src/rc_client_external.h
vendored
@ -61,6 +61,11 @@ typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_lead
|
|||||||
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t)(rc_client_t* client,
|
typedef rc_client_async_handle_t* (RC_CCONV *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);
|
uint32_t leaderboard_id, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
|
||||||
|
|
||||||
|
/* NOTE: rc_client_external_create_subset_list_func_t returns an internal wrapper structure which contains the public list
|
||||||
|
* and a destructor function. */
|
||||||
|
struct rc_client_subset_list_info_t;
|
||||||
|
typedef struct rc_client_subset_list_info_t* (RC_CCONV* rc_client_external_create_subset_list_func_t)();
|
||||||
|
|
||||||
|
|
||||||
typedef size_t (RC_CCONV *rc_client_external_progress_size_func_t)(void);
|
typedef size_t (RC_CCONV *rc_client_external_progress_size_func_t)(void);
|
||||||
typedef int (RC_CCONV *rc_client_external_serialize_progress_func_t)(uint8_t* buffer, size_t buffer_size);
|
typedef int (RC_CCONV *rc_client_external_serialize_progress_func_t)(uint8_t* buffer, size_t buffer_size);
|
||||||
@ -144,6 +149,9 @@ typedef struct rc_client_external_t
|
|||||||
rc_client_external_get_user_game_summary_func_t get_user_game_summary_v5;
|
rc_client_external_get_user_game_summary_func_t get_user_game_summary_v5;
|
||||||
rc_client_external_get_user_subset_summary_func_t get_user_subset_summary;
|
rc_client_external_get_user_subset_summary_func_t get_user_subset_summary;
|
||||||
|
|
||||||
|
/* VERSION 6 */
|
||||||
|
rc_client_external_create_subset_list_func_t create_subset_list;
|
||||||
|
|
||||||
} rc_client_external_t;
|
} rc_client_external_t;
|
||||||
|
|
||||||
#define RC_CLIENT_EXTERNAL_VERSION 5
|
#define RC_CLIENT_EXTERNAL_VERSION 5
|
||||||
|
|||||||
10
deps/rcheevos/src/rc_client_internal.h
vendored
10
deps/rcheevos/src/rc_client_internal.h
vendored
@ -222,6 +222,14 @@ typedef struct rc_client_subset_info_t {
|
|||||||
uint8_t pending_events;
|
uint8_t pending_events;
|
||||||
} rc_client_subset_info_t;
|
} rc_client_subset_info_t;
|
||||||
|
|
||||||
|
struct rc_client_subset_list_info_t;
|
||||||
|
typedef void (RC_CCONV* rc_client_destroy_subset_list_func_t)(struct rc_client_subset_list_info_t* list);
|
||||||
|
|
||||||
|
typedef struct rc_client_subset_list_info_t {
|
||||||
|
rc_client_subset_list_t public_;
|
||||||
|
rc_client_destroy_subset_list_func_t destroy_func;
|
||||||
|
} rc_client_subset_list_info_t;
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
| Game |
|
| Game |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
@ -316,6 +324,8 @@ typedef struct rc_client_state_t {
|
|||||||
rc_client_raintegration_t* raintegration;
|
rc_client_raintegration_t* raintegration;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
uint32_t frames_processed;
|
||||||
|
uint32_t frames_at_last_ping;
|
||||||
uint16_t unpaused_frame_decay;
|
uint16_t unpaused_frame_decay;
|
||||||
uint16_t required_unpaused_frames;
|
uint16_t required_unpaused_frames;
|
||||||
|
|
||||||
|
|||||||
19
deps/rcheevos/src/rc_libretro.c
vendored
19
deps/rcheevos/src/rc_libretro.c
vendored
@ -649,6 +649,7 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
|||||||
uint32_t i, j;
|
uint32_t i, j;
|
||||||
rc_libretro_core_memory_info_t info;
|
rc_libretro_core_memory_info_t info;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
|
int found_aligning_padding = 0;
|
||||||
|
|
||||||
for (i = 0; i < console_regions->num_regions; ++i) {
|
for (i = 0; i < console_regions->num_regions; ++i) {
|
||||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||||
@ -656,6 +657,16 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
|||||||
const uint32_t type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
|
const uint32_t type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
|
||||||
uint32_t base_address = 0;
|
uint32_t base_address = 0;
|
||||||
|
|
||||||
|
if (console_region->type == RC_MEMORY_TYPE_UNUSED && console_region_size >= 0x10000 && !found_aligning_padding) {
|
||||||
|
if (console_regions->region[console_regions->num_regions - 1].end_address > 0x01000000) {
|
||||||
|
/* assume anything exposing more than 16MB of regions with at least one 64KB+ UNUSED region
|
||||||
|
* is padding so things align with real addresses. this indicates the memory is disjoint
|
||||||
|
* in the system, so we cannot expect it to be contiguous in the RETRO_SYSTEM_RAM.
|
||||||
|
* stop processing regions now, and just fill the remaining memory map with null filler. */
|
||||||
|
found_aligning_padding = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j <= i; ++j) {
|
for (j = 0; j <= i; ++j) {
|
||||||
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
||||||
if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
|
if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
|
||||||
@ -665,7 +676,13 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
|||||||
}
|
}
|
||||||
offset = console_region->start_address - base_address;
|
offset = console_region->start_address - base_address;
|
||||||
|
|
||||||
get_core_memory_info(type, &info);
|
if (!found_aligning_padding) {
|
||||||
|
get_core_memory_info(type, &info);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
info.data = NULL;
|
||||||
|
info.size = console_region_size;
|
||||||
|
}
|
||||||
|
|
||||||
if (offset < info.size) {
|
if (offset < info.size) {
|
||||||
info.size -= offset;
|
info.size -= offset;
|
||||||
|
|||||||
2
deps/rcheevos/src/rc_version.h
vendored
2
deps/rcheevos/src/rc_version.h
vendored
@ -8,7 +8,7 @@
|
|||||||
RC_BEGIN_C_DECLS
|
RC_BEGIN_C_DECLS
|
||||||
|
|
||||||
#define RCHEEVOS_VERSION_MAJOR 12
|
#define RCHEEVOS_VERSION_MAJOR 12
|
||||||
#define RCHEEVOS_VERSION_MINOR 1
|
#define RCHEEVOS_VERSION_MINOR 2
|
||||||
#define RCHEEVOS_VERSION_PATCH 0
|
#define RCHEEVOS_VERSION_PATCH 0
|
||||||
|
|
||||||
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
||||||
|
|||||||
21
deps/rcheevos/src/rcheevos/condition.c
vendored
21
deps/rcheevos/src/rcheevos/condition.c
vendored
@ -374,7 +374,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
|||||||
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
||||||
}
|
}
|
||||||
|
|
||||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
|
||||||
parse->indirect_parent.type = RC_OPERAND_NONE;
|
parse->indirect_parent.type = RC_OPERAND_NONE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -388,13 +388,13 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
|||||||
/* type determined by parent */
|
/* type determined by parent */
|
||||||
const uint8_t new_size = rc_operand_is_float(&parse->addsource_parent) ? RC_MEMSIZE_FLOAT : RC_MEMSIZE_32_BITS;
|
const uint8_t new_size = rc_operand_is_float(&parse->addsource_parent) ? RC_MEMSIZE_FLOAT : RC_MEMSIZE_32_BITS;
|
||||||
|
|
||||||
if (parse->addsource_oper == RC_OPERATOR_ADD && !rc_operand_is_memref(&parse->addsource_parent)) {
|
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR && !rc_operand_is_memref(&parse->addsource_parent)) {
|
||||||
/* if the previous element was a constant we have to turn it into a memref by adding zero */
|
/* if the previous element was a constant we have to turn it into a memref by adding zero */
|
||||||
rc_modified_memref_t* memref;
|
rc_modified_memref_t* memref;
|
||||||
rc_operand_t zero;
|
rc_operand_t zero;
|
||||||
rc_operand_set_const(&zero, 0);
|
rc_operand_set_const(&zero, 0);
|
||||||
memref = rc_alloc_modified_memref(parse,
|
memref = rc_alloc_modified_memref(parse,
|
||||||
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD, &zero);
|
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD_ACCUMULATOR, &zero);
|
||||||
parse->addsource_parent.value.memref = (rc_memref_t*)memref;
|
parse->addsource_parent.value.memref = (rc_memref_t*)memref;
|
||||||
parse->addsource_parent.type = RC_OPERAND_ADDRESS;
|
parse->addsource_parent.type = RC_OPERAND_ADDRESS;
|
||||||
}
|
}
|
||||||
@ -414,19 +414,27 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* subtract the condition from the chain */
|
/* subtract the condition from the chain */
|
||||||
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB : RC_OPERATOR_SUB_PARENT;
|
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB_ACCUMULATOR : RC_OPERATOR_SUB_PARENT;
|
||||||
rc_condition_convert_to_operand(condition, &cond_operand, parse);
|
rc_condition_convert_to_operand(condition, &cond_operand, parse);
|
||||||
rc_operand_addsource(&cond_operand, parse, new_size);
|
rc_operand_addsource(&cond_operand, parse, new_size);
|
||||||
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
||||||
|
|
||||||
/* indicate the next value can be added to the chain */
|
/* indicate the next value can be added to the chain */
|
||||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse->indirect_parent.type = RC_OPERAND_NONE;
|
parse->indirect_parent.type = RC_OPERAND_NONE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_CONDITION_REMEMBER:
|
case RC_CONDITION_REMEMBER:
|
||||||
|
if (condition->operand1.type == RC_OPERAND_RECALL &&
|
||||||
|
condition->oper == RC_OPERATOR_NONE &&
|
||||||
|
parse->addsource_parent.type == RC_OPERAND_NONE &&
|
||||||
|
parse->indirect_parent.type == RC_OPERAND_NONE) {
|
||||||
|
/* Remembering {recall} without any modifications is a no-op */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
rc_condition_convert_to_operand(condition, &condition->operand1, parse);
|
rc_condition_convert_to_operand(condition, &condition->operand1, parse);
|
||||||
|
|
||||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||||
@ -465,6 +473,9 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
|||||||
default:
|
default:
|
||||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||||
/* type determined by leaf */
|
/* type determined by leaf */
|
||||||
|
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR)
|
||||||
|
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||||
|
|
||||||
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
|
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
|
||||||
condition->operand1.is_combining = 1;
|
condition->operand1.is_combining = 1;
|
||||||
|
|
||||||
|
|||||||
8
deps/rcheevos/src/rcheevos/condset.c
vendored
8
deps/rcheevos/src/rcheevos/condset.c
vendored
@ -62,8 +62,10 @@ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, c
|
|||||||
do {
|
do {
|
||||||
rc_parse_condition_internal(&condition, &memaddr, &parse);
|
rc_parse_condition_internal(&condition, &memaddr, &parse);
|
||||||
|
|
||||||
if (parse.offset < 0)
|
if (parse.offset < 0) {
|
||||||
|
rc_destroy_parse_state(&parse);
|
||||||
return parse.offset;
|
return parse.offset;
|
||||||
|
}
|
||||||
|
|
||||||
++index;
|
++index;
|
||||||
|
|
||||||
@ -106,7 +108,9 @@ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, c
|
|||||||
* logic in rc_find_next_classification */
|
* logic in rc_find_next_classification */
|
||||||
self->num_other_conditions += chain_length - 1;
|
self->num_other_conditions += chain_length - 1;
|
||||||
|
|
||||||
return index;
|
rc_destroy_parse_state(&parse);
|
||||||
|
|
||||||
|
return (int32_t)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_find_next_classification(const char* memaddr) {
|
static int rc_find_next_classification(const char* memaddr) {
|
||||||
|
|||||||
29
deps/rcheevos/src/rcheevos/memref.c
vendored
29
deps/rcheevos/src/rcheevos/memref.c
vendored
@ -154,8 +154,14 @@ rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t
|
|||||||
memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent));
|
memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent));
|
||||||
memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier));
|
memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier));
|
||||||
modified_memref->modifier_type = modifier_type;
|
modified_memref->modifier_type = modifier_type;
|
||||||
|
modified_memref->depth = 0;
|
||||||
modified_memref->memref.address = rc_operand_is_memref(modifier) ? modifier->value.memref->address : modifier->value.num;
|
modified_memref->memref.address = rc_operand_is_memref(modifier) ? modifier->value.memref->address : modifier->value.num;
|
||||||
|
|
||||||
|
if (rc_operand_is_memref(parent) && parent->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
|
||||||
|
const rc_modified_memref_t* parent_modified_memref = (rc_modified_memref_t*)parent->value.memref;
|
||||||
|
modified_memref->depth = parent_modified_memref->depth + 1;
|
||||||
|
}
|
||||||
|
|
||||||
return modified_memref;
|
return modified_memref;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,11 +735,34 @@ uint32_t rc_get_modified_memref_value(const rc_modified_memref_t* memref, rc_pee
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_OPERATOR_SUB_PARENT:
|
case RC_OPERATOR_SUB_PARENT:
|
||||||
|
/* sub parent is "-parent + modifier" */
|
||||||
rc_typed_value_negate(&value);
|
rc_typed_value_negate(&value);
|
||||||
rc_typed_value_add(&value, &modifier);
|
rc_typed_value_add(&value, &modifier);
|
||||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RC_OPERATOR_SUB_ACCUMULATOR:
|
||||||
|
rc_typed_value_negate(&modifier);
|
||||||
|
/* fallthrough */ /* to case RC_OPERATOR_SUB_ACCUMULATOR */
|
||||||
|
|
||||||
|
case RC_OPERATOR_ADD_ACCUMULATOR:
|
||||||
|
/* when modifying the accumulator, force the modifier to match the accumulator
|
||||||
|
* type instead of promoting them both to the less restrictive type.
|
||||||
|
*
|
||||||
|
* 18 - 17.5 will result in an integer. should it be 0 or 1?
|
||||||
|
*
|
||||||
|
* default: float is less restrictive, convert both to float for combine,
|
||||||
|
* then convert to the memref type.
|
||||||
|
* (int)((float)18 - 17.5) -> (int)(0.5) -> 0
|
||||||
|
*
|
||||||
|
* accumulator is integer: force modifier to be integer before combining
|
||||||
|
* (int)(18 - (int)17.5) -> (int)(18 - 17) -> 1
|
||||||
|
*/
|
||||||
|
rc_typed_value_convert(&modifier, value.type);
|
||||||
|
rc_typed_value_add(&value, &modifier);
|
||||||
|
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
rc_typed_value_combine(&value, &modifier, memref->modifier_type);
|
rc_typed_value_combine(&value, &modifier, memref->modifier_type);
|
||||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||||
|
|||||||
5
deps/rcheevos/src/rcheevos/operand.c
vendored
5
deps/rcheevos/src/rcheevos/operand.c
vendored
@ -334,8 +334,11 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) {
|
|||||||
const rc_modified_memref_t* left_memref = (const rc_modified_memref_t*)left->value.memref;
|
const rc_modified_memref_t* left_memref = (const rc_modified_memref_t*)left->value.memref;
|
||||||
const rc_modified_memref_t* right_memref = (const rc_modified_memref_t*)right->value.memref;
|
const rc_modified_memref_t* right_memref = (const rc_modified_memref_t*)right->value.memref;
|
||||||
return (left_memref->modifier_type == right_memref->modifier_type &&
|
return (left_memref->modifier_type == right_memref->modifier_type &&
|
||||||
|
left_memref->depth == right_memref->depth &&
|
||||||
|
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier) &&
|
||||||
rc_operands_are_equal(&left_memref->parent, &right_memref->parent) &&
|
rc_operands_are_equal(&left_memref->parent, &right_memref->parent) &&
|
||||||
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier));
|
1 == 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
4
deps/rcheevos/src/rcheevos/rc_internal.h
vendored
4
deps/rcheevos/src/rcheevos/rc_internal.h
vendored
@ -14,10 +14,11 @@ typedef struct rc_scratch_string {
|
|||||||
rc_scratch_string_t;
|
rc_scratch_string_t;
|
||||||
|
|
||||||
typedef struct rc_modified_memref_t {
|
typedef struct rc_modified_memref_t {
|
||||||
rc_memref_t memref; /* for compatibility with rc_operand_t.value.memref */
|
rc_memref_t memref; /* For compatibility with rc_operand_t.value.memref */
|
||||||
rc_operand_t parent; /* The parent memref this memref is derived from (type will always be a memref type) */
|
rc_operand_t parent; /* The parent memref this memref is derived from (type will always be a memref type) */
|
||||||
rc_operand_t modifier; /* The modifier to apply to the parent. */
|
rc_operand_t modifier; /* The modifier to apply to the parent. */
|
||||||
uint8_t modifier_type; /* How to apply the modifier to the parent. (RC_OPERATOR_*) */
|
uint8_t modifier_type; /* How to apply the modifier to the parent. (RC_OPERATOR_*) */
|
||||||
|
uint16_t depth; /* The number of parents this memref has. */
|
||||||
}
|
}
|
||||||
rc_modified_memref_t;
|
rc_modified_memref_t;
|
||||||
|
|
||||||
@ -382,7 +383,6 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self);
|
|||||||
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
|
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
|
||||||
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
|
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
|
||||||
|
|
||||||
int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address);
|
|
||||||
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);
|
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);
|
||||||
|
|
||||||
RC_END_C_DECLS
|
RC_END_C_DECLS
|
||||||
|
|||||||
10
deps/rcheevos/src/rcheevos/trigger.c
vendored
10
deps/rcheevos/src/rcheevos/trigger.c
vendored
@ -235,8 +235,14 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, void* unus
|
|||||||
is_paused |= sub_paused;
|
is_paused |= sub_paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if paused, the measured value may not be captured, keep the old value */
|
if (is_paused) {
|
||||||
if (!is_paused) {
|
/* if the trigger is fully paused, ignore any updates to the measured value */
|
||||||
|
}
|
||||||
|
else if (measured_value.type == RC_VALUE_TYPE_NONE) {
|
||||||
|
/* if a measured value was not captured, keep the old value (it's possible to pause
|
||||||
|
* an alt that is generating the measured value without fully pausing the trigger) */
|
||||||
|
}
|
||||||
|
else {
|
||||||
rc_typed_value_convert(&measured_value, RC_VALUE_TYPE_UNSIGNED);
|
rc_typed_value_convert(&measured_value, RC_VALUE_TYPE_UNSIGNED);
|
||||||
self->measured_value = measured_value.value.u32;
|
self->measured_value = measured_value.value.u32;
|
||||||
}
|
}
|
||||||
|
|||||||
36
deps/rcheevos/src/rcheevos/value.c
vendored
36
deps/rcheevos/src/rcheevos/value.c
vendored
@ -67,6 +67,7 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
|||||||
next_clause = &self->conditions;
|
next_clause = &self->conditions;
|
||||||
do {
|
do {
|
||||||
/* count the number of joiners and add one to determine the number of clauses. */
|
/* count the number of joiners and add one to determine the number of clauses. */
|
||||||
|
buffer[0] = 'A'; /* reset to AddSource */
|
||||||
done = 0;
|
done = 0;
|
||||||
num_measured_conditions = 1;
|
num_measured_conditions = 1;
|
||||||
buffer_ptr = *memaddr;
|
buffer_ptr = *memaddr;
|
||||||
@ -97,8 +98,8 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
|||||||
}
|
}
|
||||||
} while (!done);
|
} while (!done);
|
||||||
|
|
||||||
/* if last condition is SubSource, we'll need to add a dummy condition for the Measured */
|
/* if last condition is not AddSource, we'll need to add a dummy condition for the Measured */
|
||||||
if (buffer[0] == 'B')
|
if (buffer[0] != 'A')
|
||||||
++num_measured_conditions;
|
++num_measured_conditions;
|
||||||
|
|
||||||
condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
|
condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
|
||||||
@ -121,10 +122,18 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
|||||||
for (;; ++(*memaddr)) {
|
for (;; ++(*memaddr)) {
|
||||||
switch (**memaddr) {
|
switch (**memaddr) {
|
||||||
case '_': /* add next */
|
case '_': /* add next */
|
||||||
|
*ptr = '\0';
|
||||||
|
break;
|
||||||
|
|
||||||
case '$': /* maximum of */
|
case '$': /* maximum of */
|
||||||
case '\0': /* end of string */
|
case '\0': /* end of string */
|
||||||
case ':': /* end of leaderboard clause */
|
case ':': /* end of leaderboard clause */
|
||||||
case ')': /* end of rich presence macro */
|
case ')': /* end of rich presence macro */
|
||||||
|
/* the last condition needs to be Measured - AddSource can be changed here,
|
||||||
|
* SubSource will be handled later */
|
||||||
|
if (buffer[0] == 'A')
|
||||||
|
buffer[0] = 'M';
|
||||||
|
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -176,33 +185,34 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc_condition_update_parse_state(cond, parse);
|
||||||
|
|
||||||
*next = cond;
|
*next = cond;
|
||||||
next = &cond->next;
|
next = &cond->next;
|
||||||
|
|
||||||
if (**memaddr != '_') /* add next */
|
if (**memaddr != '_') /* add next */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
rc_condition_update_parse_state(cond, parse);
|
|
||||||
++cond;
|
++cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end of clause */
|
/* -- end of clause -- */
|
||||||
if (cond->type == RC_CONDITION_SUB_SOURCE) {
|
|
||||||
/* cannot change SubSource to Measured. add a dummy condition */
|
/* clause must end in a Measured. if it doesn't, append one */
|
||||||
rc_condition_update_parse_state(cond, parse);
|
if (cond->type != RC_CONDITION_MEASURED) {
|
||||||
if (parse->buffer)
|
if (!parse->buffer)
|
||||||
|
cond = &local_cond;
|
||||||
|
else
|
||||||
++cond;
|
++cond;
|
||||||
|
|
||||||
buffer_ptr = "A:0";
|
buffer_ptr = "M:0";
|
||||||
rc_parse_condition_internal(cond, &buffer_ptr, parse);
|
rc_parse_condition_internal(cond, &buffer_ptr, parse);
|
||||||
*next = cond;
|
*next = cond;
|
||||||
next = &cond->next;
|
next = &cond->next;
|
||||||
|
rc_condition_update_parse_state(cond, parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert final AddSource condition to Measured */
|
*next = NULL;
|
||||||
cond->type = RC_CONDITION_MEASURED;
|
|
||||||
cond->next = NULL;
|
|
||||||
rc_condition_update_parse_state(cond, parse);
|
|
||||||
|
|
||||||
/* finalize clause */
|
/* finalize clause */
|
||||||
*next_clause = condset;
|
*next_clause = condset;
|
||||||
|
|||||||
22
deps/rcheevos/src/rhash/hash.c
vendored
22
deps/rcheevos/src/rhash/hash.c
vendored
@ -1300,7 +1300,18 @@ static void rc_hash_initialize_iterator_from_path(rc_hash_iterator_t* iterator,
|
|||||||
bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
|
bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler->handler(iterator, handler->data);
|
handler->handler(iterator, handler->data);
|
||||||
} else {
|
|
||||||
|
if (iterator->callbacks.verbose_message) {
|
||||||
|
int count = 0;
|
||||||
|
while (iterator->consoles[count])
|
||||||
|
++count;
|
||||||
|
|
||||||
|
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rc_hash_iterator_error_formatted(iterator, "No console mapping specified for %s file extension - trying full file hash", ext);
|
||||||
|
|
||||||
/* if we didn't match the extension, default to something that does a whole file hash */
|
/* if we didn't match the extension, default to something that does a whole file hash */
|
||||||
if (!iterator->consoles[0])
|
if (!iterator->consoles[0])
|
||||||
iterator->consoles[0] = RC_CONSOLE_GAMEBOY;
|
iterator->consoles[0] = RC_CONSOLE_GAMEBOY;
|
||||||
@ -1332,15 +1343,6 @@ int rc_hash_iterate(char hash[33], rc_hash_iterator_t* iterator) {
|
|||||||
|
|
||||||
if (iterator->index == -1) {
|
if (iterator->index == -1) {
|
||||||
rc_hash_initialize_iterator_from_path(iterator, iterator->path);
|
rc_hash_initialize_iterator_from_path(iterator, iterator->path);
|
||||||
|
|
||||||
if (iterator->callbacks.verbose_message) {
|
|
||||||
int count = 0;
|
|
||||||
while (iterator->consoles[count])
|
|
||||||
++count;
|
|
||||||
|
|
||||||
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, rc_path_get_extension(iterator->path));
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator->index = 0;
|
iterator->index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user