From 750dd1cb87eb0623f2cee66777408cc8ec08d5ba Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 26 Dec 2024 15:00:24 +1000 Subject: [PATCH] dep/rcheevos: Bump to 3a91a58 --- dep/rcheevos/include/rc_consoles.h | 1 + dep/rcheevos/include/rc_runtime.h | 10 +- dep/rcheevos/include/rc_runtime_types.h | 93 +- dep/rcheevos/rcheevos.vcxproj | 4 + dep/rcheevos/rcheevos.vcxproj.filters | 6 + dep/rcheevos/src/rc_client.c | 225 ++-- dep/rcheevos/src/rc_client_raintegration.c | 21 + dep/rcheevos/src/rc_client_types.natvis | 394 +++++++ dep/rcheevos/src/rc_compat.c | 119 ++- dep/rcheevos/src/rc_compat.h | 30 +- dep/rcheevos/src/rc_libretro.c | 89 +- dep/rcheevos/src/rc_util.c | 7 + dep/rcheevos/src/rcheevos/alloc.c | 227 +++- dep/rcheevos/src/rcheevos/condition.c | 373 ++++--- dep/rcheevos/src/rcheevos/condset.c | 967 +++++++++++------- dep/rcheevos/src/rcheevos/consoleinfo.c | 66 +- dep/rcheevos/src/rcheevos/lboard.c | 52 +- dep/rcheevos/src/rcheevos/memref.c | 327 +++++- dep/rcheevos/src/rcheevos/operand.c | 230 ++++- dep/rcheevos/src/rcheevos/rc_internal.h | 241 ++++- .../src/rcheevos/rc_runtime_types.natvis | 539 ++++++++++ dep/rcheevos/src/rcheevos/rc_validate.c | 168 ++- dep/rcheevos/src/rcheevos/rc_validate.h | 4 - dep/rcheevos/src/rcheevos/richpresence.c | 176 +++- dep/rcheevos/src/rcheevos/runtime.c | 313 +++--- dep/rcheevos/src/rcheevos/runtime_progress.c | 198 +++- dep/rcheevos/src/rcheevos/trigger.c | 112 +- dep/rcheevos/src/rcheevos/value.c | 535 ++++++---- dep/rcheevos/src/rhash/hash.c | 16 +- 29 files changed, 4103 insertions(+), 1440 deletions(-) create mode 100644 dep/rcheevos/src/rc_client_types.natvis create mode 100644 dep/rcheevos/src/rcheevos/rc_runtime_types.natvis diff --git a/dep/rcheevos/include/rc_consoles.h b/dep/rcheevos/include/rc_consoles.h index 269879202..5f788855b 100644 --- a/dep/rcheevos/include/rc_consoles.h +++ b/dep/rcheevos/include/rc_consoles.h @@ -93,6 +93,7 @@ enum { RC_CONSOLE_NINTENDO_DSI = 78, RC_CONSOLE_TI83 = 79, RC_CONSOLE_UZEBOX = 80, + RC_CONSOLE_FAMICOM_DISK_SYSTEM = 81, RC_CONSOLE_HUBS = 100, RC_CONSOLE_EVENTS = 101, diff --git a/dep/rcheevos/include/rc_runtime.h b/dep/rcheevos/include/rc_runtime.h index 8186c010b..a049426e1 100644 --- a/dep/rcheevos/include/rc_runtime.h +++ b/dep/rcheevos/include/rc_runtime.h @@ -46,7 +46,6 @@ typedef struct rc_runtime_trigger_t { rc_memref_t* invalid_memref; uint8_t md5[16]; int32_t serialized_size; - uint8_t owns_memrefs; } rc_runtime_trigger_t; @@ -58,16 +57,13 @@ typedef struct rc_runtime_lboard_t { rc_memref_t* invalid_memref; uint8_t md5[16]; uint32_t serialized_size; - uint8_t owns_memrefs; } rc_runtime_lboard_t; typedef struct rc_runtime_richpresence_t { rc_richpresence_t* richpresence; void* buffer; - struct rc_runtime_richpresence_t* previous; uint8_t md5[16]; - uint8_t owns_memrefs; } rc_runtime_richpresence_t; @@ -82,11 +78,7 @@ typedef struct rc_runtime_t { rc_runtime_richpresence_t* richpresence; - rc_memref_t* memrefs; - rc_memref_t** next_memref; - - rc_value_t* variables; - rc_value_t** next_variable; + struct rc_memrefs_t* memrefs; uint8_t owns_self; } diff --git a/dep/rcheevos/include/rc_runtime_types.h b/dep/rcheevos/include/rc_runtime_types.h index 77d2390f4..cc4e725c7 100644 --- a/dep/rcheevos/include/rc_runtime_types.h +++ b/dep/rcheevos/include/rc_runtime_types.h @@ -29,7 +29,7 @@ typedef struct rc_value_t rc_value_t; * num_bytes is greater than 1, the value is read in little-endian from * memory. */ -typedef uint32_t(RC_CCONV *rc_peek_t)(uint32_t address, uint32_t num_bytes, void* ud); +typedef uint32_t(RC_CCONV* rc_peek_t)(uint32_t address, uint32_t num_bytes, void* ud); /*****************************************************************************\ | Memory References | @@ -70,15 +70,14 @@ typedef struct rc_memref_value_t { /* The last differing value of this memory reference. */ uint32_t prior; - /* The size of the value. */ + /* The size of the value. (RC_MEMSIZE_*) */ uint8_t size; /* True if the value changed this frame. */ uint8_t changed; - /* The value type of the value (for variables) */ + /* The value type of the value. (RC_VALUE_TYPE_*) */ uint8_t type; - /* True if the reference will be used in indirection. - * NOTE: This is actually a property of the rc_memref_t, but we put it here to save space */ - uint8_t is_indirect; + /* The type of memref (RC_MEMREF_TYPE_*) */ + uint8_t memref_type; } rc_memref_value_t; @@ -88,9 +87,6 @@ struct rc_memref_t { /* The memory address of this variable. */ uint32_t address; - - /* The next memory reference in the chain. */ - rc_memref_t* next; }; /*****************************************************************************\ @@ -125,11 +121,14 @@ typedef struct rc_operand_t { int luafunc; } value; - /* specifies which member of the value union is being used */ + /* specifies which member of the value union is being used (RC_OPERAND_*) */ uint8_t type; - /* the actual RC_MEMSIZE of the operand - memref.size may differ */ + /* the RC_MEMSIZE of the operand specified in the condition definition - memref.size may differ */ uint8_t size; + + /* specifies how to read the memref for some types (RC_OPERAND_*) */ + uint8_t memref_access_type; } rc_operand_t; @@ -141,23 +140,16 @@ RC_EXPORT int RC_CCONV rc_operand_is_memref(const rc_operand_t* operand); /* types */ enum { - /* NOTE: this enum is ordered to optimize the switch statements in rc_test_condset_internal. the values may change between releases */ - - /* non-combining conditions (third switch) */ RC_CONDITION_STANDARD, /* this should always be 0 */ RC_CONDITION_PAUSE_IF, RC_CONDITION_RESET_IF, RC_CONDITION_MEASURED_IF, RC_CONDITION_TRIGGER, - RC_CONDITION_MEASURED, /* measured also appears in the first switch, so place it at the border between them */ - - /* modifiers (first switch) */ - RC_CONDITION_ADD_SOURCE, /* everything from this point on affects the condition after it */ + RC_CONDITION_MEASURED, + RC_CONDITION_ADD_SOURCE, RC_CONDITION_SUB_SOURCE, RC_CONDITION_ADD_ADDRESS, RC_CONDITION_REMEMBER, - - /* logic flags (second switch) */ RC_CONDITION_ADD_HITS, RC_CONDITION_SUB_HITS, RC_CONDITION_RESET_NEXT_IF, @@ -180,7 +172,10 @@ enum { RC_OPERATOR_XOR, RC_OPERATOR_MOD, RC_OPERATOR_ADD, - RC_OPERATOR_SUB + RC_OPERATOR_SUB, + + RC_OPERATOR_SUB_PARENT, /* internal use */ + RC_OPERATOR_INDIRECT_READ /* internal use */ }; typedef struct rc_condition_t rc_condition_t; @@ -204,9 +199,6 @@ struct rc_condition_t { /* The comparison operator to use. (RC_OPERATOR_*) */ uint8_t oper; /* operator is a reserved word in C++. */ - /* Set if the condition needs to processed as part of the "check if paused" pass. (bool) */ - uint8_t pause; - /* Whether or not the condition evaluated true on the last check. (bool) */ uint8_t is_true; @@ -224,17 +216,32 @@ struct rc_condset_t { /* The next condition set in the chain. */ rc_condset_t* next; - /* The list of conditions in this condition set. */ + /* The first condition in this condition set. Then follow ->next chain. */ rc_condition_t* conditions; - /* True if any condition in the set is a pause condition. */ - uint8_t has_pause; + /* The number of pause conditions in this condition set. */ + /* The first pause condition is at "this + RC_ALIGN(sizeof(this)). */ + uint16_t num_pause_conditions; + /* The number of reset conditions in this condition set. */ + uint16_t num_reset_conditions; + + /* The number of hittarget conditions in this condition set. */ + uint16_t num_hittarget_conditions; + + /* The number of non-hittarget measured conditions in this condition set. */ + uint16_t num_measured_conditions; + + /* The number of other conditions in this condition set. */ + uint16_t num_other_conditions; + + /* The number of indirect conditions in this condition set. */ + uint16_t num_indirect_conditions; + + /* True if any condition in the set is a pause condition. */ + uint8_t has_pause; /* DEPRECATED - just check num_pause_conditions != 0 */ /* True if the set is currently paused. */ uint8_t is_paused; - - /* True if the set has indirect memory references. */ - uint8_t has_indirect_memrefs; }; /*****************************************************************************\ @@ -259,9 +266,6 @@ struct rc_trigger_t { /* The list of sub condition sets in this test. */ rc_condset_t* alternative; - /* The memory references required by the trigger. */ - rc_memref_t* memrefs; - /* The current state of the MEASURED condition. */ uint32_t measured_value; @@ -274,11 +278,11 @@ struct rc_trigger_t { /* True if at least one condition has a non-zero hit count */ uint8_t has_hits; - /* True if at least one condition has a non-zero required hit count */ - uint8_t has_required_hits; - /* True if the measured value should be displayed as a percentage */ uint8_t measured_as_percent; + + /* True if the trigger has its own rc_memrefs_t */ + uint8_t has_memrefs; }; RC_EXPORT int RC_CCONV rc_trigger_size(const char* memaddr); @@ -297,11 +301,11 @@ struct rc_value_t { /* The current value of the variable. */ rc_memref_value_t value; - /* The list of conditions to evaluate. */ - rc_condset_t* conditions; + /* True if the value has its own rc_memrefs_t */ + uint8_t has_memrefs; - /* The memory references required by the variable. */ - rc_memref_t* memrefs; + /* The list of possible values (traverse next chain, pick max). */ + rc_condset_t* conditions; /* The name of the variable. */ const char* name; @@ -335,9 +339,9 @@ struct rc_lboard_t { rc_trigger_t cancel; rc_value_t value; rc_value_t* progress; - rc_memref_t* memrefs; uint8_t state; + uint8_t has_memrefs; }; RC_EXPORT int RC_CCONV rc_lboard_size(const char* memaddr); @@ -406,7 +410,7 @@ struct rc_richpresence_display_part_t { rc_richpresence_display_part_t* next; const char* text; rc_richpresence_lookup_t* lookup; - rc_memref_value_t *value; + rc_operand_t value; uint8_t display_type; }; @@ -416,13 +420,14 @@ struct rc_richpresence_display_t { rc_trigger_t trigger; rc_richpresence_display_t* next; rc_richpresence_display_part_t* display; + uint8_t has_required_hits; }; struct rc_richpresence_t { rc_richpresence_display_t* first_display; rc_richpresence_lookup_t* first_lookup; - rc_memref_t* memrefs; - rc_value_t* variables; + rc_value_t* values; + uint8_t has_memrefs; }; RC_EXPORT int RC_CCONV rc_richpresence_size(const char* script); diff --git a/dep/rcheevos/rcheevos.vcxproj b/dep/rcheevos/rcheevos.vcxproj index a75e325bb..bf6057f18 100644 --- a/dep/rcheevos/rcheevos.vcxproj +++ b/dep/rcheevos/rcheevos.vcxproj @@ -48,6 +48,10 @@ + + + + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64} diff --git a/dep/rcheevos/rcheevos.vcxproj.filters b/dep/rcheevos/rcheevos.vcxproj.filters index d6653598f..e955f0fd1 100644 --- a/dep/rcheevos/rcheevos.vcxproj.filters +++ b/dep/rcheevos/rcheevos.vcxproj.filters @@ -138,4 +138,10 @@ include + + + + rcheevos + + \ No newline at end of file diff --git a/dep/rcheevos/src/rc_client.c b/dep/rcheevos/src/rc_client.c index b7591ee1a..0df41fdf9 100644 --- a/dep/rcheevos/src/rc_client.c +++ b/dep/rcheevos/src/rc_client.c @@ -91,6 +91,70 @@ static void rc_client_award_achievement_retry(rc_client_scheduled_callback_data_ static int rc_client_is_award_achievement_pending(const rc_client_t* client, uint32_t achievement_id); static void rc_client_submit_leaderboard_entry_retry(rc_client_scheduled_callback_data_t* callback_data, rc_client_t* client, rc_clock_t now); +/* ===== natvis extensions ===== */ + +typedef struct __rc_client_achievement_state_enum_t { uint8_t value; } __rc_client_achievement_state_enum_t; +typedef struct __rc_client_achievement_category_enum_t { uint8_t value; } __rc_client_achievement_category_enum_t; +typedef struct __rc_client_achievement_type_enum_t { uint8_t value; } __rc_client_achievement_type_enum_t; +typedef struct __rc_client_achievement_bucket_enum_t { uint8_t value; } __rc_client_achievement_bucket_enum_t; +typedef struct __rc_client_achievement_unlocked_enum_t { uint8_t value; } __rc_client_achievement_unlocked_enum_t; +typedef struct __rc_client_leaderboard_state_enum_t { uint8_t value; } __rc_client_leaderboard_state_enum_t; +typedef struct __rc_client_leaderboard_format_enum_t { uint8_t value; } __rc_client_leaderboard_format_enum_t; +typedef struct __rc_client_log_level_enum_t { uint8_t value; } __rc_client_log_level_enum_t; +typedef struct __rc_client_event_type_enum_t { uint8_t value; } __rc_client_event_type_enum_t; +typedef struct __rc_client_load_game_state_enum_t { uint8_t value; } __rc_client_load_game_state_enum_t; +typedef struct __rc_client_user_state_enum_t { uint8_t value; } __rc_client_user_state_enum_t; +typedef struct __rc_client_mastery_state_enum_t { uint8_t value; } __rc_client_mastery_state_enum_t; +typedef struct __rc_client_spectator_mode_enum_t { uint8_t value; } __rc_client_spectator_mode_enum_t; +typedef struct __rc_client_disconnect_enum_t { uint8_t value; } __rc_client_disconnect_enum_t; +typedef struct __rc_client_leaderboard_tracker_list_t { rc_client_leaderboard_tracker_info_t* first; } __rc_client_leaderboard_tracker_list_t; +typedef struct __rc_client_subset_info_list_t { rc_client_subset_info_t* first; } __rc_client_subset_info_list_t; +typedef struct __rc_client_media_hash_list_t { rc_client_media_hash_t* first; } __rc_client_media_hash_list_t; +typedef struct __rc_client_subset_info_achievements_list_t { rc_client_subset_info_t info; } __rc_client_subset_info_achievements_list_t; +typedef struct __rc_client_subset_info_leaderboards_list_t { rc_client_subset_info_t info; } __rc_client_subset_info_leaderboards_list_t; +typedef struct __rc_client_scheduled_callback_list_t { rc_client_state_t state; } __rc_client_scheduled_callback_list_t; +typedef struct __rc_client_game_hash_list_t { rc_client_t client; } __rc_client_game_hash_list_t; + +static void rc_client_natvis_helper(const rc_client_event_t* event, rc_client_t* client) +{ + struct natvis_extensions { + __rc_client_achievement_state_enum_t achievement_state; + __rc_client_achievement_category_enum_t achievement_category; + __rc_client_achievement_type_enum_t achievement_type; + __rc_client_achievement_bucket_enum_t achievement_bucket; + __rc_client_achievement_unlocked_enum_t achievement_unlocked; + __rc_client_leaderboard_state_enum_t leaderboard_state; + __rc_client_leaderboard_format_enum_t leaderboard_format; + __rc_client_log_level_enum_t log_level; + __rc_client_event_type_enum_t event_type; + __rc_client_load_game_state_enum_t load_game_state; + __rc_client_user_state_enum_t user_state; + __rc_client_mastery_state_enum_t mastery_state; + __rc_client_spectator_mode_enum_t spectator_mode; + __rc_client_disconnect_enum_t disconnect; + __rc_client_leaderboard_tracker_list_t leaderboard_tracker_list; + __rc_client_subset_info_list_t subset_info_list; + __rc_client_media_hash_list_t media_hash_list; + __rc_client_subset_info_achievements_list_t subset_info_achievements_list; + __rc_client_subset_info_leaderboards_list_t subset_info_leaderboards_list; + __rc_client_scheduled_callback_list_t scheduled_callback_list; + __rc_client_game_hash_list_t client_game_hash_list; + } natvis; + + memset(&natvis, 0, sizeof(natvis)); + (void)event; + (void)client; + + /* this code should never be executed. it just ensures these constants get defined for + * the natvis VisualStudio extension as they're not used directly in the code. */ + natvis.achievement_type.value = RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD; + natvis.achievement_type.value = RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE; + natvis.achievement_type.value = RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION; + natvis.achievement_type.value = RC_CLIENT_ACHIEVEMENT_TYPE_WIN; + natvis.achievement_category.value = RC_CLIENT_ACHIEVEMENT_CATEGORY_NONE; + natvis.event_type.value = RC_CLIENT_EVENT_TYPE_NONE; +} + /* ===== Construction/Destruction ===== */ static void rc_client_dummy_event_handler(const rc_client_event_t* event, rc_client_t* client) @@ -110,6 +174,7 @@ rc_client_t* rc_client_create(rc_client_read_memory_func_t read_memory_function, client->callbacks.read_memory = read_memory_function; client->callbacks.server_call = server_call_function; + client->callbacks.event_handler = rc_client_natvis_helper; client->callbacks.event_handler = rc_client_dummy_event_handler; rc_client_set_legacy_peek(client, RC_CLIENT_LEGACY_PEEK_AUTO); rc_client_set_get_time_millisecs_function(client, NULL); @@ -180,7 +245,7 @@ static void rc_client_log_message_va(const rc_client_t* client, const char* form if (client->callbacks.log_call) { char buffer[2048]; -#ifdef __STDC_WANT_SECURE_LIB__ +#ifdef __STDC_SECURE_LIB__ vsprintf_s(buffer, sizeof(buffer), format, args); #elif __STDC_VERSION__ >= 199901L /* vsnprintf requires c99 */ vsnprintf(buffer, sizeof(buffer), format, args); @@ -1071,27 +1136,23 @@ static void rc_client_validate_addresses(rc_client_game_info_t* game, rc_client_ uint32_t total_count = 0; uint32_t invalid_count = 0; - rc_memref_t** last_memref = &game->runtime.memrefs; - rc_memref_t* memref = game->runtime.memrefs; - for (; memref; memref = memref->next) { - if (!memref->value.is_indirect) { - total_count++; + rc_memref_list_t* memref_list = &game->runtime.memrefs->memrefs; + for (; memref_list; memref_list = memref_list->next) { + rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_end = memref + memref_list->count; + total_count += memref_list->count; + for (; memref < memref_end; ++memref) { if (memref->address > max_address || - client->callbacks.read_memory(memref->address, buffer, 1, client) == 0) { - /* invalid address, remove from chain so we don't have to evaluate it in the future. - * it's still there, so anything referencing it will always fetch 0. */ - *last_memref = memref->next; + client->callbacks.read_memory(memref->address, buffer, 1, client) == 0) { + memref->value.type = RC_VALUE_TYPE_NONE; rc_client_invalidate_memref_achievements(game, client, memref); rc_client_invalidate_memref_leaderboards(game, client, memref); invalid_count++; - continue; } } - - last_memref = &memref->next; } game->max_valid_address = max_address; @@ -1654,9 +1715,10 @@ static void rc_client_copy_achievements(rc_client_load_state_t* load_state, rc_client_achievement_info_t* achievement; rc_client_achievement_info_t* scan; rc_buffer_t* buffer; - rc_parse_state_t parse; + rc_preparse_state_t preparse; const char* memaddr; size_t size; + rc_trigger_t* trigger; int trigger_size; subset->achievements = NULL; @@ -1686,11 +1748,11 @@ static void rc_client_copy_achievements(rc_client_load_state_t* load_state, + sizeof(rc_trigger_t) + sizeof(rc_condset_t) * 2 /* trigger container */ + sizeof(rc_condition_t) * 8 /* assume average trigger length of 8 conditions */ + sizeof(rc_client_achievement_info_t); - rc_buffer_reserve(&load_state->game->buffer, size * num_achievements); + buffer = &load_state->game->buffer; + rc_buffer_reserve(buffer, size * num_achievements); /* allocate the achievement array */ size = sizeof(rc_client_achievement_info_t) * num_achievements; - buffer = &load_state->game->buffer; achievement = achievements = rc_buffer_alloc(buffer, size); memset(achievements, 0, size); @@ -1713,7 +1775,12 @@ static void rc_client_copy_achievements(rc_client_load_state_t* load_state, memaddr = read->definition; rc_runtime_checksum(memaddr, achievement->md5); - trigger_size = rc_trigger_size(memaddr); + rc_init_preparse_state(&preparse, NULL, 0); + preparse.parse.existing_memrefs = load_state->game->runtime.memrefs; + trigger = RC_ALLOC(rc_trigger_t, &preparse.parse); + rc_parse_trigger_internal(trigger, &memaddr, &preparse.parse); + + trigger_size = preparse.parse.offset; if (trigger_size < 0) { RC_CLIENT_LOG_WARN_FORMATTED(load_state->client, "Parse error %d processing achievement %u", trigger_size, read->id); achievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_DISABLED; @@ -1721,23 +1788,22 @@ static void rc_client_copy_achievements(rc_client_load_state_t* load_state, } else { /* populate the item, using the communal memrefs pool */ - rc_init_parse_state(&parse, rc_buffer_reserve(buffer, trigger_size), NULL, 0); - parse.first_memref = &load_state->game->runtime.memrefs; - parse.variables = &load_state->game->runtime.variables; - achievement->trigger = RC_ALLOC(rc_trigger_t, &parse); - rc_parse_trigger_internal(achievement->trigger, &memaddr, &parse); + rc_reset_parse_state(&preparse.parse, rc_buffer_reserve(buffer, trigger_size), NULL, 0); + rc_preparse_reserve_memrefs(&preparse, load_state->game->runtime.memrefs); + achievement->trigger = RC_ALLOC(rc_trigger_t, &preparse.parse); + memaddr = read->definition; + rc_parse_trigger_internal(achievement->trigger, &memaddr, &preparse.parse); - if (parse.offset < 0) { - RC_CLIENT_LOG_WARN_FORMATTED(load_state->client, "Parse error %d processing achievement %u", parse.offset, read->id); + if (preparse.parse.offset < 0) { + RC_CLIENT_LOG_WARN_FORMATTED(load_state->client, "Parse error %d processing achievement %u", preparse.parse.offset, read->id); achievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_DISABLED; achievement->public_.bucket = RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED; } else { - rc_buffer_consume(buffer, parse.buffer, (uint8_t*)parse.buffer + parse.offset); - achievement->trigger->memrefs = NULL; /* memrefs managed by runtime */ + rc_buffer_consume(buffer, preparse.parse.buffer, (uint8_t*)preparse.parse.buffer + preparse.parse.offset); } - rc_destroy_parse_state(&parse); + rc_destroy_preparse_state(&preparse); } achievement->created_time = read->created; @@ -1801,10 +1867,11 @@ static void rc_client_copy_leaderboards(rc_client_load_state_t* load_state, rc_client_leaderboard_info_t* leaderboards; rc_client_leaderboard_info_t* leaderboard; rc_buffer_t* buffer; - rc_parse_state_t parse; + rc_preparse_state_t preparse; const char* memaddr; const char* ptr; size_t size; + rc_lboard_t* lboard; int lboard_size; subset->leaderboards = NULL; @@ -1854,29 +1921,32 @@ static void rc_client_copy_leaderboards(rc_client_load_state_t* load_state, leaderboard->value_djb2 = hash; } - lboard_size = rc_lboard_size(memaddr); + rc_init_preparse_state(&preparse, NULL, 0); + preparse.parse.existing_memrefs = load_state->game->runtime.memrefs; + lboard = RC_ALLOC(rc_lboard_t, &preparse.parse); + rc_parse_lboard_internal(lboard, memaddr, &preparse.parse); + + lboard_size = preparse.parse.offset; if (lboard_size < 0) { RC_CLIENT_LOG_WARN_FORMATTED(load_state->client, "Parse error %d processing leaderboard %u", lboard_size, read->id); leaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_DISABLED; } else { /* populate the item, using the communal memrefs pool */ - rc_init_parse_state(&parse, rc_buffer_reserve(buffer, lboard_size), NULL, 0); - parse.first_memref = &load_state->game->runtime.memrefs; - parse.variables = &load_state->game->runtime.variables; - leaderboard->lboard = RC_ALLOC(rc_lboard_t, &parse); - rc_parse_lboard_internal(leaderboard->lboard, memaddr, &parse); + rc_reset_parse_state(&preparse.parse, rc_buffer_reserve(buffer, lboard_size), NULL, 0); + rc_preparse_reserve_memrefs(&preparse, load_state->game->runtime.memrefs); + leaderboard->lboard = RC_ALLOC(rc_lboard_t, &preparse.parse); + rc_parse_lboard_internal(leaderboard->lboard, memaddr, &preparse.parse); - if (parse.offset < 0) { - RC_CLIENT_LOG_WARN_FORMATTED(load_state->client, "Parse error %d processing leaderboard %u", parse.offset, read->id); + if (preparse.parse.offset < 0) { + RC_CLIENT_LOG_WARN_FORMATTED(load_state->client, "Parse error %d processing leaderboard %u", preparse.parse.offset, read->id); leaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_DISABLED; } else { - rc_buffer_consume(buffer, parse.buffer, (uint8_t*)parse.buffer + parse.offset); - leaderboard->lboard->memrefs = NULL; /* memrefs managed by runtime */ + rc_buffer_consume(buffer, preparse.parse.buffer, (uint8_t*)preparse.parse.buffer + preparse.parse.offset); } - rc_destroy_parse_state(&parse); + rc_destroy_preparse_state(&preparse); } ++leaderboard; @@ -4801,22 +4871,11 @@ void rc_client_set_read_memory_function(rc_client_t* client, rc_client_read_memo static void rc_client_invalidate_processing_memref(rc_client_t* client) { - rc_memref_t** next_memref = &client->game->runtime.memrefs; - rc_memref_t* memref; - /* if processing_memref is not set, this occurred following a pointer chain. ignore it. */ if (!client->state.processing_memref) return; - /* invalid memref. remove from chain so we don't have to evaluate it in the future. - * it's still there, so anything referencing it will always fetch the current value. */ - while ((memref = *next_memref) != NULL) { - if (memref == client->state.processing_memref) { - *next_memref = memref->next; - break; - } - next_memref = &memref->next; - } + client->state.processing_memref->value.type = RC_VALUE_TYPE_NONE; rc_client_invalidate_memref_achievements(client->game, client, client->state.processing_memref); rc_client_invalidate_memref_leaderboards(client->game, client, client->state.processing_memref); @@ -4924,31 +4983,57 @@ int rc_client_is_processing_required(rc_client_t* client) return (client->game->runtime.richpresence && client->game->runtime.richpresence->richpresence); } -static void rc_client_update_memref_values(rc_client_t* client) -{ - rc_memref_t* memref = client->game->runtime.memrefs; - uint32_t value; +static void rc_client_update_memref_values(rc_client_t* client) { + rc_memrefs_t* memrefs = client->game->runtime.memrefs; + rc_memref_list_t* memref_list; + rc_modified_memref_list_t* modified_memref_list; int invalidated_memref = 0; - for (; memref; memref = memref->next) { - if (memref->value.is_indirect) - continue; + memref_list = &memrefs->memrefs; + do { + rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_stop = memref + memref_list->count; + uint32_t value; - client->state.processing_memref = memref; + for (; memref < memref_stop; ++memref) { + if (memref->value.type == RC_VALUE_TYPE_NONE) + continue; - value = rc_peek_value(memref->address, memref->value.size, client->state.legacy_peek, client); + /* if processing_memref is set, and the memory read fails, all dependent achievements will be disabled */ + client->state.processing_memref = memref; - if (client->state.processing_memref) { - rc_update_memref_value(&memref->value, value); + value = rc_peek_value(memref->address, memref->value.size, client->state.legacy_peek, client); + + if (client->state.processing_memref) { + rc_update_memref_value(&memref->value, value); + } + else { + /* if the peek function cleared the processing_memref, the memref was invalidated */ + invalidated_memref = 1; + } } - else { - /* if the peek function cleared the processing_memref, the memref was invalidated */ - invalidated_memref = 1; - } - } + + memref_list = memref_list->next; + } while (memref_list); client->state.processing_memref = NULL; + modified_memref_list = &memrefs->modified_memrefs; + if (modified_memref_list->count) { + do { + rc_modified_memref_t* modified_memref = modified_memref_list->items; + const rc_modified_memref_t* modified_memref_stop = modified_memref + modified_memref_list->count; + + for (; modified_memref < modified_memref_stop; ++modified_memref) + rc_update_memref_value(&modified_memref->memref.value, rc_get_modified_memref_value(modified_memref, client->state.legacy_peek, client)); + + modified_memref_list = modified_memref_list->next; + } while (modified_memref_list); + } + + if (client->game->runtime.richpresence && client->game->runtime.richpresence->richpresence) + rc_update_values(client->game->runtime.richpresence->richpresence->values, client->state.legacy_peek, client, NULL); + if (invalidated_memref) rc_client_update_active_achievements(client->game); } @@ -5362,7 +5447,6 @@ void rc_client_do_frame(rc_client_t* client) rc_client_reset_pending_events(client); rc_client_update_memref_values(client); - rc_update_variables(client->game->runtime.variables, client->state.legacy_peek, client, NULL); client->game->progress_tracker.progress = 0.0; for (subset = client->game->subsets; subset; subset = subset->next) { @@ -5534,9 +5618,8 @@ static void rc_client_reset_richpresence(rc_client_t* client) static void rc_client_reset_variables(rc_client_t* client) { - rc_value_t* variable = client->game->runtime.variables; - for (; variable; variable = variable->next) - rc_reset_value(variable); + if (client->game->runtime.richpresence && client->game->runtime.richpresence->richpresence) + rc_reset_values(client->game->runtime.richpresence->richpresence->values); } static void rc_client_reset_all(rc_client_t* client) diff --git a/dep/rcheevos/src/rc_client_raintegration.c b/dep/rcheevos/src/rc_client_raintegration.c index a686f7fd3..f1893f6b5 100644 --- a/dep/rcheevos/src/rc_client_raintegration.c +++ b/dep/rcheevos/src/rc_client_raintegration.c @@ -6,6 +6,24 @@ #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION +/* ===== natvis extensions ===== */ + +typedef struct __rc_client_raintegration_event_enum_t { uint8_t value; } __rc_client_raintegration_event_enum_t; +static void rc_client_raintegration_natvis_helper(void) +{ + struct natvis_extensions { + __rc_client_raintegration_event_enum_t raintegration_event_type; + } natvis; + + natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE; + natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED; + natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED; + natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_PAUSE; + natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED; +} + +/* ============================= */ + static void rc_client_raintegration_load_dll(rc_client_t* client, const wchar_t* search_directory, rc_client_callback_t callback, void* callback_userdata) { @@ -89,6 +107,9 @@ static void rc_client_raintegration_load_dll(rc_client_t* client, FreeLibrary(hDLL); callback(RC_ABORTED, "One or more required exports was not found in RA_Integration.dll", client, callback_userdata); + + /* dummy reference to natvis helper to ensure extensions get compiled in. */ + raintegration->shutdown = rc_client_raintegration_natvis_helper; } else { rc_mutex_lock(&client->state.mutex); diff --git a/dep/rcheevos/src/rc_client_types.natvis b/dep/rcheevos/src/rc_client_types.natvis new file mode 100644 index 000000000..83f99ec9a --- /dev/null +++ b/dep/rcheevos/src/rc_client_types.natvis @@ -0,0 +1,394 @@ + + + + + + {{display_name={display_name,s} score={score}}} + + + {{title={title,s} id={id}}} + + + {{title={title,s} id={id}}} + + + {RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE} + {RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE} + {RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED} + {RC_CLIENT_ACHIEVEMENT_STATE_DISABLED} + unknown ({value}) + + + {RC_CLIENT_ACHIEVEMENT_CATEGORY_NONE} + {RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE} + {RC_CLIENT_ACHIEVEMENT_CATEGORY_UNOFFICIAL} + {RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL} + unknown ({value}) + + + {RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD} + {RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE} + {RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION} + {RC_CLIENT_ACHIEVEMENT_TYPE_WIN} + unknown ({value}) + + + {RC_CLIENT_ACHIEVEMENT_BUCKET_UNKNOWN} + {RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED} + {RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED} + {RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED} + {RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL} + {RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED} + {RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE} + {RC_CLIENT_ACHIEVEMENT_BUCKET_ALMOST_THERE} + {RC_CLIENT_ACHIEVEMENT_BUCKET_UNSYNCED} + unknown ({value}) + + + {RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE} + {RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE} + {RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE} + {RC_CLIENT_ACHIEVEMENT_UNLOCKED_BOTH} + unknown ({value}) + + + {{title={title,s} id={id}}} + + title + description + points + id + *((__rc_client_achievement_state_enum_t*)&state) + *((__rc_client_achievement_type_enum_t*)&state) + *((__rc_client_achievement_category_enum_t*)&category) + *((__rc_client_achievement_state_enum_t*)&bucket) + *((__rc_client_achievement_unlocked_enum_t*)&unlocked) + + + + {{label={label,s} count={num_achievements}}} + + + num_achievements + achievements[$i] + + + + + {{count={num_buckets}}} + + + num_buckets + buckets[$i] + + + + + {RC_CLIENT_LEADERBOARD_STATE_INACTIVE} + {RC_CLIENT_LEADERBOARD_STATE_ACTIVE} + {RC_CLIENT_LEADERBOARD_STATE_TRACKING} + {RC_CLIENT_LEADERBOARD_STATE_DISABLED} + unknown ({value}) + + + {RC_CLIENT_LEADERBOARD_FORMAT_TIME} + {RC_CLIENT_LEADERBOARD_FORMAT_SCORE} + {RC_CLIENT_LEADERBOARD_FORMAT_VALUE} + unknown ({value}) + + + {{title={title,s} id={id}}} + + title + description + tracker_value + id + *((__rc_client_leaderboard_state_enum_t*)&state) + *((__rc_client_leaderboard_format_enum_t*)&format) + *((__rc_bool_enum_t*)&lower_is_better) + + + + {{label={label,s} count={num_leaderboards}}} + + + num_leaderboards + leaderboards[$i] + + + + + {{count={num_buckets}}} + + + num_buckets + buckets[$i] + + + + + {{rank={rank} score={score,s} username={username}}} + + + {{leaderboard_id={leaderboard_id} num_entries={num_entries}}} + + leaderboard_id + submitted_score + best_score + new_rank + num_entries + + num_top_entries + top_entries[$i] + + + + + {RC_CLIENT_EVENT_TYPE_NONE} + {RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED} + {RC_CLIENT_EVENT_LEADERBOARD_STARTED} + {RC_CLIENT_EVENT_LEADERBOARD_FAILED} + {RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED} + {RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW} + {RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE} + {RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW} + {RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE} + {RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE} + {RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW} + {RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE} + {RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE} + {RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD} + {RC_CLIENT_EVENT_RESET} + {RC_CLIENT_EVENT_GAME_COMPLETED} + {RC_CLIENT_EVENT_SERVER_ERROR} + {RC_CLIENT_EVENT_DISCONNECTED} + {RC_CLIENT_EVENT_RECONNECTED} + unknown ({value}) + + + {{type={*((__rc_client_event_type_enum_t*)&type)}}} + + *((__rc_client_event_type_enum_t*)&type) + *achievement + *leaderboard + *leaderboard + *leaderboard + *achievement + *achievement + *achievement + *achievement + *achievement + *leaderboard_tracker + *leaderboard_tracker + *leaderboard_tracker + *leaderboard_scoreboard + *leaderboard + *server_error + + + + {{count={info.public_.num_achievements}}} + + + info.public_.num_achievements + info.achievements[$i] + + + + + {{count={info.public_.num_leaderboards}}} + + + info.public_.num_leaderboards + info.leaderboards[$i] + + + + + {RC_CLIENT_MASTERY_STATE_NONE} + {RC_CLIENT_MASTERY_STATE_PENDING} + {RC_CLIENT_MASTERY_STATE_SHOWN} + unknown ({value}) + + + {{title={public_.title,s} id={public_.id}}} + + public_ + *((__rc_bool_enum_t*)&active) + *((__rc_client_mastery_state_enum_t*)&mastery) + *((__rc_client_subset_info_achievements_list_t*)this) + *((__rc_client_subset_info_leaderboards_list_t*)this) + + + + {{NULL}} + {(void**)&first,na} + + + first + next + this + + + + + {{NULL}} + {(void**)&first,na} + + + first + next + this + + + + + {{NULL}} + {(void**)&first,na} + + + first + next + this + + + + + {{title={public_.title,s} id={public_.id}}} + + + {{title={public_.title,s} id={public_.id}}} + + + {{title={public_.title,s} id={public_.id}}} + + public_ + *((__rc_client_subset_info_list_t*)&subsets) + *((__rc_client_media_hash_list_t*)&media_hash) + *((__rc_client_leaderboard_tracker_list_t*)&leaderboard_trackers) + progress_tracker + runtime + + + + {{hash={hash,s} game_id={game_id}}} + + + {client.hashes} + + + client.hashes + next + *this + + + + + {RC_CLIENT_LOAD_GAME_STATE_NONE} + {RC_CLIENT_LOAD_GAME_STATE_IDENTIFYING_GAME} + {RC_CLIENT_LOAD_GAME_STATE_AWAIT_LOGIN} + {RC_CLIENT_LOAD_GAME_STATE_FETCHING_GAME_DATA} + {RC_CLIENT_LOAD_GAME_STATE_STARTING_SESSION} + {RC_CLIENT_LOAD_GAME_STATE_DONE} + {RC_CLIENT_LOAD_GAME_STATE_ABORTED} + unknown ({value}) + + + + *((__rc_client_load_game_state_enum_t*)&progress) + *game + subset + *hash + pending_media + start_session_response + (int)outstanding_requests + + + + {{when={when} callback={callback,na}}} + + + {state.scheduled_callbacks} + + + state.scheduled_callbacks + next + *this + + + + + {RC_CLIENT_LOG_LEVEL_NONE} + {RC_CLIENT_LOG_LEVEL_ERROR} + {RC_CLIENT_LOG_LEVEL_WARN} + {RC_CLIENT_LOG_LEVEL_INFO} + {RC_CLIENT_LOG_LEVEL_VERBOSE} + unknown ({value}) + + + {RC_CLIENT_USER_STATE_NONE} + {RC_CLIENT_USER_STATE_LOGIN_REQUESTED} + {RC_CLIENT_USER_STATE_LOGGED_IN} + unknown ({value}) + + + {RC_CLIENT_SPECTATOR_MODE_OFF} + {RC_CLIENT_SPECTATOR_MODE_ON} + {RC_CLIENT_SPECTATOR_MODE_LOCKED} + unknown ({value}) + + + {RC_CLIENT_DISCONNECT_HIDDEN} + {RC_CLIENT_DISCONNECT_VISIBLE} + {RC_CLIENT_DISCONNECT_SHOW_PENDING} + {RC_CLIENT_DISCONNECT_HIDE_PENDING} + {RC_CLIENT_DISCONNECT_VISIBLE|RC_CLIENT_DISCONNECT_HIDE_PENDING} + unknown ({value}) + + + + *((__rc_bool_enum_t*)&hardcore) + *((__rc_bool_enum_t*)&unofficial_enabled) + *((__rc_bool_enum_t*)&encore_mode) + *((__rc_client_spectator_mode_enum_t*)&spectator_mode) + *((__rc_client_disconnect_enum_t*)&disconnect) + *((__rc_client_log_level_enum_t*)&log_level) + *((__rc_client_user_state_enum_t*)&user) + *((__rc_client_scheduled_callback_list_t*)this) + load + + + + + game + *((__rc_client_game_hash_list_t*)&hashes) + user + callbacks + state + + + + {{count={num_items}}} + + + num_items + items[$i] + + + + + {RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE} + {RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED} + {RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED} + {RC_CLIENT_RAINTEGRATION_EVENT_PAUSE} + {RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED} + unknown ({value}) + + + {{type={*((__rc_client_raintegration_event_enum_t*)&type)}}} + + *((__rc_client_raintegration_event_enum_t*)&type) + menu_item + + + diff --git a/dep/rcheevos/src/rc_compat.c b/dep/rcheevos/src/rc_compat.c index 6a8a5de57..0ab9e4dda 100644 --- a/dep/rcheevos/src/rc_compat.c +++ b/dep/rcheevos/src/rc_compat.c @@ -1,5 +1,15 @@ +#if !defined(RC_NO_THREADS) && !defined(_WIN32) && !defined(GEKKO) && !defined(_3DS) && (!defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0) < 500) +/* We'll want to use pthread_mutexattr_settype/PTHREAD_MUTEX_RECURSIVE, but glibc only conditionally exposes pthread_mutexattr_settype and PTHREAD_MUTEX_RECURSIVE depending on feature flags + * Defining _XOPEN_SOURCE must be done at the top of the source file, before including any headers + * pthread_mutexattr_settype/PTHREAD_MUTEX_RECURSIVE are specified the Single UNIX Specification (Version 2, 1997), along with POSIX later on (IEEE Standard 1003.1-2008), so should cover practically any pthread implementation + */ +#undef _XOPEN_SOURCE +#define _XOPEN_SOURCE 500 +#endif + #include "rc_compat.h" +#include #include #include @@ -58,7 +68,7 @@ int rc_snprintf(char* buffer, size_t size, const char* format, ...) va_start(args, format); -#ifdef __STDC_WANT_SECURE_LIB__ +#ifdef __STDC_SECURE_LIB__ result = vsprintf_s(buffer, size, format, args); #else /* assume buffer is large enough and ignore size */ @@ -73,7 +83,7 @@ int rc_snprintf(char* buffer, size_t size, const char* format, ...) #endif -#ifndef __STDC_WANT_SECURE_LIB__ +#ifndef __STDC_SECURE_LIB__ struct tm* rc_gmtime_s(struct tm* buf, const time_t* timer) { @@ -88,61 +98,138 @@ struct tm* rc_gmtime_s(struct tm* buf, const time_t* timer) #if defined(_WIN32) -/* https://gist.github.com/roxlu/1c1af99f92bafff9d8d9 */ +/* https://learn.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c */ +/* implementation largely taken from https://github.com/libsdl-org/SDL/blob/0fc3574/src/thread/windows/SDL_sysmutex.c */ + +#if defined(WINVER) && WINVER >= 0x0600 -#define WIN32_LEAN_AND_MEAN -#include - void rc_mutex_init(rc_mutex_t* mutex) { - /* default security, not owned by calling thread, unnamed */ - mutex->handle = CreateMutex(NULL, FALSE, NULL); + InitializeSRWLock(&mutex->srw_lock); + /* https://learn.microsoft.com/en-us/windows/win32/procthread/thread-handles-and-identifiers */ + /* thread ids are never 0 */ + mutex->owner = 0; + mutex->count = 0; } void rc_mutex_destroy(rc_mutex_t* mutex) { - CloseHandle(mutex->handle); + /* Nothing to do here */ + (void)mutex; } void rc_mutex_lock(rc_mutex_t* mutex) { - WaitForSingleObject(mutex->handle, 0xFFFFFFFF); + DWORD current_thread = GetCurrentThreadId(); + if (mutex->owner == current_thread) { + ++mutex->count; + assert(mutex->count > 0); + } + else { + AcquireSRWLockExclusive(&mutex->srw_lock); + assert(mutex->owner == 0 && mutex->count == 0); + mutex->owner = current_thread; + mutex->count = 1; + } } void rc_mutex_unlock(rc_mutex_t* mutex) { - ReleaseMutex(mutex->handle); + if (mutex->owner == GetCurrentThreadId()) { + assert(mutex->count > 0); + if (--mutex->count == 0) { + mutex->owner = 0; + ReleaseSRWLockExclusive(&mutex->srw_lock); + } + } + else { + assert(!"Tried to unlock unowned mutex"); + } } +#else + +void rc_mutex_init(rc_mutex_t* mutex) +{ + InitializeCriticalSection(&mutex->critical_section); +} + +void rc_mutex_destroy(rc_mutex_t* mutex) +{ + DeleteCriticalSection(&mutex->critical_section); +} + +void rc_mutex_lock(rc_mutex_t* mutex) +{ + EnterCriticalSection(&mutex->critical_section); +} + +void rc_mutex_unlock(rc_mutex_t* mutex) +{ + LeaveCriticalSection(&mutex->critical_section); +} + +#endif + #elif defined(GEKKO) /* https://github.com/libretro/RetroArch/pull/16116 */ void rc_mutex_init(rc_mutex_t* mutex) { - LWP_MutexInit(mutex, NULL); + /* LWP_MutexInit has the handle passed by reference */ + /* Other LWP_Mutex* calls have the handle passed by value */ + LWP_MutexInit(&mutex->handle, 1); } void rc_mutex_destroy(rc_mutex_t* mutex) { - LWP_MutexDestroy(mutex); + LWP_MutexDestroy(mutex->handle); } void rc_mutex_lock(rc_mutex_t* mutex) { - LWP_MutexLock(mutex); + LWP_MutexLock(mutex->handle); } void rc_mutex_unlock(rc_mutex_t* mutex) { - LWP_MutexUnlock(mutex); + LWP_MutexUnlock(mutex->handle); +} + +#elif defined(_3DS) + +void rc_mutex_init(rc_mutex_t* mutex) +{ + RecursiveLock_Init(mutex); +} + +void rc_mutex_destroy(rc_mutex_t* mutex) +{ + /* Nothing to do here */ + (void)mutex; +} + +void rc_mutex_lock(rc_mutex_t* mutex) +{ + RecursiveLock_Lock(mutex); +} + +void rc_mutex_unlock(rc_mutex_t* mutex) +{ + RecursiveLock_Unlock(mutex); } #else void rc_mutex_init(rc_mutex_t* mutex) { - pthread_mutex_init(mutex, NULL); + /* Define the mutex as recursive, for consistent semantics against other rc_mutex_t implementations */ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(mutex, &attr); + pthread_mutexattr_destroy(&attr); } void rc_mutex_destroy(rc_mutex_t* mutex) diff --git a/dep/rcheevos/src/rc_compat.h b/dep/rcheevos/src/rc_compat.h index 2ef9ae0c5..3f24df2ef 100644 --- a/dep/rcheevos/src/rc_compat.h +++ b/dep/rcheevos/src/rc_compat.h @@ -1,6 +1,13 @@ #ifndef RC_COMPAT_H #define RC_COMPAT_H +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include +#endif + #include "rc_export.h" #include @@ -58,7 +65,7 @@ RC_BEGIN_C_DECLS #endif /* __STDC_VERSION__ < 199901L */ -#ifndef __STDC_WANT_SECURE_LIB__ +#ifndef __STDC_SECURE_LIB__ /* _CRT_SECURE_NO_WARNINGS redefinitions */ #define strcpy_s(dest, sz, src) strcpy(dest, src) #define sscanf_s sscanf @@ -77,10 +84,27 @@ RC_BEGIN_C_DECLS #define rc_mutex_lock(mutex) #define rc_mutex_unlock(mutex) #else - #ifdef _WIN32 + #if defined(_WIN32) + typedef struct rc_mutex_t { + #if defined(WINVER) && WINVER >= 0x0600 + /* Windows Vista and later can use a slim reader/writer (SRW) lock */ + SRWLOCK srw_lock; + /* Current thread owner needs to be tracked (for recursive mutex usage) */ + DWORD owner; + DWORD count; + #else + /* Pre-Vista must use a critical section */ + CRITICAL_SECTION critical_section; + #endif + } rc_mutex_t; + #elif defined(GEKKO) + #include typedef struct rc_mutex_t { - void* handle; /* HANDLE is defined as "void*" */ + mutex_t handle; } rc_mutex_t; + #elif defined(_3DS) + #include <3ds/synchronization.h> + typedef RecursiveLock rc_mutex_t; #else #include typedef pthread_mutex_t rc_mutex_t; diff --git a/dep/rcheevos/src/rc_libretro.c b/dep/rcheevos/src/rc_libretro.c index d343ce78c..978ff0b7d 100644 --- a/dep/rcheevos/src/rc_libretro.c +++ b/dep/rcheevos/src/rc_libretro.c @@ -124,7 +124,7 @@ static const rc_disallowed_setting_t _rc_disallowed_neocd_settings[] = { }; static const rc_disallowed_setting_t _rc_disallowed_pcsx_rearmed_settings[] = { - { "pcsx_rearmed_psxclock", "<55" }, + { "pcsx_rearmed_psxclock", ",!auto,<55" }, { "pcsx_rearmed_region", "pal" }, { NULL, NULL } }; @@ -202,14 +202,14 @@ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = { { NULL, NULL } }; -static int rc_libretro_string_equal_nocase_wildcard(const char* test, const char* value) { +static int rc_libretro_string_equal_nocase_wildcard(const char* test, const char* match) { char c1, c2; while ((c1 = *test++)) { - if (tolower(c1) != tolower(c2 = *value++) && c2 != '?') + if (tolower(c1) != tolower(c2 = *match++) && c2 != '?') return (c2 == '*'); } - return (*value == '\0'); + return (*match == '\0'); } static int rc_libretro_numeric_less_than(const char* test, const char* value) { @@ -218,7 +218,50 @@ static int rc_libretro_numeric_less_than(const char* test, const char* value) { return (test_num < value_num); } +static int rc_libretro_match_token(const char* val, const char* token, size_t size, int* result) { + if (*token == '!') { + /* !X => if X is a match, it's explicitly allowed. match with result = false */ + if (rc_libretro_match_token(val, token + 1, size - 1, result)) { + *result = 0; + return 1; + } + } + + if (*token == '<') { + /* if val < token, match with result = true */ + char buffer[128]; + memcpy(buffer, token + 1, size - 1); + buffer[size - 1] = '\0'; + if (rc_libretro_numeric_less_than(val, buffer)) { + *result = 1; + return 1; + } + } + + if (memcmp(token, val, size) == 0 && val[size] == 0) { + /* exact match, match with result = true */ + *result = 1; + return 1; + } + else { + /* check for case insensitive match */ + char buffer[128]; + memcpy(buffer, token, size); + buffer[size] = '\0'; + if (rc_libretro_string_equal_nocase_wildcard(val, buffer)) { + /* case insensitive match, match with result = true */ + *result = 1; + return 1; + } + } + + /* no match */ + return 0; +} + static int rc_libretro_match_value(const char* val, const char* match) { + int result = 0; + /* if value starts with a comma, it's a CSV list of potential matches */ if (*match == ',') { do { @@ -229,33 +272,23 @@ static int rc_libretro_match_value(const char* val, const char* match) { ++match; size = match - ptr; - if (val[size] == '\0') { - if (memcmp(ptr, val, size) == 0) { - return 1; - } - else { - char buffer[128]; - memcpy(buffer, ptr, size); - buffer[size] = '\0'; - if (rc_libretro_string_equal_nocase_wildcard(buffer, val)) - return 1; - } - } - } while (*match == ','); + if (rc_libretro_match_token(val, ptr, size, &result)) + return result; - return 0; + } while (*match == ','); + } + else { + /* a leading exclamation point means the provided value(s) are not forbidden (are allowed) */ + if (*match == '!') + return !rc_libretro_match_value(val, &match[1]); + + /* just a single value, attempt to match it */ + if (rc_libretro_match_token(val, match, strlen(match), &result)) + return result; } - /* a leading exclamation point means the provided value(s) are not forbidden (are allowed) */ - if (*match == '!') - return !rc_libretro_match_value(val, &match[1]); - - /* a leading less tahn means the provided value is the minimum allowed */ - if (*match == '<') - return rc_libretro_numeric_less_than(val, &match[1]); - - /* just a single value, attempt to match it */ - return rc_libretro_string_equal_nocase_wildcard(val, match); + /* value did not match filters, assume it's allowed */ + return 0; } int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value) { diff --git a/dep/rcheevos/src/rc_util.c b/dep/rcheevos/src/rc_util.c index b6aa5bf6d..7d4d966e0 100644 --- a/dep/rcheevos/src/rc_util.c +++ b/dep/rcheevos/src/rc_util.c @@ -38,6 +38,9 @@ void rc_buffer_destroy(rc_buffer_t* buffer) total += (int)(chunk->end - chunk->start); wasted += (int)(chunk->end - chunk->write); ++count; +#endif +#ifdef DEBUG_BUFFERS + printf("< free %p.%p\n", (void*)buffer, (void*)chunk); #endif free(chunk); chunk = next; @@ -70,6 +73,10 @@ uint8_t* rc_buffer_reserve(rc_buffer_t* buffer, size_t amount) if (!chunk->next) break; +#ifdef DEBUG_BUFFERS + printf("> alloc %p.%p\n", (void*)buffer, (void*)chunk->next); +#endif + chunk->next->start = (uint8_t*)chunk->next + chunk_header_size; chunk->next->write = chunk->next->start; chunk->next->end = (uint8_t*)chunk->next + alloc_size; diff --git a/dep/rcheevos/src/rcheevos/alloc.c b/dep/rcheevos/src/rcheevos/alloc.c index 0aa4e5cb5..68b0514ce 100644 --- a/dep/rcheevos/src/rcheevos/alloc.c +++ b/dep/rcheevos/src/rcheevos/alloc.c @@ -35,11 +35,11 @@ void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment if (pointer != 0) { /* valid buffer, grab the next chunk */ - ptr = (void*)((char*)pointer + *offset); + ptr = (void*)((uint8_t*)pointer + *offset); } else if (scratch != 0 && scratch_object_pointer_offset < sizeof(scratch->objs)) { /* only allocate one instance of each object type (indentified by scratch_object_pointer_offset) */ - void** scratch_object_pointer = (void**)((char*)&scratch->objs + scratch_object_pointer_offset); + void** scratch_object_pointer = (void**)((uint8_t*)&scratch->objs + scratch_object_pointer_offset); ptr = *scratch_object_pointer; if (!ptr) { int32_t used; @@ -94,22 +94,223 @@ char* rc_alloc_str(rc_parse_state_t* parse, const char* text, size_t length) { return ptr; } +void rc_init_preparse_state(rc_preparse_state_t* preparse, lua_State* L, int funcs_ndx) +{ + rc_init_parse_state(&preparse->parse, NULL, L, funcs_ndx); + rc_init_parse_state_memrefs(&preparse->parse, &preparse->memrefs); +} + +void rc_destroy_preparse_state(rc_preparse_state_t* preparse) +{ + rc_destroy_parse_state(&preparse->parse); +} + +void rc_preparse_alloc_memrefs(rc_memrefs_t* memrefs, rc_preparse_state_t* preparse) +{ + const uint32_t num_memrefs = rc_memrefs_count_memrefs(&preparse->memrefs); + const uint32_t num_modified_memrefs = rc_memrefs_count_modified_memrefs(&preparse->memrefs); + + if (preparse->parse.offset < 0) + return; + + if (memrefs) { + memset(memrefs, 0, sizeof(*memrefs)); + preparse->parse.memrefs = memrefs; + } + + if (num_memrefs) { + rc_memref_t* memref_items = RC_ALLOC_ARRAY(rc_memref_t, num_memrefs, &preparse->parse); + + if (memrefs) { + memrefs->memrefs.capacity = num_memrefs; + memrefs->memrefs.items = memref_items; + } + } + + if (num_modified_memrefs) { + rc_modified_memref_t* modified_memref_items = + RC_ALLOC_ARRAY(rc_modified_memref_t, num_modified_memrefs, &preparse->parse); + + if (memrefs) { + memrefs->modified_memrefs.capacity = num_modified_memrefs; + memrefs->modified_memrefs.items = modified_memref_items; + } + } + + /* when preparsing, this structure will be allocated at the end. when it's allocated earlier + * in the buffer, it could be followed by something aligned at 8 bytes. force the offset to + * an 8-byte boundary */ + if (!memrefs) { + rc_alloc(preparse->parse.buffer, &preparse->parse.offset, 0, 8, &preparse->parse.scratch, 0); + } +} + +static uint32_t rc_preparse_array_size(uint32_t needed, uint32_t minimum) +{ + while (minimum < needed) + minimum <<= 1; + + return minimum; +} + +void rc_preparse_reserve_memrefs(rc_preparse_state_t* preparse, rc_memrefs_t* memrefs) +{ + uint32_t num_memrefs = rc_memrefs_count_memrefs(&preparse->memrefs); + uint32_t num_modified_memrefs = rc_memrefs_count_modified_memrefs(&preparse->memrefs); + uint32_t available; + + if (preparse->parse.offset < 0) + return; + + if (num_memrefs) { + rc_memref_list_t* memref_list = &memrefs->memrefs; + while (memref_list->count == memref_list->capacity) { + if (!memref_list->next) + break; + + memref_list = memref_list->next; + } + + available = memref_list->capacity - memref_list->count; + if (available < num_memrefs) { + rc_memref_list_t* new_memref_list = (rc_memref_list_t*)calloc(1, sizeof(rc_memref_list_t)); + if (!new_memref_list) + return; + + new_memref_list->capacity = rc_preparse_array_size(num_memrefs - available, 16); + new_memref_list->items = (rc_memref_t*)malloc(new_memref_list->capacity * sizeof(rc_memref_t)); + new_memref_list->allocated = 1; + memref_list->next = new_memref_list; + } + } + + if (num_modified_memrefs) { + rc_modified_memref_list_t* modified_memref_list = &memrefs->modified_memrefs; + while (modified_memref_list->count == modified_memref_list->capacity) { + if (!modified_memref_list->next) + break; + + modified_memref_list = modified_memref_list->next; + } + + available = modified_memref_list->capacity - modified_memref_list->count; + if (available < num_modified_memrefs) { + rc_modified_memref_list_t* new_modified_memref_list = (rc_modified_memref_list_t*)calloc(1, sizeof(rc_modified_memref_list_t)); + if (!new_modified_memref_list) + return; + + new_modified_memref_list->capacity = rc_preparse_array_size(num_modified_memrefs - available, 8); + new_modified_memref_list->items = (rc_modified_memref_t*)malloc(new_modified_memref_list->capacity * sizeof(rc_modified_memref_t)); + new_modified_memref_list->allocated = 1; + modified_memref_list->next = new_modified_memref_list; + } + } + + preparse->parse.memrefs = memrefs; +} + +static void rc_preparse_sync_operand(rc_operand_t* operand, rc_parse_state_t* parse, const rc_memrefs_t* memrefs) +{ + if (rc_operand_is_memref(operand) || rc_operand_is_recall(operand)) { + const rc_memref_t* src_memref = operand->value.memref; + + if (src_memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) { + const rc_modified_memref_list_t* modified_memref_list = &memrefs->modified_memrefs; + for (; modified_memref_list; modified_memref_list = modified_memref_list->next) { + const rc_modified_memref_t* modified_memref = modified_memref_list->items; + const rc_modified_memref_t* modified_memref_end = modified_memref + modified_memref_list->count; + + for (; modified_memref < modified_memref_end; ++modified_memref) { + if ((const rc_modified_memref_t*)src_memref == modified_memref) { + rc_modified_memref_t* dst_modified_memref = rc_alloc_modified_memref(parse, modified_memref->memref.value.size, + &modified_memref->parent, modified_memref->modifier_type, &modified_memref->modifier); + + operand->value.memref = &dst_modified_memref->memref; + return; + } + } + } + } + else { + const rc_memref_list_t* memref_list = &memrefs->memrefs; + for (; memref_list; memref_list = memref_list->next) { + const rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_end = memref + memref_list->count; + + for (; memref < memref_end; ++memref) { + if (src_memref == memref) { + operand->value.memref = rc_alloc_memref(parse, memref->address, memref->value.size); + return; + } + } + } + } + } +} + +void rc_preparse_copy_memrefs(rc_parse_state_t* parse, rc_memrefs_t* memrefs) +{ + const rc_memref_list_t* memref_list = &memrefs->memrefs; + const rc_modified_memref_list_t* modified_memref_list = &memrefs->modified_memrefs; + + for (; memref_list; memref_list = memref_list->next) { + const rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_end = memref + memref_list->count; + + for (; memref < memref_end; ++memref) + rc_alloc_memref(parse, memref->address, memref->value.size); + } + + for (; modified_memref_list; modified_memref_list = modified_memref_list->next) { + rc_modified_memref_t* modified_memref = modified_memref_list->items; + const rc_modified_memref_t* modified_memref_end = modified_memref + modified_memref_list->count; + + for (; modified_memref < modified_memref_end; ++modified_memref) { + rc_preparse_sync_operand(&modified_memref->parent, parse, memrefs); + rc_preparse_sync_operand(&modified_memref->modifier, parse, memrefs); + + rc_alloc_modified_memref(parse, modified_memref->memref.value.size, + &modified_memref->parent, modified_memref->modifier_type, &modified_memref->modifier); + } + } +} + +void rc_reset_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx) +{ +#ifndef RC_DISABLE_LUA + parse->L = L; + parse->funcs_ndx = funcs_ndx; +#else + (void)L; + (void)funcs_ndx; +#endif + + parse->buffer = buffer; + + parse->offset = 0; + parse->memrefs = NULL; + parse->existing_memrefs = NULL; + parse->variables = NULL; + parse->measured_target = 0; + parse->lines_read = 0; + parse->addsource_oper = RC_OPERATOR_NONE; + parse->addsource_parent.type = RC_OPERAND_NONE; + parse->indirect_parent.type = RC_OPERAND_NONE; + parse->remember.type = RC_OPERAND_NONE; + parse->is_value = 0; + parse->has_required_hits = 0; + parse->measured_as_percent = 0; + + parse->scratch.strings = NULL; +} + void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx) { /* could use memset here, but rc_parse_state_t contains a 512 byte buffer that doesn't need to be initialized */ - parse->offset = 0; - parse->L = L; - parse->funcs_ndx = funcs_ndx; - parse->buffer = buffer; - 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; - parse->measured_target = 0; - parse->lines_read = 0; - parse->has_required_hits = 0; - parse->measured_as_percent = 0; + + rc_reset_parse_state(parse, buffer, L, funcs_ndx); } void rc_destroy_parse_state(rc_parse_state_t* parse) diff --git a/dep/rcheevos/src/rcheevos/condition.c b/dep/rcheevos/src/rcheevos/condition.c index de8efd924..57913f26e 100644 --- a/dep/rcheevos/src/rcheevos/condition.c +++ b/dep/rcheevos/src/rcheevos/condition.c @@ -1,6 +1,7 @@ #include "rc_internal.h" #include +#include #include static int rc_test_condition_compare(uint32_t value1, uint32_t value2, uint8_t oper) { @@ -15,7 +16,7 @@ static int rc_test_condition_compare(uint32_t value1, uint32_t value2, uint8_t o } } -static char rc_condition_determine_comparator(const rc_condition_t* self) { +static uint8_t rc_condition_determine_comparator(const rc_condition_t* self) { switch (self->oper) { case RC_OPERATOR_EQ: case RC_OPERATOR_NE: @@ -31,7 +32,8 @@ static char rc_condition_determine_comparator(const rc_condition_t* self) { } if ((self->operand1.type == RC_OPERAND_ADDRESS || self->operand1.type == RC_OPERAND_DELTA) && - !self->operand1.value.memref->value.is_indirect && !rc_operand_is_float(&self->operand1)) { + /* TODO: allow modified memref comparisons */ + self->operand1.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && !rc_operand_is_float(&self->operand1)) { /* left side is an integer memory reference */ int needs_translate = (self->operand1.size != self->operand1.value.memref->value.size); @@ -43,7 +45,7 @@ static char rc_condition_determine_comparator(const rc_condition_t* self) { return needs_translate ? RC_PROCESSING_COMPARE_DELTA_TO_CONST_TRANSFORMED : RC_PROCESSING_COMPARE_DELTA_TO_CONST; } else if ((self->operand2.type == RC_OPERAND_ADDRESS || self->operand2.type == RC_OPERAND_DELTA) && - !self->operand2.value.memref->value.is_indirect && !rc_operand_is_float(&self->operand2)) { + self->operand2.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && !rc_operand_is_float(&self->operand2)) { /* right side is an integer memory reference */ const int is_same_memref = (self->operand1.value.memref == self->operand2.value.memref); needs_translate |= (self->operand2.size != self->operand2.value.memref->value.size); @@ -161,17 +163,45 @@ static int rc_parse_operator(const char** memaddr) { } } -rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, uint8_t is_indirect) { - rc_condition_t* self; +void rc_condition_convert_to_operand(const rc_condition_t* condition, rc_operand_t* operand, rc_parse_state_t* parse) { + if (condition->oper == RC_OPERATOR_NONE) { + if (operand != &condition->operand1) + memcpy(operand, &condition->operand1, sizeof(*operand)); + } + else { + uint8_t new_size = RC_MEMSIZE_32_BITS; + if (rc_operand_is_float(&condition->operand1) || rc_operand_is_float(&condition->operand2)) + new_size = RC_MEMSIZE_FLOAT; + + /* NOTE: this makes the operand include the modification, but we have to also + * leave the modification in the condition so the condition reflects the actual + * definition. This doesn't affect the evaluation logic since this method is only + * called for combining conditions and Measured, and the Measured handling function + * ignores the operator assuming it's been handled by a modified memref chain */ + operand->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse, + new_size, &condition->operand1, condition->oper, &condition->operand2); + + /* not actually an address, just a non-delta memref read */ + operand->type = operand->memref_access_type = RC_OPERAND_ADDRESS; + + operand->size = new_size; + } +} + +rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse) { + rc_condition_t * self = RC_ALLOC(rc_condition_t, parse); + rc_parse_condition_internal(self, memaddr, parse); + return (parse->offset < 0) ? NULL : self; +} + +void rc_parse_condition_internal(rc_condition_t* self, const char** memaddr, rc_parse_state_t* parse) { const char* aux; int result; int can_modify = 0; aux = *memaddr; - self = RC_ALLOC(rc_condition_t, parse); self->current_hits = 0; self->is_true = 0; - self->pause = 0; self->optimized_comparator = RC_PROCESSING_COMPARE_DEFAULT; if (*aux != 0 && aux[1] == ':') { @@ -194,8 +224,11 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse parse->measured_as_percent = 1; self->type = RC_CONDITION_MEASURED; break; + /* e f h j l s u v w x y */ - default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0; + default: + parse->offset = RC_INVALID_CONDITION_TYPE; + return; } aux += 2; @@ -204,79 +237,63 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse self->type = RC_CONDITION_STANDARD; } - result = rc_parse_operand(&self->operand1, &aux, is_indirect, parse); + result = rc_parse_operand(&self->operand1, &aux, parse); if (result < 0) { parse->offset = result; - return 0; + return; } result = rc_parse_operator(&aux); if (result < 0) { parse->offset = result; - return 0; + return; } - self->oper = (char)result; - switch (self->oper) { - case RC_OPERATOR_NONE: - /* non-modifying statements must have a second operand */ - if (!can_modify) { - /* measured does not require a second operand when used in a value */ - if (self->type != RC_CONDITION_MEASURED) { - parse->offset = RC_INVALID_OPERATOR; - return 0; - } + self->oper = (uint8_t)result; + + if (self->oper == RC_OPERATOR_NONE) { + /* non-modifying statements must have a second operand */ + if (!can_modify) { + /* measured does not require a second operand when used in a value */ + if (self->type != RC_CONDITION_MEASURED) { + parse->offset = RC_INVALID_OPERATOR; + return; } + } - /* provide dummy operand of '1' and no required hits */ - self->operand2.type = RC_OPERAND_CONST; - self->operand2.value.num = 1; - self->required_hits = 0; - *memaddr = aux; - return self; + /* provide dummy operand of '1' and no required hits */ + rc_operand_set_const(&self->operand2, 1); + self->required_hits = 0; + *memaddr = aux; + return; + } - case RC_OPERATOR_MULT: - case RC_OPERATOR_DIV: - case RC_OPERATOR_AND: - case RC_OPERATOR_XOR: - case RC_OPERATOR_MOD: - case RC_OPERATOR_ADD: - case RC_OPERATOR_SUB: - /* modifying operators are only valid on modifying statements */ - if (can_modify) + if (can_modify && !rc_operator_is_modifying(self->oper)) { + /* comparison operators are not valid on modifying statements */ + switch (self->type) { + case RC_CONDITION_ADD_SOURCE: + case RC_CONDITION_SUB_SOURCE: + case RC_CONDITION_ADD_ADDRESS: + case RC_CONDITION_REMEMBER: + /* prevent parse errors on legacy achievements where a condition was present before changing the type */ + self->oper = RC_OPERATOR_NONE; break; - /* fallthrough */ - default: - /* comparison operators are not valid on modifying statements */ - if (can_modify) { - switch (self->type) { - case RC_CONDITION_ADD_SOURCE: - case RC_CONDITION_SUB_SOURCE: - case RC_CONDITION_ADD_ADDRESS: - case RC_CONDITION_REMEMBER: - /* prevent parse errors on legacy achievements where a condition was present before changing the type */ - self->oper = RC_OPERATOR_NONE; - break; - - default: - parse->offset = RC_INVALID_OPERATOR; - return 0; - } - } - break; + default: + parse->offset = RC_INVALID_OPERATOR; + return; + } } - result = rc_parse_operand(&self->operand2, &aux, is_indirect, parse); + result = rc_parse_operand(&self->operand2, &aux, parse); if (result < 0) { parse->offset = result; - return 0; + return; } if (self->oper == RC_OPERATOR_NONE) { /* if operator is none, explicitly clear out the right side */ - self->operand2.type = RC_OPERAND_CONST; - self->operand2.value.num = 0; + rc_operand_set_const(&self->operand2, 0); } if (*aux == '(') { @@ -285,7 +302,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse if (end == aux || *end != ')') { parse->offset = RC_INVALID_REQUIRED_HITS; - return 0; + return; } /* if operator is none, explicitly clear out the required hits */ @@ -302,7 +319,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse if (end == aux || *end != '.') { parse->offset = RC_INVALID_REQUIRED_HITS; - return 0; + return; } /* if operator is none, explicitly clear out the required hits */ @@ -321,7 +338,140 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse self->optimized_comparator = rc_condition_determine_comparator(self); *memaddr = aux; - return self; +} + +void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t* parse) { + /* type of values in the chain are determined by the parent. + * the last element of a chain is determined by the operand + * + * 1 + 1.5 + 1.75 + 1.0 => (int)1 + (int)1 + (int)1 + (float)1 = (float)4.0 + * 1.0 + 1.5 + 1.75 + 1.0 => (float)1.0 + (float)1.5 + (float)1.75 + (float)1.0 = (float)5.25 + * 1.0 + 1.5 + 1.75 + 1 => (float)1.0 + (float)1.5 + (float)1.75 + (int)1 = (int)5 + */ + + switch (condition->type) { + case RC_CONDITION_ADD_ADDRESS: + if (condition->oper != RC_OPERAND_NONE) + rc_condition_convert_to_operand(condition, &parse->indirect_parent, parse); + else + memcpy(&parse->indirect_parent, &condition->operand1, sizeof(parse->indirect_parent)); + + break; + + case RC_CONDITION_ADD_SOURCE: + if (parse->addsource_parent.type == RC_OPERAND_NONE) { + rc_condition_convert_to_operand(condition, &parse->addsource_parent, parse); + } + else { + rc_operand_t cond_operand; + /* type determined by parent */ + const uint8_t new_size = rc_operand_is_float(&parse->addsource_parent) ? RC_MEMSIZE_FLOAT : RC_MEMSIZE_32_BITS; + + rc_condition_convert_to_operand(condition, &cond_operand, parse); + rc_operand_addsource(&cond_operand, parse, new_size); + memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand)); + } + + parse->addsource_oper = RC_OPERATOR_ADD; + parse->indirect_parent.type = RC_OPERAND_NONE; + break; + + case RC_CONDITION_SUB_SOURCE: + if (parse->addsource_parent.type == RC_OPERAND_NONE) { + rc_condition_convert_to_operand(condition, &parse->addsource_parent, parse); + parse->addsource_oper = RC_OPERATOR_SUB_PARENT; + } + else { + rc_operand_t cond_operand; + /* type determined by parent */ + 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 the previous element was a constant we have to turn it into a memref by adding zero */ + rc_modified_memref_t* memref; + rc_operand_t zero; + rc_operand_set_const(&zero, 0); + memref = rc_alloc_modified_memref(parse, + parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD, &zero); + parse->addsource_parent.value.memref = (rc_memref_t*)memref; + parse->addsource_parent.type = RC_OPERAND_ADDRESS; + } + else if (parse->addsource_oper == RC_OPERATOR_SUB_PARENT) { + /* if the previous element was also a SubSource, we have to insert a 0 and start subtracting from there */ + rc_modified_memref_t* negate; + rc_operand_t zero; + + if (rc_operand_is_float(&parse->addsource_parent)) + rc_operand_set_float_const(&zero, 0.0); + else + rc_operand_set_const(&zero, 0); + + negate = rc_alloc_modified_memref(parse, new_size, &parse->addsource_parent, RC_OPERATOR_SUB_PARENT, &zero); + parse->addsource_parent.value.memref = (rc_memref_t*)negate; + parse->addsource_parent.size = zero.size; + } + + /* subtract the condition from the chain */ + parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB : RC_OPERATOR_SUB_PARENT; + rc_condition_convert_to_operand(condition, &cond_operand, parse); + rc_operand_addsource(&cond_operand, parse, new_size); + memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand)); + + /* indicate the next value can be added to the chain */ + parse->addsource_oper = RC_OPERATOR_ADD; + } + + parse->indirect_parent.type = RC_OPERAND_NONE; + break; + + case RC_CONDITION_REMEMBER: + rc_condition_convert_to_operand(condition, &condition->operand1, parse); + + if (parse->addsource_parent.type != RC_OPERAND_NONE) { + /* type determined by leaf */ + rc_operand_addsource(&condition->operand1, parse, condition->operand1.size); + } + + memcpy(&parse->remember, &condition->operand1, sizeof(parse->remember)); + + parse->addsource_parent.type = RC_OPERAND_NONE; + parse->indirect_parent.type = RC_OPERAND_NONE; + break; + + case RC_CONDITION_MEASURED: + /* Measured condition can have modifiers in values */ + if (parse->is_value) { + switch (condition->oper) { + case RC_OPERATOR_AND: + case RC_OPERATOR_XOR: + case RC_OPERATOR_DIV: + case RC_OPERATOR_MULT: + case RC_OPERATOR_MOD: + case RC_OPERATOR_ADD: + case RC_OPERATOR_SUB: + rc_condition_convert_to_operand(condition, &condition->operand1, parse); + break; + + default: + break; + } + } + + /* fallthrough */ /* to default */ + + default: + if (parse->addsource_parent.type != RC_OPERAND_NONE) { + /* type determined by leaf */ + rc_operand_addsource(&condition->operand1, parse, condition->operand1.size); + + if (parse->buffer) + condition->optimized_comparator = rc_condition_determine_comparator(condition); + } + + parse->addsource_parent.type = RC_OPERAND_NONE; + parse->indirect_parent.type = RC_OPERAND_NONE; + break; + } } int rc_condition_is_combining(const rc_condition_t* self) { @@ -500,41 +650,35 @@ static int rc_test_condition_compare_delta_to_memref_transformed(rc_condition_t* int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state) { rc_typed_value_t value1, value2; - if (eval_state->add_value.type != RC_VALUE_TYPE_NONE) { - /* if there's an accumulator, we can't use the optimized comparators */ - rc_evaluate_operand(&value1, &self->operand1, eval_state); - rc_typed_value_add(&value1, &eval_state->add_value); - } else { - /* use an optimized comparator whenever possible */ - switch (self->optimized_comparator) { - case RC_PROCESSING_COMPARE_MEMREF_TO_CONST: - return rc_test_condition_compare_memref_to_const(self); - case RC_PROCESSING_COMPARE_MEMREF_TO_DELTA: - return rc_test_condition_compare_memref_to_delta(self); - case RC_PROCESSING_COMPARE_MEMREF_TO_MEMREF: - return rc_test_condition_compare_memref_to_memref(self); - case RC_PROCESSING_COMPARE_DELTA_TO_CONST: - return rc_test_condition_compare_delta_to_const(self); - case RC_PROCESSING_COMPARE_DELTA_TO_MEMREF: - return rc_test_condition_compare_delta_to_memref(self); - case RC_PROCESSING_COMPARE_MEMREF_TO_CONST_TRANSFORMED: - return rc_test_condition_compare_memref_to_const_transformed(self); - case RC_PROCESSING_COMPARE_MEMREF_TO_DELTA_TRANSFORMED: - return rc_test_condition_compare_memref_to_delta_transformed(self); - case RC_PROCESSING_COMPARE_MEMREF_TO_MEMREF_TRANSFORMED: - return rc_test_condition_compare_memref_to_memref_transformed(self); - case RC_PROCESSING_COMPARE_DELTA_TO_CONST_TRANSFORMED: - return rc_test_condition_compare_delta_to_const_transformed(self); - case RC_PROCESSING_COMPARE_DELTA_TO_MEMREF_TRANSFORMED: - return rc_test_condition_compare_delta_to_memref_transformed(self); - case RC_PROCESSING_COMPARE_ALWAYS_TRUE: - return 1; - case RC_PROCESSING_COMPARE_ALWAYS_FALSE: - return 0; - default: - rc_evaluate_operand(&value1, &self->operand1, eval_state); - break; - } + /* use an optimized comparator whenever possible */ + switch (self->optimized_comparator) { + case RC_PROCESSING_COMPARE_MEMREF_TO_CONST: + return rc_test_condition_compare_memref_to_const(self); + case RC_PROCESSING_COMPARE_MEMREF_TO_DELTA: + return rc_test_condition_compare_memref_to_delta(self); + case RC_PROCESSING_COMPARE_MEMREF_TO_MEMREF: + return rc_test_condition_compare_memref_to_memref(self); + case RC_PROCESSING_COMPARE_DELTA_TO_CONST: + return rc_test_condition_compare_delta_to_const(self); + case RC_PROCESSING_COMPARE_DELTA_TO_MEMREF: + return rc_test_condition_compare_delta_to_memref(self); + case RC_PROCESSING_COMPARE_MEMREF_TO_CONST_TRANSFORMED: + return rc_test_condition_compare_memref_to_const_transformed(self); + case RC_PROCESSING_COMPARE_MEMREF_TO_DELTA_TRANSFORMED: + return rc_test_condition_compare_memref_to_delta_transformed(self); + case RC_PROCESSING_COMPARE_MEMREF_TO_MEMREF_TRANSFORMED: + return rc_test_condition_compare_memref_to_memref_transformed(self); + case RC_PROCESSING_COMPARE_DELTA_TO_CONST_TRANSFORMED: + return rc_test_condition_compare_delta_to_const_transformed(self); + case RC_PROCESSING_COMPARE_DELTA_TO_MEMREF_TRANSFORMED: + return rc_test_condition_compare_delta_to_memref_transformed(self); + case RC_PROCESSING_COMPARE_ALWAYS_TRUE: + return 1; + case RC_PROCESSING_COMPARE_ALWAYS_FALSE: + return 0; + default: + rc_evaluate_operand(&value1, &self->operand1, eval_state); + break; } rc_evaluate_operand(&value2, &self->operand2, eval_state); @@ -548,38 +692,5 @@ void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_evaluate_operand(value, &self->operand1, eval_state); rc_evaluate_operand(&amount, &self->operand2, eval_state); - switch (self->oper) { - case RC_OPERATOR_MULT: - rc_typed_value_multiply(value, &amount); - break; - - case RC_OPERATOR_DIV: - rc_typed_value_divide(value, &amount); - break; - - case RC_OPERATOR_AND: - rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED); - rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED); - value->value.u32 &= amount.value.u32; - break; - - case RC_OPERATOR_XOR: - rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED); - rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED); - value->value.u32 ^= amount.value.u32; - break; - - case RC_OPERATOR_MOD: - rc_typed_value_modulus(value, &amount); - break; - - case RC_OPERATOR_ADD: - rc_typed_value_add(value, &amount); - break; - - case RC_OPERATOR_SUB: - rc_typed_value_negate(&amount); - rc_typed_value_add(value, &amount); - break; - } + rc_typed_value_combine(value, &amount, self->oper); } diff --git a/dep/rcheevos/src/rcheevos/condset.c b/dep/rcheevos/src/rcheevos/condset.c index f03d47b45..1ac64b334 100644 --- a/dep/rcheevos/src/rcheevos/condset.c +++ b/dep/rcheevos/src/rcheevos/condset.c @@ -2,272 +2,415 @@ #include /* memcpy */ -static void rc_update_condition_pause(rc_condition_t* condition) { - rc_condition_t* subclause = condition; +enum { + RC_CONDITION_CLASSIFICATION_COMBINING, + RC_CONDITION_CLASSIFICATION_PAUSE, + RC_CONDITION_CLASSIFICATION_RESET, + RC_CONDITION_CLASSIFICATION_HITTARGET, + RC_CONDITION_CLASSIFICATION_MEASURED, + RC_CONDITION_CLASSIFICATION_OTHER, + RC_CONDITION_CLASSIFICATION_INDIRECT +}; - while (condition) { - if (condition->type == RC_CONDITION_PAUSE_IF) { - while (subclause != condition) { - subclause->pause = 1; - subclause = subclause->next; - } - condition->pause = 1; - } - else { - condition->pause = 0; - } +static int rc_classify_condition(const rc_condition_t* cond) { + switch (cond->type) { + case RC_CONDITION_PAUSE_IF: + return RC_CONDITION_CLASSIFICATION_PAUSE; - if (!rc_condition_is_combining(condition)) - subclause = condition->next; + case RC_CONDITION_RESET_IF: + return RC_CONDITION_CLASSIFICATION_RESET; - condition = condition->next; + case RC_CONDITION_ADD_ADDRESS: + case RC_CONDITION_ADD_SOURCE: + case RC_CONDITION_SUB_SOURCE: + /* these are handled by rc_modified_memref_t */ + return RC_CONDITION_CLASSIFICATION_INDIRECT; + + case RC_CONDITION_ADD_HITS: + case RC_CONDITION_AND_NEXT: + case RC_CONDITION_OR_NEXT: + case RC_CONDITION_REMEMBER: + case RC_CONDITION_RESET_NEXT_IF: + case RC_CONDITION_SUB_HITS: + return RC_CONDITION_CLASSIFICATION_COMBINING; + + case RC_CONDITION_MEASURED: + case RC_CONDITION_MEASURED_IF: + /* even if not measuring a hit target, we still want to evaluate it every frame */ + return RC_CONDITION_CLASSIFICATION_MEASURED; + + default: + if (cond->required_hits != 0) + return RC_CONDITION_CLASSIFICATION_HITTARGET; + + return RC_CONDITION_CLASSIFICATION_OTHER; } } -rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value) { - rc_condset_t* self; - rc_condition_t** next; - int in_add_address; - uint32_t measured_target = 0; +static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr) { + rc_parse_state_t parse; + rc_memrefs_t memrefs; + rc_condition_t condition; + int classification; + uint32_t index = 0; + uint32_t chain_length = 1; - self = RC_ALLOC(rc_condset_t, parse); - self->has_pause = self->is_paused = self->has_indirect_memrefs = 0; - next = &self->conditions; + rc_init_parse_state(&parse, NULL, NULL, 0); + rc_init_parse_state_memrefs(&parse, &memrefs); + + do { + rc_parse_condition_internal(&condition, &memaddr, &parse); + + if (parse.offset < 0) + return parse.offset; + + ++index; + + classification = rc_classify_condition(&condition); + switch (classification) { + case RC_CONDITION_CLASSIFICATION_COMBINING: + ++chain_length; + continue; + + case RC_CONDITION_CLASSIFICATION_INDIRECT: + ++self->num_indirect_conditions; + continue; + + case RC_CONDITION_CLASSIFICATION_PAUSE: + self->num_pause_conditions += chain_length; + break; + + case RC_CONDITION_CLASSIFICATION_RESET: + self->num_reset_conditions += chain_length; + break; + + case RC_CONDITION_CLASSIFICATION_HITTARGET: + self->num_hittarget_conditions += chain_length; + break; + + case RC_CONDITION_CLASSIFICATION_MEASURED: + self->num_measured_conditions += chain_length; + break; + + default: + self->num_other_conditions += chain_length; + break; + } + + chain_length = 1; + } while (*memaddr++ == '_'); + + return index; +} + +static int rc_find_next_classification(const char* memaddr) { + rc_parse_state_t parse; + rc_memrefs_t memrefs; + rc_condition_t condition; + int classification; + + rc_init_parse_state(&parse, NULL, NULL, 0); + rc_init_parse_state_memrefs(&parse, &memrefs); + + do { + rc_parse_condition_internal(&condition, &memaddr, &parse); + if (parse.offset < 0) + break; + + classification = rc_classify_condition(&condition); + switch (classification) { + case RC_CONDITION_CLASSIFICATION_COMBINING: + case RC_CONDITION_CLASSIFICATION_INDIRECT: + break; + + default: + return classification; + } + } while (*memaddr++ == '_'); + + return RC_CONDITION_CLASSIFICATION_OTHER; +} + +static void rc_condition_update_recall_operand(rc_operand_t* operand, const rc_operand_t* remember) +{ + if (operand->type == RC_OPERAND_RECALL) { + if (rc_operand_type_is_memref(operand->memref_access_type) && operand->value.memref == NULL) { + memcpy(operand, remember, sizeof(*remember)); + operand->memref_access_type = operand->type; + operand->type = RC_OPERAND_RECALL; + } + } + else if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) { + rc_modified_memref_t* modified_memref = (rc_modified_memref_t*)operand->value.memref; + rc_condition_update_recall_operand(&modified_memref->parent, remember); + rc_condition_update_recall_operand(&modified_memref->modifier, remember); + } +} + +static void rc_update_condition_pause_remember(rc_condset_t* self) { + rc_operand_t* pause_remember = NULL; + rc_condition_t* condition; + rc_condition_t* pause_conditions; + const rc_condition_t* end_pause_condition; + + /* ASSERT: pause conditions are first conditions */ + pause_conditions = rc_condset_get_conditions(self); + end_pause_condition = pause_conditions + self->num_pause_conditions; + + for (condition = pause_conditions; condition < end_pause_condition; ++condition) { + if (condition->type == RC_CONDITION_REMEMBER) { + pause_remember = &condition->operand1; + } + else if (pause_remember == NULL) { + /* if we picked up a non-pause remember, discard it */ + if (condition->operand1.type == RC_OPERAND_RECALL && + rc_operand_type_is_memref(condition->operand1.memref_access_type)) { + condition->operand1.value.memref = NULL; + } + + if (condition->operand2.type == RC_OPERAND_RECALL && + rc_operand_type_is_memref(condition->operand2.memref_access_type)) { + condition->operand2.value.memref = NULL; + } + } + } + + if (pause_remember) { + for (condition = self->conditions; condition; condition = condition->next) { + if (condition >= end_pause_condition) { + /* if we didn't find a remember for a non-pause condition, use the last pause remember */ + rc_condition_update_recall_operand(&condition->operand1, pause_remember); + rc_condition_update_recall_operand(&condition->operand2, pause_remember); + } + + /* Anything after this point will have already been handled */ + if (condition->type == RC_CONDITION_REMEMBER) + break; + } + } +} + +rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) { + rc_condset_with_trailing_conditions_t* condset_with_conditions; + rc_condset_t local_condset; + rc_condset_t* self; + rc_condition_t condition; + rc_condition_t* conditions; + rc_condition_t** next; + rc_condition_t* pause_conditions = NULL; + rc_condition_t* reset_conditions = NULL; + rc_condition_t* hittarget_conditions = NULL; + rc_condition_t* measured_conditions = NULL; + rc_condition_t* other_conditions = NULL; + rc_condition_t* indirect_conditions = NULL; + int classification, combining_classification = RC_CONDITION_CLASSIFICATION_COMBINING; + uint32_t measured_target = 0; + int32_t result; if (**memaddr == 'S' || **memaddr == 's' || !**memaddr) { /* empty group - editor allows it, so we have to support it */ - *next = 0; + self = RC_ALLOC(rc_condset_t, parse); + memset(self, 0, sizeof(*self)); return self; } - in_add_address = 0; + memset(&local_condset, 0, sizeof(local_condset)); + result = rc_classify_conditions(&local_condset, *memaddr); + if (result < 0) { + parse->offset = result; + return NULL; + } + + condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t, + rc_condition_t, conditions, result, parse); + if (parse->offset < 0) + return NULL; + + self = (rc_condset_t*)condset_with_conditions; + memcpy(self, &local_condset, sizeof(local_condset)); + conditions = &condset_with_conditions->conditions[0]; + + if (parse->buffer) { + pause_conditions = conditions; + conditions += self->num_pause_conditions; + + reset_conditions = conditions; + conditions += self->num_reset_conditions; + + hittarget_conditions = conditions; + conditions += self->num_hittarget_conditions; + + measured_conditions = conditions; + conditions += self->num_measured_conditions; + + other_conditions = conditions; + conditions += self->num_other_conditions; + + indirect_conditions = conditions; + } + + next = &self->conditions; + + /* each condition set has a functionally new recall accumulator */ + parse->remember.type = RC_OPERAND_NONE; + for (;;) { - *next = rc_parse_condition(memaddr, parse, in_add_address); + rc_parse_condition_internal(&condition, memaddr, parse); - if (parse->offset < 0) { - return 0; - } + if (parse->offset < 0) + return NULL; - if ((*next)->oper == RC_OPERATOR_NONE) { - switch ((*next)->type) { + if (condition.oper == RC_OPERATOR_NONE) { + switch (condition.type) { case RC_CONDITION_ADD_ADDRESS: case RC_CONDITION_ADD_SOURCE: case RC_CONDITION_SUB_SOURCE: case RC_CONDITION_REMEMBER: - /* these conditions don't require a right hand size (implied *1) */ + /* these conditions don't require a right hand side (implied *1) */ break; case RC_CONDITION_MEASURED: /* right hand side is not required when Measured is used in a value */ - if (is_value) + if (parse->is_value) break; /* fallthrough */ /* to default */ default: parse->offset = RC_INVALID_OPERATOR; - return 0; + return NULL; } } - self->has_pause |= (*next)->type == RC_CONDITION_PAUSE_IF; - in_add_address = (*next)->type == RC_CONDITION_ADD_ADDRESS; - self->has_indirect_memrefs |= in_add_address; - - switch ((*next)->type) { - case RC_CONDITION_MEASURED: - if (measured_target != 0) { - /* multiple Measured flags cannot exist in the same group */ - parse->offset = RC_MULTIPLE_MEASURED; - return 0; - } - else if (is_value) { - measured_target = (unsigned)-1; - switch ((*next)->oper) - { - case RC_OPERATOR_AND: - case RC_OPERATOR_XOR: - case RC_OPERATOR_DIV: - case RC_OPERATOR_MULT: - case RC_OPERATOR_MOD: - case RC_OPERATOR_ADD: - case RC_OPERATOR_SUB: - case RC_OPERATOR_NONE: - /* measuring value. leave required_hits at 0 */ - break; - - default: - /* comparison operator, measuring hits. set required_hits to MAX_INT */ - (*next)->required_hits = measured_target; - break; + switch (condition.type) { + case RC_CONDITION_MEASURED: + if (measured_target != 0) { + /* multiple Measured flags cannot exist in the same group */ + parse->offset = RC_MULTIPLE_MEASURED; + return NULL; } - } - else if ((*next)->required_hits != 0) { - measured_target = (*next)->required_hits; - } - else if ((*next)->operand2.type == RC_OPERAND_CONST) { - measured_target = (*next)->operand2.value.num; - } - else if ((*next)->operand2.type == RC_OPERAND_FP) { - measured_target = (unsigned)(*next)->operand2.value.dbl; + else if (parse->is_value) { + measured_target = (uint32_t)-1; + if (!rc_operator_is_modifying(condition.oper)) { + /* measuring comparison in a value results in a tally (hit count). set target to MAX_INT */ + condition.required_hits = measured_target; + } + } + else if (condition.required_hits != 0) { + measured_target = condition.required_hits; + } + else if (condition.operand2.type == RC_OPERAND_CONST) { + measured_target = condition.operand2.value.num; + } + else if (condition.operand2.type == RC_OPERAND_FP) { + measured_target = (unsigned)condition.operand2.value.dbl; + } + else { + parse->offset = RC_INVALID_MEASURED_TARGET; + return NULL; + } + + if (parse->measured_target && measured_target != parse->measured_target) { + /* multiple Measured flags in separate groups must have the same target */ + parse->offset = RC_MULTIPLE_MEASURED; + return NULL; + } + + parse->measured_target = measured_target; + break; + + case RC_CONDITION_STANDARD: + case RC_CONDITION_TRIGGER: + /* these flags are not allowed in value expressions */ + if (parse->is_value) { + parse->offset = RC_INVALID_VALUE_FLAG; + return NULL; + } + break; + } + + rc_condition_update_parse_state(&condition, parse); + + if (parse->buffer) { + classification = rc_classify_condition(&condition); + if (classification == RC_CONDITION_CLASSIFICATION_COMBINING) { + if (combining_classification == RC_CONDITION_CLASSIFICATION_COMBINING) + combining_classification = rc_find_next_classification(&(*memaddr)[1]); /* skip over '_' */ + + classification = combining_classification; } else { - parse->offset = RC_INVALID_MEASURED_TARGET; - return 0; + combining_classification = RC_CONDITION_CLASSIFICATION_COMBINING; } - if (parse->measured_target && measured_target != parse->measured_target) { - /* multiple Measured flags in separate groups must have the same target */ - parse->offset = RC_MULTIPLE_MEASURED; - return 0; + switch (classification) { + case RC_CONDITION_CLASSIFICATION_PAUSE: + memcpy(pause_conditions, &condition, sizeof(condition)); + *next = pause_conditions++; + break; + + case RC_CONDITION_CLASSIFICATION_RESET: + memcpy(reset_conditions, &condition, sizeof(condition)); + *next = reset_conditions++; + break; + + case RC_CONDITION_CLASSIFICATION_HITTARGET: + memcpy(hittarget_conditions, &condition, sizeof(condition)); + *next = hittarget_conditions++; + break; + + case RC_CONDITION_CLASSIFICATION_MEASURED: + memcpy(measured_conditions, &condition, sizeof(condition)); + *next = measured_conditions++; + break; + + case RC_CONDITION_CLASSIFICATION_INDIRECT: + memcpy(indirect_conditions, &condition, sizeof(condition)); + *next = indirect_conditions++; + break; + + default: + memcpy(other_conditions, &condition, sizeof(condition)); + *next = other_conditions++; + break; } - parse->measured_target = measured_target; - break; - - case RC_CONDITION_STANDARD: - case RC_CONDITION_TRIGGER: - /* these flags are not allowed in value expressions */ - if (is_value) { - parse->offset = RC_INVALID_VALUE_FLAG; - return 0; - } - break; - - default: - break; + next = &(*next)->next; } - next = &(*next)->next; - - if (**memaddr != '_') { + if (**memaddr != '_') break; - } (*memaddr)++; } - *next = 0; + *next = NULL; - if (parse->buffer != 0) - rc_update_condition_pause(self->conditions); + self->has_pause = self->num_pause_conditions > 0; + if (self->has_pause && parse->buffer && parse->remember.type != RC_OPERAND_NONE) + rc_update_condition_pause_remember(self); return self; } -static void rc_condset_update_indirect_memrefs(rc_condition_t* condition, int processing_pause, rc_eval_state_t* eval_state) { - for (; condition != 0; condition = condition->next) { - if (condition->pause != processing_pause) - continue; +static uint8_t rc_condset_evaluate_condition_no_add_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) { + /* evaluate the current condition */ + uint8_t cond_valid = (uint8_t)rc_test_condition(condition, eval_state); + condition->is_true = cond_valid; - if (condition->type == RC_CONDITION_ADD_ADDRESS) { - rc_typed_value_t value; - rc_evaluate_condition_value(&value, condition, eval_state); - rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED); - eval_state->add_address = value.value.u32; - continue; - } + if (eval_state->reset_next) { + /* previous ResetNextIf resets the hit count on this condition and prevents it from being true */ + eval_state->was_cond_reset |= (condition->current_hits != 0); - /* call rc_get_memref_value to update the indirect memrefs. it won't do anything with non-indirect - * memrefs and avoids a second check of is_indirect. also, we ignore the response, so it doesn't - * matter what operand type we pass. assume RC_OPERAND_ADDRESS is the quickest. */ - if (rc_operand_is_memref(&condition->operand1)) - rc_get_memref_value(condition->operand1.value.memref, RC_OPERAND_ADDRESS, eval_state); - - if (rc_operand_is_memref(&condition->operand2)) - rc_get_memref_value(condition->operand2.value.memref, RC_OPERAND_ADDRESS, eval_state); - - eval_state->add_address = 0; + condition->current_hits = 0; + cond_valid = 0; } -} + else { + /* apply chained logic flags */ + cond_valid &= eval_state->and_next; + cond_valid |= eval_state->or_next; -static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc_eval_state_t* eval_state) { - rc_condition_t* condition; - rc_typed_value_t value; - int set_valid, cond_valid, and_next, or_next, reset_next, measured_from_hits, can_measure; - rc_typed_value_t measured_value; - uint32_t total_hits; - - measured_value.type = RC_VALUE_TYPE_NONE; - measured_from_hits = 0; - can_measure = 1; - total_hits = 0; - - eval_state->primed = 1; - set_valid = 1; - and_next = 1; - or_next = 0; - reset_next = 0; - eval_state->add_value.type = RC_VALUE_TYPE_NONE; - eval_state->add_hits = eval_state->add_address = 0; - - for (condition = self->conditions; condition != 0; condition = condition->next) { - if (condition->pause != processing_pause) - continue; - - /* STEP 1: process modifier conditions */ - switch (condition->type) { - case RC_CONDITION_ADD_SOURCE: - rc_evaluate_condition_value(&value, condition, eval_state); - rc_typed_value_add(&eval_state->add_value, &value); - eval_state->add_address = 0; - continue; - - case RC_CONDITION_SUB_SOURCE: - rc_evaluate_condition_value(&value, condition, eval_state); - rc_typed_value_negate(&value); - rc_typed_value_add(&eval_state->add_value, &value); - eval_state->add_address = 0; - continue; - - case RC_CONDITION_ADD_ADDRESS: - rc_evaluate_condition_value(&value, condition, eval_state); - rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED); - eval_state->add_address = value.value.u32; - continue; - - case RC_CONDITION_REMEMBER: - rc_evaluate_condition_value(&value, condition, eval_state); - rc_typed_value_add(&value, &eval_state->add_value); - eval_state->recall_value.type = value.type; - eval_state->recall_value.value = value.value; - eval_state->add_value.type = RC_VALUE_TYPE_NONE; - eval_state->add_address = 0; - continue; - - case RC_CONDITION_MEASURED: - if (condition->required_hits == 0 && can_measure) { - /* Measured condition without a hit target measures the value of the left operand */ - rc_evaluate_condition_value(&measured_value, condition, eval_state); - rc_typed_value_add(&measured_value, &eval_state->add_value); - } - break; - - default: - break; - } - - /* STEP 2: evaluate the current condition */ - condition->is_true = (char)rc_test_condition(condition, eval_state); - eval_state->add_value.type = RC_VALUE_TYPE_NONE; - eval_state->add_address = 0; - - /* apply logic flags and reset them for the next condition */ - cond_valid = condition->is_true; - cond_valid &= and_next; - cond_valid |= or_next; - and_next = 1; - or_next = 0; - - if (reset_next) { - /* previous ResetNextIf resets the hit count on this condition and prevents it from being true */ - if (condition->current_hits) - eval_state->was_cond_reset = 1; - - condition->current_hits = 0; - cond_valid = 0; - } - else if (cond_valid) { - /* true conditions should update hit count */ + if (cond_valid) { + /* true conditions should update their hit count */ eval_state->has_hits = 1; if (condition->required_hits == 0) { @@ -288,161 +431,299 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc eval_state->has_hits = 1; cond_valid = (condition->current_hits == condition->required_hits); } + } - /* STEP 3: handle logic flags */ + /* reset chained logic flags for the next condition */ + eval_state->and_next = 1; + eval_state->or_next = 0; + + return cond_valid; +} + +static uint32_t rc_condset_evaluate_total_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) { + uint32_t total_hits = condition->current_hits; + + if (condition->required_hits != 0) { + /* if the condition has a target hit count, we have to recalculate cond_valid including the AddHits counter */ + const int32_t signed_hits = (int32_t)condition->current_hits + eval_state->add_hits; + total_hits = (signed_hits >= 0) ? (uint32_t)signed_hits : 0; + } + else { + /* no target hit count. we can't tell if the add_hits value is from this frame or not, so ignore it. + complex condition will only be true if the current condition is true */ + } + + eval_state->add_hits = 0; + + return total_hits; +} + +static uint8_t rc_condset_evaluate_condition(rc_condition_t* condition, rc_eval_state_t* eval_state) { + uint8_t cond_valid = rc_condset_evaluate_condition_no_add_hits(condition, eval_state); + + if (eval_state->add_hits != 0 && condition->required_hits != 0) { + uint32_t total_hits = rc_condset_evaluate_total_hits(condition, eval_state); + cond_valid = (total_hits >= condition->required_hits); + } + + /* reset logic flags for the next condition */ + eval_state->reset_next = 0; + + return cond_valid; +} + +static void rc_condset_evaluate_standard(rc_condition_t* condition, rc_eval_state_t* eval_state) { + const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state); + + eval_state->is_true &= cond_valid; + eval_state->is_primed &= cond_valid; + + if (!cond_valid && eval_state->can_short_curcuit) + eval_state->stop_processing = 1; +} + +static void rc_condset_evaluate_pause_if(rc_condition_t* condition, rc_eval_state_t* eval_state) { + const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state); + + if (cond_valid) { + eval_state->is_paused = 1; + + /* set cannot be valid if it's paused */ + eval_state->is_true = eval_state->is_primed = 0; + + /* as soon as we find a PauseIf that evaluates to true, stop processing the rest of the group */ + eval_state->stop_processing = 1; + } + else if (condition->required_hits == 0) { + /* PauseIf didn't evaluate true, and doesn't have a HitCount, reset the HitCount to indicate the condition didn't match */ + condition->current_hits = 0; + } + else { + /* PauseIf has a HitCount that hasn't been met, ignore it for now. */ + } +} + +static void rc_condset_evaluate_reset_if(rc_condition_t* condition, rc_eval_state_t* eval_state) { + const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state); + + if (cond_valid) { + /* set cannot be valid if we've hit a reset condition */ + eval_state->is_true = eval_state->is_primed = 0; + + /* let caller know to reset all hit counts */ + eval_state->was_reset = 1; + + /* can stop processing once an active ResetIf is encountered */ + eval_state->stop_processing = 1; + } +} + +static void rc_condset_evaluate_trigger(rc_condition_t* condition, rc_eval_state_t* eval_state) { + const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state); + + eval_state->is_true &= cond_valid; +} + +static void rc_condset_evaluate_measured(rc_condition_t* condition, rc_eval_state_t* eval_state) { + if (condition->required_hits == 0) { + rc_condset_evaluate_standard(condition, eval_state); + + /* Measured condition without a hit target measures the value of the left operand */ + rc_evaluate_operand(&eval_state->measured_value, &condition->operand1, eval_state); + eval_state->measured_from_hits = 0; + } + else { + /* this largely mimicks rc_condset_evaluate_condition, but captures the total_hits */ + uint8_t cond_valid = rc_condset_evaluate_condition_no_add_hits(condition, eval_state); + const uint32_t total_hits = rc_condset_evaluate_total_hits(condition, eval_state); + + cond_valid = (total_hits >= condition->required_hits); + eval_state->is_true &= cond_valid; + eval_state->is_primed &= cond_valid; + + /* if there is a hit target, capture the current hits */ + eval_state->measured_value.value.u32 = total_hits; + eval_state->measured_value.type = RC_VALUE_TYPE_UNSIGNED; + eval_state->measured_from_hits = 1; + + /* reset logic flags for the next condition */ + eval_state->reset_next = 0; + } +} + +static void rc_condset_evaluate_measured_if(rc_condition_t* condition, rc_eval_state_t* eval_state) { + rc_condset_evaluate_standard(condition, eval_state); + + eval_state->can_measure &= condition->is_true; +} + +static void rc_condset_evaluate_add_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) { + rc_condset_evaluate_condition_no_add_hits(condition, eval_state); + + eval_state->add_hits += (int32_t)condition->current_hits; + + /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */ + eval_state->reset_next = 0; +} + +static void rc_condset_evaluate_sub_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) { + rc_condset_evaluate_condition_no_add_hits(condition, eval_state); + + eval_state->add_hits -= (int32_t)condition->current_hits; + + /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */ + eval_state->reset_next = 0; +} + +static void rc_condset_evaluate_reset_next_if(rc_condition_t* condition, rc_eval_state_t* eval_state) { + eval_state->reset_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state); +} + +static void rc_condset_evaluate_and_next(rc_condition_t* condition, rc_eval_state_t* eval_state) { + eval_state->and_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state); +} + +static void rc_condset_evaluate_or_next(rc_condition_t* condition, rc_eval_state_t* eval_state) { + eval_state->or_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state); +} + +static void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, + rc_eval_state_t* eval_state, int can_short_circuit) { + const rc_condition_t* condition_end = condition + num_conditions; + for (; condition < condition_end; ++condition) { switch (condition->type) { - case RC_CONDITION_ADD_HITS: - eval_state->add_hits += condition->current_hits; - reset_next = 0; /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */ - continue; - - case RC_CONDITION_SUB_HITS: - eval_state->add_hits -= condition->current_hits; - reset_next = 0; /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */ - continue; - - case RC_CONDITION_RESET_NEXT_IF: - reset_next = cond_valid; - continue; - - case RC_CONDITION_AND_NEXT: - and_next = cond_valid; - continue; - - case RC_CONDITION_OR_NEXT: - or_next = cond_valid; - continue; - - default: + case RC_CONDITION_STANDARD: + rc_condset_evaluate_standard(condition, eval_state); break; - } - - /* reset logic flags for next condition */ - reset_next = 0; - - /* STEP 4: calculate total hits */ - total_hits = condition->current_hits; - - if (eval_state->add_hits) { - if (condition->required_hits != 0) { - /* if the condition has a target hit count, we have to recalculate cond_valid including the AddHits counter */ - const int signed_hits = (int)condition->current_hits + eval_state->add_hits; - total_hits = (signed_hits >= 0) ? (unsigned)signed_hits : 0; - cond_valid = (total_hits >= condition->required_hits); - } - else { - /* no target hit count. we can't tell if the add_hits value is from this frame or not, so ignore it. - complex condition will only be true if the current condition is true */ - } - - eval_state->add_hits = 0; - } - - /* STEP 5: handle special flags */ - switch (condition->type) { case RC_CONDITION_PAUSE_IF: - /* as soon as we find a PauseIf that evaluates to true, stop processing the rest of the group */ - if (cond_valid) { - /* indirect memrefs are not updated as part of the rc_update_memref_values call. - * an active pause aborts processing of the remaining part of the pause subset and the entire non-pause subset. - * if the set has any indirect memrefs, manually update them now so the deltas are correct */ - if (self->has_indirect_memrefs) { - /* first, update any indirect memrefs in the remaining part of the pause subset */ - rc_condset_update_indirect_memrefs(condition->next, 1, eval_state); - - /* then, update all indirect memrefs in the non-pause subset */ - rc_condset_update_indirect_memrefs(self->conditions, 0, eval_state); - } - - return 1; - } - - /* if we make it to the end of the function, make sure we indicate that nothing matched. if we do find - a later PauseIf match, it'll automatically return true via the previous condition. */ - set_valid = 0; - - if (condition->required_hits == 0) { - /* PauseIf didn't evaluate true, and doesn't have a HitCount, reset the HitCount to indicate the condition didn't match */ - condition->current_hits = 0; - } - else { - /* PauseIf has a HitCount that hasn't been met, ignore it for now. */ - } - - continue; - + rc_condset_evaluate_pause_if(condition, eval_state); + break; case RC_CONDITION_RESET_IF: - if (cond_valid) { - eval_state->was_reset = 1; /* let caller know to reset all hit counts */ - set_valid = 0; /* cannot be valid if we've hit a reset condition */ - } - continue; - - case RC_CONDITION_MEASURED: - if (condition->required_hits != 0) { - /* if there's a hit target, capture the current hits for recording Measured value later */ - measured_from_hits = 1; - if (can_measure) { - measured_value.value.u32 = total_hits; - measured_value.type = RC_VALUE_TYPE_UNSIGNED; - } - } + rc_condset_evaluate_reset_if(condition, eval_state); break; - - case RC_CONDITION_MEASURED_IF: - if (!cond_valid) { - measured_value.value.u32 = 0; - measured_value.type = RC_VALUE_TYPE_UNSIGNED; - can_measure = 0; - } - break; - case RC_CONDITION_TRIGGER: - /* update truthiness of set, but do not update truthiness of primed state */ - set_valid &= cond_valid; - continue; - + rc_condset_evaluate_trigger(condition, eval_state); + break; + case RC_CONDITION_MEASURED: + rc_condset_evaluate_measured(condition, eval_state); + break; + case RC_CONDITION_MEASURED_IF: + rc_condset_evaluate_measured_if(condition, eval_state); + break; + case RC_CONDITION_ADD_SOURCE: + case RC_CONDITION_SUB_SOURCE: + case RC_CONDITION_ADD_ADDRESS: + case RC_CONDITION_REMEMBER: + /* these are handled by rc_modified_memref_t */ + break; + case RC_CONDITION_ADD_HITS: + rc_condset_evaluate_add_hits(condition, eval_state); + break; + case RC_CONDITION_SUB_HITS: + rc_condset_evaluate_sub_hits(condition, eval_state); + break; + case RC_CONDITION_RESET_NEXT_IF: + rc_condset_evaluate_reset_next_if(condition, eval_state); + break; + case RC_CONDITION_AND_NEXT: + rc_condset_evaluate_and_next(condition, eval_state); + break; + case RC_CONDITION_OR_NEXT: + rc_condset_evaluate_or_next(condition, eval_state); + break; default: + eval_state->stop_processing = 1; + eval_state->is_true = eval_state->is_primed = 0; break; } - /* STEP 5: update overall truthiness of set and primed state */ - eval_state->primed &= cond_valid; - set_valid &= cond_valid; + if (eval_state->stop_processing && can_short_circuit) + break; } +} - if (measured_value.type != RC_VALUE_TYPE_NONE) { - /* if no previous Measured value was captured, or the new one is greater, keep the new one */ - if (eval_state->measured_value.type == RC_VALUE_TYPE_NONE || - rc_typed_value_compare(&measured_value, &eval_state->measured_value, RC_OPERATOR_GT)) { - memcpy(&eval_state->measured_value, &measured_value, sizeof(measured_value)); - eval_state->measured_from_hits = (char)measured_from_hits; - } - } +rc_condition_t* rc_condset_get_conditions(rc_condset_t* self) { + if (self->conditions) + return RC_GET_TRAILING(self, rc_condset_with_trailing_conditions_t, rc_condition_t, conditions); - return set_valid; + return NULL; } int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state) { - if (self->conditions == 0) { - /* important: empty group must evaluate true */ - return 1; + rc_condition_t* conditions; + + /* reset the processing state before processing each condset. do not reset the result state. */ + eval_state->measured_value.type = RC_VALUE_TYPE_NONE; + eval_state->add_hits = 0; + eval_state->is_true = 1; + eval_state->is_primed = 1; + eval_state->is_paused = 0; + eval_state->can_measure = 1; + eval_state->measured_from_hits = 0; + eval_state->and_next = 1; + eval_state->or_next = 0; + eval_state->reset_next = 0; + eval_state->stop_processing = 0; + + /* the conditions array is allocated immediately after the rc_condset_t, without a separate pointer */ + conditions = rc_condset_get_conditions(self); + + if (self->num_pause_conditions) { + /* one or more Pause conditions exist. if any of them are true (eval_state->is_paused), + * stop processing this group */ + rc_test_condset_internal(conditions, self->num_pause_conditions, eval_state, 1); + + self->is_paused = eval_state->is_paused; + if (self->is_paused) { + /* condset is paused. stop processing immediately. */ + return 0; + } + + conditions += self->num_pause_conditions; } - /* initialize recall value so each condition set has a functionally new recall accumulator */ - eval_state->recall_value.type = RC_VALUE_TYPE_UNSIGNED; - eval_state->recall_value.value.u32 = 0; + if (self->num_reset_conditions) { + /* one or more Reset conditions exists. if any of them are true (eval_state->was_reset), + * we'll skip some of the later steps */ + rc_test_condset_internal(conditions, self->num_reset_conditions, eval_state, eval_state->can_short_curcuit); + conditions += self->num_reset_conditions; + } - if (self->has_pause) { - /* one or more Pause conditions exists, if any of them are true, stop processing this group */ - self->is_paused = (char)rc_test_condset_internal(self, 1, eval_state); - if (self->is_paused) { - eval_state->primed = 0; - return 0; + if (self->num_hittarget_conditions) { + /* one or more hit target conditions exists. these must be processed every frame, + * unless their hit count is going to be reset */ + if (!eval_state->was_reset) + rc_test_condset_internal(conditions, self->num_hittarget_conditions, eval_state, 0); + + conditions += self->num_hittarget_conditions; + } + + if (self->num_measured_conditions) { + /* the measured value must be calculated every frame, even if hit counts will be reset */ + rc_test_condset_internal(conditions, self->num_measured_conditions, eval_state, 0); + conditions += self->num_measured_conditions; + + if (eval_state->measured_value.type != RC_VALUE_TYPE_NONE) { + /* if a MeasuredIf was false (!eval_state->can_measure), or the measured + * value is a hitcount and a ResetIf is true, zero out the measured value */ + if (!eval_state->can_measure || + (eval_state->measured_from_hits && eval_state->was_reset)) { + eval_state->measured_value.type = RC_VALUE_TYPE_UNSIGNED; + eval_state->measured_value.value.u32 = 0; + } } } - return rc_test_condset_internal(self, 0, eval_state); + if (self->num_other_conditions) { + /* the remaining conditions only need to be evaluated if the rest of the condset is true */ + if (eval_state->is_true) + rc_test_condset_internal(conditions, self->num_other_conditions, eval_state, eval_state->can_short_curcuit); + /* something else is false. if we can't short circuit, and there wasn't a reset, we still need to evaluate these */ + else if (!eval_state->can_short_curcuit && !eval_state->was_reset) + rc_test_condset_internal(conditions, self->num_other_conditions, eval_state, eval_state->can_short_curcuit); + } + + return eval_state->is_true; } void rc_reset_condset(rc_condset_t* self) { diff --git a/dep/rcheevos/src/rcheevos/consoleinfo.c b/dep/rcheevos/src/rcheevos/consoleinfo.c index 98b43b6d0..0fa9f14db 100644 --- a/dep/rcheevos/src/rcheevos/consoleinfo.c +++ b/dep/rcheevos/src/rcheevos/consoleinfo.c @@ -72,6 +72,9 @@ const char* rc_console_name(uint32_t console_id) case RC_CONSOLE_FAIRCHILD_CHANNEL_F: return "Fairchild Channel F"; + case RC_CONSOLE_FAMICOM_DISK_SYSTEM: + return "Famicom Disk System"; + case RC_CONSOLE_FM_TOWNS: return "FM Towns"; @@ -380,14 +383,18 @@ static const rc_memory_regions_t rc_memory_regions_colecovision = { _rc_memory_r /* ===== Commodore 64 ===== */ /* https://www.c64-wiki.com/wiki/Memory_Map */ /* https://sta.c64.org/cbm64mem.html */ +/* NOTE: Several blocks of C64 memory can be bank-switched for ROM data (see https://www.c64-wiki.com/wiki/Bank_Switching). + * Achievement triggers rely on values changing, so we don't really need to look at the ROM data. + * The achievement logic assumes the RAM data is always present in the bankable blocks. As such, + * clients providing memory to achievements should always return the RAM values at the queried address. */ static const rc_memory_region_t _rc_memory_regions_c64[] = { { 0x000000U, 0x0003FFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" }, { 0x000400U, 0x0007FFU, 0x000400U, RC_MEMORY_TYPE_VIDEO_RAM, "Screen RAM" }, - { 0x000800U, 0x009FFFU, 0x000800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* BASIC Program Storage Area */ - { 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Machine Language Storage Area / BASIC ROM Area */ - { 0x00C000U, 0x00CFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Machine Language Storage Area */ - { 0x00D000U, 0x00DFFFU, 0x00D000U, RC_MEMORY_TYPE_SYSTEM_RAM, "I/O Area" }, /* also Character ROM */ - { 0x00E000U, 0x00FFFFU, 0x00E000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Machine Language Storage Area / Kernal ROM */ + { 0x000800U, 0x009FFFU, 0x000800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* BASIC area. $8000-$9FFF can bank to cartridge ROM */ + { 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* can bank to BASIC ROM or cartridge ROM */ + { 0x00C000U, 0x00CFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, + { 0x00D000U, 0x00DFFFU, 0x00D000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* can bank to I/O Area or character ROM */ + { 0x00E000U, 0x00FFFFU, 0x00E000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* can bank to kernel ROM */ }; static const rc_memory_regions_t rc_memory_regions_c64 = { _rc_memory_regions_c64, 7 }; @@ -423,6 +430,24 @@ static const rc_memory_region_t _rc_memory_regions_fairchild_channel_f[] = { }; static const rc_memory_regions_t rc_memory_regions_fairchild_channel_f = { _rc_memory_regions_fairchild_channel_f, 4 }; +/* ===== Famicon Disk System ===== */ +/* https://fms.komkon.org/EMUL8/NES.html */ +static const rc_memory_region_t _rc_memory_regions_famicom_disk_system[] = { + { 0x0000U, 0x07FFU, 0x0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, + { 0x0800U, 0x0FFFU, 0x0000U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirror RAM" }, /* duplicates memory from $0000-$07FF */ + { 0x1000U, 0x17FFU, 0x0000U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirror RAM" }, /* duplicates memory from $0000-$07FF */ + { 0x1800U, 0x1FFFU, 0x0000U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirror RAM" }, /* duplicates memory from $0000-$07FF */ + { 0x2000U, 0x2007U, 0x2000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "PPU Register" }, + { 0x2008U, 0x3FFFU, 0x2008U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirrored PPU Register" }, /* repeats every 8 bytes */ + { 0x4000U, 0x4017U, 0x4000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "APU and I/O register" }, + { 0x4018U, 0x401FU, 0x4018U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "APU and I/O test register" }, + { 0x4020U, 0x40FFU, 0x4020U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "FDS I/O registers"}, + { 0x4100U, 0x5FFFU, 0x4100U, RC_MEMORY_TYPE_READONLY, "Cartridge data"}, /* varies by mapper */ + { 0x6000U, 0xDFFFU, 0x6000U, RC_MEMORY_TYPE_SYSTEM_RAM, "FDS RAM"}, + { 0xE000U, 0xFFFFU, 0xE000U, RC_MEMORY_TYPE_READONLY, "FDS BIOS ROM"}, +}; +static const rc_memory_regions_t rc_memory_regions_famicom_disk_system = { _rc_memory_regions_famicom_disk_system, 12 }; + /* ===== GameBoy / MegaDuck ===== */ static const rc_memory_region_t _rc_memory_regions_gameboy[] = { { 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt vector" }, @@ -694,18 +719,6 @@ static const rc_memory_region_t _rc_memory_regions_nes[] = { { 0x4020U, 0x5FFFU, 0x4020U, RC_MEMORY_TYPE_READONLY, "Cartridge data"}, /* varies by mapper */ { 0x6000U, 0x7FFFU, 0x6000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM"}, { 0x8000U, 0xFFFFU, 0x8000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM"}, - - /* NOTE: these are the correct mappings for FDS: https://fms.komkon.org/EMUL8/NES.html - * 0x6000-0xDFFF is RAM on the FDS system and 0xE000-0xFFFF is FDS BIOS. - * If the core implements a memory map, we should still be able to translate the addresses - * correctly as we only use the classifications when a memory map is not provided - - { 0x4020U, 0x40FFU, 0x4020U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "FDS I/O registers"}, - { 0x4100U, 0x5FFFU, 0x4100U, RC_MEMORY_TYPE_READONLY, "Cartridge data"}, // varies by mapper - { 0x6000U, 0xDFFFU, 0x6000U, RC_MEMORY_TYPE_SYSTEM_RAM, "FDS RAM"}, - { 0xE000U, 0xFFFFU, 0xE000U, RC_MEMORY_TYPE_READONLY, "FDS BIOS ROM"}, - - */ }; static const rc_memory_regions_t rc_memory_regions_nes = { _rc_memory_regions_nes, 11 }; @@ -868,10 +881,18 @@ static const rc_memory_regions_t rc_memory_regions_scv = { _rc_memory_regions_sc /* ===== Super Nintendo ===== */ /* https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map#LoROM */ static const rc_memory_region_t _rc_memory_regions_snes[] = { - { 0x000000U, 0x01FFFFU, 0x7E0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, - { 0x020000U, 0x03FFFFU, 0xFE0000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" } + { 0x000000U, 0x01FFFFU, 0x07E0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, + /* Cartridge RAM here could be in a variety of places in SNES memory, depending on the ROM type. + * Due to this, we place Cartridge RAM outside of the possible native addressing space. + * Note that this also covers SA-1 BW-RAM (which is exposed as RETRO_MEMORY_SAVE_RAM for libretro). + */ + { 0x020000U, 0x09FFFFU, 0x1000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }, + /* I-RAM on the SA-1 is normally at 0x003000. However, this address typically just has a mirror of System RAM for other ROM types. + * To avoid conflicts, don't use 0x003000, instead map it outside of the possible native addressing space. + */ + { 0x0A0000U, 0x0A07FFU, 0x1080000U, RC_MEMORY_TYPE_SYSTEM_RAM, "I-RAM (SA-1)" } }; -static const rc_memory_regions_t rc_memory_regions_snes = { _rc_memory_regions_snes, 2 }; +static const rc_memory_regions_t rc_memory_regions_snes = { _rc_memory_regions_snes, 3 }; /* ===== Thomson TO8 ===== */ /* https://github.com/mamedev/mame/blob/master/src/mame/drivers/thomson.cpp#L1617 */ @@ -1046,7 +1067,10 @@ const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id) case RC_CONSOLE_FAIRCHILD_CHANNEL_F: return &rc_memory_regions_fairchild_channel_f; - + + case RC_CONSOLE_FAMICOM_DISK_SYSTEM: + return &rc_memory_regions_famicom_disk_system; + case RC_CONSOLE_GAMEBOY: return &rc_memory_regions_gameboy; diff --git a/dep/rcheevos/src/rcheevos/lboard.c b/dep/rcheevos/src/rcheevos/lboard.c index 47e05f273..dbe8a7d07 100644 --- a/dep/rcheevos/src/rcheevos/lboard.c +++ b/dep/rcheevos/src/rcheevos/lboard.c @@ -29,7 +29,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s if (*memaddr && *memaddr != ':') { found |= RC_LBOARD_START; rc_parse_trigger_internal(&self->start, &memaddr, parse); - self->start.memrefs = 0; } } else if ((memaddr[0] == 'c' || memaddr[0] == 'C') && @@ -44,7 +43,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s if (*memaddr && *memaddr != ':') { found |= RC_LBOARD_CANCEL; rc_parse_trigger_internal(&self->cancel, &memaddr, parse); - self->cancel.memrefs = 0; } } else if ((memaddr[0] == 's' || memaddr[0] == 'S') && @@ -59,7 +57,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s if (*memaddr && *memaddr != ':') { found |= RC_LBOARD_SUBMIT; rc_parse_trigger_internal(&self->submit, &memaddr, parse); - self->submit.memrefs = 0; } } else if ((memaddr[0] == 'v' || memaddr[0] == 'V') && @@ -74,7 +71,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s if (*memaddr && *memaddr != ':') { found |= RC_LBOARD_VALUE; rc_parse_value_internal(&self->value, &memaddr, parse); - self->value.memrefs = 0; } } else if ((memaddr[0] == 'p' || memaddr[0] == 'P') && @@ -91,7 +87,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s self->progress = RC_ALLOC(rc_value_t, parse); rc_parse_value_internal(self->progress, &memaddr, parse); - self->progress->memrefs = 0; } } @@ -130,44 +125,55 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s } self->state = RC_LBOARD_STATE_WAITING; + self->has_memrefs = 0; } int rc_lboard_size(const char* memaddr) { - rc_lboard_t* self; - rc_parse_state_t parse; - rc_memref_t* first_memref; - rc_init_parse_state(&parse, 0, 0, 0); - rc_init_parse_state_memrefs(&parse, &first_memref); + rc_lboard_with_memrefs_t* lboard; + rc_preparse_state_t preparse; + rc_init_preparse_state(&preparse, NULL, 0); - self = RC_ALLOC(rc_lboard_t, &parse); - rc_parse_lboard_internal(self, memaddr, &parse); + lboard = RC_ALLOC(rc_lboard_with_memrefs_t, &preparse.parse); + rc_parse_lboard_internal(&lboard->lboard, memaddr, &preparse.parse); + rc_preparse_alloc_memrefs(NULL, &preparse); - rc_destroy_parse_state(&parse); - return parse.offset; + rc_destroy_preparse_state(&preparse); + return preparse.parse.offset; } rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) { - rc_lboard_t* self; - rc_parse_state_t parse; + rc_lboard_with_memrefs_t* lboard; + rc_preparse_state_t preparse; if (!buffer || !memaddr) return 0; - rc_init_parse_state(&parse, buffer, L, funcs_ndx); + rc_init_preparse_state(&preparse, L, funcs_ndx); + lboard = RC_ALLOC(rc_lboard_with_memrefs_t, &preparse.parse); + rc_parse_lboard_internal(&lboard->lboard, memaddr, &preparse.parse); - self = RC_ALLOC(rc_lboard_t, &parse); - rc_init_parse_state_memrefs(&parse, &self->memrefs); + rc_reset_parse_state(&preparse.parse, buffer, L, funcs_ndx); + lboard = RC_ALLOC(rc_lboard_with_memrefs_t, &preparse.parse); + rc_preparse_alloc_memrefs(&lboard->memrefs, &preparse); - rc_parse_lboard_internal(self, memaddr, &parse); + rc_parse_lboard_internal(&lboard->lboard, memaddr, &preparse.parse); + lboard->lboard.has_memrefs = 1; - rc_destroy_parse_state(&parse); - return (parse.offset >= 0) ? self : 0; + rc_destroy_preparse_state(&preparse); + return (preparse.parse.offset >= 0) ? &lboard->lboard : NULL; +} + +static void rc_update_lboard_memrefs(rc_lboard_t* self, rc_peek_t peek, void* ud) { + if (self->has_memrefs) { + rc_lboard_with_memrefs_t* lboard = (rc_lboard_with_memrefs_t*)self; + rc_update_memref_values(&lboard->memrefs, peek, ud); + } } int rc_evaluate_lboard(rc_lboard_t* self, int32_t* value, rc_peek_t peek, void* peek_ud, lua_State* L) { int start_ok, cancel_ok, submit_ok; - rc_update_memref_values(self->memrefs, peek, peek_ud); + rc_update_lboard_memrefs(self, peek, peek_ud); if (self->state == RC_LBOARD_STATE_INACTIVE || self->state == RC_LBOARD_STATE_DISABLED) return RC_LBOARD_STATE_INACTIVE; diff --git a/dep/rcheevos/src/rcheevos/memref.c b/dep/rcheevos/src/rcheevos/memref.c index 0cbec67b8..ca94077ad 100644 --- a/dep/rcheevos/src/rcheevos/memref.c +++ b/dep/rcheevos/src/rcheevos/memref.c @@ -6,41 +6,232 @@ #define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF -rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, uint32_t address, uint8_t size, uint8_t is_indirect) { - rc_memref_t** next_memref; - rc_memref_t* memref; +rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, uint32_t address, uint8_t size) { + rc_memref_list_t* memref_list = NULL; + rc_memref_t* memref = NULL; + int i; - if (!is_indirect) { - /* attempt to find an existing memref that can be shared */ - next_memref = parse->first_memref; - while (*next_memref) { - memref = *next_memref; - if (!memref->value.is_indirect && memref->address == address && memref->value.size == size) - return memref; + for (i = 0; i < 2; i++) { + if (i == 0) { + if (!parse->existing_memrefs) + continue; - next_memref = &memref->next; + memref_list = &parse->existing_memrefs->memrefs; + } + else { + memref_list = &parse->memrefs->memrefs; } - /* no match found, create a new entry */ - memref = RC_ALLOC_SCRATCH(rc_memref_t, parse); - *next_memref = memref; + do + { + const rc_memref_t* memref_stop; + + memref = memref_list->items; + memref_stop = memref + memref_list->count; + + for (; memref < memref_stop; ++memref) { + if (memref->address == address && memref->value.size == size) + return memref; + } + + if (!memref_list->next) + break; + + memref_list = memref_list->next; + } while (1); } - else { - /* indirect references always create a new entry because we can't guarantee that the - * indirection amount will be the same between references. because they aren't shared, - * don't bother putting them in the chain. - */ - memref = RC_ALLOC(rc_memref_t, parse); + + /* no match found, find a place to put the new entry */ + memref_list = &parse->memrefs->memrefs; + while (memref_list->count == memref_list->capacity && memref_list->next) + memref_list = memref_list->next; + + /* create a new entry */ + if (memref_list->count < memref_list->capacity) { + memref = &memref_list->items[memref_list->count++]; + } else { + const int32_t old_offset = parse->offset; + + if (memref_list->capacity != 0) { + memref_list = memref_list->next = RC_ALLOC_SCRATCH(rc_memref_list_t, parse); + memref_list->next = NULL; + } + + memref_list->items = RC_ALLOC_ARRAY_SCRATCH(rc_memref_t, 8, parse); + memref_list->count = 1; + memref_list->capacity = 8; + memref_list->allocated = 0; + + memref = memref_list->items; + + /* in preparse mode, don't count this memory, we'll do a single allocation once we have + * the final total */ + if (!parse->buffer) + parse->offset = old_offset; } memset(memref, 0, sizeof(*memref)); - memref->address = address; + memref->value.memref_type = RC_MEMREF_TYPE_MEMREF; + memref->value.type = RC_VALUE_TYPE_UNSIGNED; memref->value.size = size; - memref->value.is_indirect = is_indirect; + memref->address = address; return memref; } +rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t size, const rc_operand_t* parent, + uint8_t modifier_type, const rc_operand_t* modifier) { + rc_modified_memref_list_t* modified_memref_list = NULL; + rc_modified_memref_t* modified_memref = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + if (i == 0) { + if (!parse->existing_memrefs) + continue; + + modified_memref_list = &parse->existing_memrefs->modified_memrefs; + } + else { + modified_memref_list = &parse->memrefs->modified_memrefs; + } + + do { + const rc_modified_memref_t* memref_stop; + + modified_memref = modified_memref_list->items; + memref_stop = modified_memref + modified_memref_list->count; + + for (; modified_memref < memref_stop; ++modified_memref) { + if (modified_memref->memref.value.size == size && + modified_memref->modifier_type == modifier_type && + rc_operands_are_equal(&modified_memref->parent, parent) && + rc_operands_are_equal(&modified_memref->modifier, modifier)) { + return modified_memref; + } + } + + if (!modified_memref_list->next) + break; + + modified_memref_list = modified_memref_list->next; + } while (1); + } + + /* no match found, find a place to put the new entry */ + modified_memref_list = &parse->memrefs->modified_memrefs; + while (modified_memref_list->count == modified_memref_list->capacity && modified_memref_list->next) + modified_memref_list = modified_memref_list->next; + + /* create a new entry */ + if (modified_memref_list->count < modified_memref_list->capacity) { + modified_memref = &modified_memref_list->items[modified_memref_list->count++]; + } else { + const int32_t old_offset = parse->offset; + + if (modified_memref_list->capacity != 0) { + modified_memref_list = modified_memref_list->next = RC_ALLOC_SCRATCH(rc_modified_memref_list_t, parse); + modified_memref_list->next = NULL; + } + + modified_memref_list->items = RC_ALLOC_ARRAY_SCRATCH(rc_modified_memref_t, 8, parse); + modified_memref_list->count = 1; + modified_memref_list->capacity = 8; + modified_memref_list->allocated = 0; + + modified_memref = modified_memref_list->items; + + /* in preparse mode, don't count this memory, we'll do a single allocation once we have + * the final total */ + if (!parse->buffer) + parse->offset = old_offset; + } + + memset(modified_memref, 0, sizeof(*modified_memref)); + modified_memref->memref.value.memref_type = RC_MEMREF_TYPE_MODIFIED_MEMREF; + modified_memref->memref.value.size = size; + modified_memref->memref.value.type = (size == RC_MEMSIZE_FLOAT) ? RC_VALUE_TYPE_FLOAT : RC_VALUE_TYPE_UNSIGNED; + memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent)); + memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier)); + modified_memref->modifier_type = modifier_type; + modified_memref->memref.address = rc_operand_is_memref(modifier) ? modifier->value.memref->address : modifier->value.num; + + return modified_memref; +} + +void rc_memrefs_init(rc_memrefs_t* memrefs) +{ + memset(memrefs, 0, sizeof(*memrefs)); + + memrefs->memrefs.capacity = 32; + memrefs->memrefs.items = + (rc_memref_t*)malloc(memrefs->memrefs.capacity * sizeof(rc_memref_t)); + memrefs->memrefs.allocated = 1; + + memrefs->modified_memrefs.capacity = 16; + memrefs->modified_memrefs.items = + (rc_modified_memref_t*)malloc(memrefs->modified_memrefs.capacity * sizeof(rc_modified_memref_t)); + memrefs->modified_memrefs.allocated = 1; +} + +void rc_memrefs_destroy(rc_memrefs_t* memrefs) +{ + rc_memref_list_t* memref_list = &memrefs->memrefs; + rc_modified_memref_list_t* modified_memref_list = &memrefs->modified_memrefs; + + do { + rc_memref_list_t* current_memref_list = memref_list; + memref_list = memref_list->next; + + if (current_memref_list->allocated) { + if (current_memref_list->items) + free(current_memref_list->items); + + if (current_memref_list != &memrefs->memrefs) + free(current_memref_list); + } + } while (memref_list); + + do { + rc_modified_memref_list_t* current_modified_memref_list = modified_memref_list; + modified_memref_list = modified_memref_list->next; + + if (current_modified_memref_list->allocated) { + if (current_modified_memref_list->items) + free(current_modified_memref_list->items); + + if (current_modified_memref_list != &memrefs->modified_memrefs) + free(current_modified_memref_list); + } + } while (modified_memref_list); + + free(memrefs); +} + +uint32_t rc_memrefs_count_memrefs(const rc_memrefs_t* memrefs) +{ + uint32_t count = 0; + const rc_memref_list_t* memref_list = &memrefs->memrefs; + while (memref_list) { + count += memref_list->count; + memref_list = memref_list->next; + } + + return count; +} + +uint32_t rc_memrefs_count_modified_memrefs(const rc_memrefs_t* memrefs) +{ + uint32_t count = 0; + const rc_modified_memref_list_t* modified_memref_list = &memrefs->modified_memrefs; + while (modified_memref_list) { + count += modified_memref_list->count; + modified_memref_list = modified_memref_list->next; + } + + return count; +} + int rc_parse_memref(const char** memaddr, uint8_t* size, uint32_t* address) { const char* aux = *memaddr; char* end; @@ -77,7 +268,12 @@ int rc_parse_memref(const char** memaddr, uint8_t* size, uint32_t* address) { /* case 'y': case 'Y': 64 bit? */ /* case 'z': case 'Z': 128 bit? */ - case '0': case '1': case '2': case '3': case '4': + case '0': + if (*aux == 'x') /* user mistyped an extra 0x: 0x0xabcd */ + return RC_INVALID_MEMORY_OPERAND; + /* fallthrough */ + + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': @@ -485,19 +681,12 @@ void rc_update_memref_value(rc_memref_value_t* memref, uint32_t new_value) { } } -void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud) { - while (memref) { - /* indirect memory references are not shared and will be updated in rc_get_memref_value */ - if (!memref->value.is_indirect) - rc_update_memref_value(&memref->value, rc_peek_value(memref->address, memref->value.size, peek, ud)); +void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memrefs_t* memrefs) +{ + if (memrefs) + memset(memrefs, 0, sizeof(*memrefs)); - memref = memref->next; - } -} - -void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs) { - parse->first_memref = memrefs; - *memrefs = 0; + parse->memrefs = memrefs; } static uint32_t rc_get_memref_value_value(const rc_memref_value_t* memref, int operand_type) { @@ -520,12 +709,68 @@ static uint32_t rc_get_memref_value_value(const rc_memref_value_t* memref, int o } } -uint32_t rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state) { - /* if this is an indirect reference, handle the indirection. */ - if (memref->value.is_indirect) { - const uint32_t new_address = memref->address + eval_state->add_address; - rc_update_memref_value(&memref->value, rc_peek_value(new_address, memref->value.size, eval_state->peek, eval_state->peek_userdata)); +void rc_get_memref_value(rc_typed_value_t* value, rc_memref_t* memref, int operand_type) { + value->type = memref->value.type; + value->value.u32 = rc_get_memref_value_value(&memref->value, operand_type); +} + +uint32_t rc_get_modified_memref_value(const rc_modified_memref_t* memref, rc_peek_t peek, void* ud) { + rc_typed_value_t value, modifier; + + rc_evaluate_operand(&value, &memref->parent, NULL); + rc_evaluate_operand(&modifier, &memref->modifier, NULL); + + switch (memref->modifier_type) { + case RC_OPERATOR_INDIRECT_READ: + rc_typed_value_add(&value, &modifier); + rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED); + value.value.u32 = rc_peek_value(value.value.u32, memref->memref.value.size, peek, ud); + value.type = memref->memref.value.type; + break; + + case RC_OPERATOR_SUB_PARENT: + rc_typed_value_negate(&value); + rc_typed_value_add(&value, &modifier); + rc_typed_value_convert(&value, memref->memref.value.type); + break; + + default: + rc_typed_value_combine(&value, &modifier, memref->modifier_type); + rc_typed_value_convert(&value, memref->memref.value.type); + break; } - return rc_get_memref_value_value(&memref->value, operand_type); + return value.value.u32; +} + +void rc_update_memref_values(rc_memrefs_t* memrefs, rc_peek_t peek, void* ud) { + rc_memref_list_t* memref_list; + rc_modified_memref_list_t* modified_memref_list; + + memref_list = &memrefs->memrefs; + do + { + rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_stop = memref + memref_list->count; + + for (; memref < memref_stop; ++memref) { + if (memref->value.type != RC_VALUE_TYPE_NONE) + rc_update_memref_value(&memref->value, rc_peek_value(memref->address, memref->value.size, peek, ud)); + } + + memref_list = memref_list->next; + } while (memref_list); + + modified_memref_list = &memrefs->modified_memrefs; + if (modified_memref_list->count) { + do { + rc_modified_memref_t* modified_memref = modified_memref_list->items; + const rc_modified_memref_t* modified_memref_stop = modified_memref + modified_memref_list->count; + + for (; modified_memref < modified_memref_stop; ++modified_memref) + rc_update_memref_value(&modified_memref->memref.value, rc_get_modified_memref_value(modified_memref, peek, ud)); + + modified_memref_list = modified_memref_list->next; + } while (modified_memref_list); + } } diff --git a/dep/rcheevos/src/rcheevos/operand.c b/dep/rcheevos/src/rcheevos/operand.c index 09ac65ee1..4325df07c 100644 --- a/dep/rcheevos/src/rcheevos/operand.c +++ b/dep/rcheevos/src/rcheevos/operand.c @@ -61,11 +61,13 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par #endif /* RC_DISABLE_LUA */ self->type = RC_OPERAND_LUA; + self->size = RC_MEMSIZE_32_BITS; + self->memref_access_type = RC_OPERAND_ADDRESS; *memaddr = aux; return RC_OK; } -static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr) { +static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) { const char* aux = *memaddr; size_t i; char varName[RC_VALUE_MAX_NAME_LENGTH + 1] = { 0 }; @@ -86,6 +88,15 @@ static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr) { ++aux; if (strcmp(varName, "recall") == 0) { + if (parse->remember.type == RC_OPERAND_NONE) { + self->value.memref = NULL; + self->size = RC_MEMSIZE_32_BITS; + self->memref_access_type = RC_OPERAND_ADDRESS; + } + else { + memcpy(self, &parse->remember, sizeof(*self)); + self->memref_access_type = self->type; + } self->type = RC_OPERAND_RECALL; } else { /* process named variable when feature is available.*/ @@ -96,7 +107,7 @@ static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr) { return RC_OK; } -static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, uint8_t is_indirect) { +static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) { const char* aux = *memaddr; uint32_t address; uint8_t size; @@ -128,6 +139,8 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_ break; } + self->memref_access_type = self->type; + ret = rc_parse_memref(&aux, &self->size, &address); if (ret != RC_OK) return ret; @@ -137,13 +150,28 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_ /* if the shared size differs from the requested size and it's a prior operation, we * have to check to make sure both sizes use the same mask, or the prior value may be * updated when bits outside the mask are modified, which would make it look like the - * current value once the mask is applied. if the mask differs, create a new + * current value once the mask is applied. if the mask differs, create a new * non-shared record for tracking the prior data. */ if (rc_memref_mask(size) != rc_memref_mask(self->size)) size = self->size; } - self->value.memref = rc_alloc_memref(parse, address, size, is_indirect); + if (parse->indirect_parent.type != RC_OPERAND_NONE) { + if (parse->indirect_parent.type == RC_OPERAND_CONST) { + self->value.memref = rc_alloc_memref(parse, address + parse->indirect_parent.value.num, size); + } + else { + rc_operand_t offset; + rc_operand_set_const(&offset, address); + + self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse, + size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset); + } + } + else { + self->value.memref = rc_alloc_memref(parse, address, size); + } + if (parse->offset < 0) return parse->offset; @@ -151,7 +179,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_ return RC_OK; } -int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indirect, rc_parse_state_t* parse) { +int rc_parse_operand(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) { const char* aux = *memaddr; char* end; int ret; @@ -159,8 +187,6 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire int negative; int allow_decimal = 0; - self->size = RC_MEMSIZE_32_BITS; - switch (*aux) { case 'h': case 'H': /* hex constant */ if (aux[2] == 'x' || aux[2] == 'X') { @@ -175,15 +201,14 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire if (value > 0xffffffffU) value = 0xffffffffU; - self->type = RC_OPERAND_CONST; - self->value.num = (unsigned)value; + rc_operand_set_const(self, (unsigned)value); aux = end; break; case 'f': case 'F': /* floating point constant */ if (isalpha((unsigned char)aux[1])) { - ret = rc_parse_operand_memory(self, &aux, parse, is_indirect); + ret = rc_parse_operand_memory(self, &aux, parse); if (ret < 0) return ret; @@ -211,6 +236,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire /* custom parser for decimal values to ignore locale */ unsigned long shift = 1; unsigned long fraction = 0; + double dbl_val; aux = end + 1; if (*aux < '0' || *aux > '9') @@ -231,19 +257,19 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire /* non-zero fractional part, convert to double and merge in integer portion */ const double dbl_fraction = ((double)fraction) / ((double)shift); if (negative) - self->value.dbl = ((double)(-((long)value))) - dbl_fraction; + dbl_val = ((double)(-((long)value))) - dbl_fraction; else - self->value.dbl = (double)value + dbl_fraction; + dbl_val = (double)value + dbl_fraction; } else { /* fractional part is 0, just convert the integer portion */ if (negative) - self->value.dbl = (double)(-((long)value)); + dbl_val = (double)(-((long)value)); else - self->value.dbl = (double)value; + dbl_val = (double)value; } - self->type = RC_OPERAND_FP; + rc_operand_set_float_const(self, dbl_val); } else { /* not a floating point value, make sure something was read and advance the read pointer */ @@ -255,17 +281,15 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire if (value > 0x7fffffffU) value = 0x7fffffffU; - self->type = RC_OPERAND_CONST; - if (negative) - self->value.num = (unsigned)(-((long)value)); + rc_operand_set_const(self, (unsigned)(-((long)value))); else - self->value.num = (unsigned)value; + rc_operand_set_const(self, (unsigned)value); } break; case '{': /* variable */ ++aux; - ret = rc_parse_operand_variable(self, &aux); + ret = rc_parse_operand_variable(self, &aux, parse); if (ret < 0) return ret; @@ -275,7 +299,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire if (aux[1] == 'x' || aux[1] == 'X') { /* hex integer constant */ /* fallthrough */ /* to default */ default: - ret = rc_parse_operand_memory(self, &aux, parse, is_indirect); + ret = rc_parse_operand_memory(self, &aux, parse); if (ret < 0) return ret; @@ -292,8 +316,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire if (value > 0xffffffffU) value = 0xffffffffU; - self->type = RC_OPERAND_CONST; - self->value.num = (unsigned)value; + rc_operand_set_const(self, (unsigned)value); aux = end; break; @@ -332,8 +355,74 @@ static int rc_luapeek(lua_State* L) { #endif /* RC_DISABLE_LUA */ -int rc_operand_is_float_memref(const rc_operand_t* self) { - switch (self->size) { +void rc_operand_set_const(rc_operand_t* self, uint32_t value) { + self->size = RC_MEMSIZE_32_BITS; + self->type = RC_OPERAND_CONST; + self->memref_access_type = RC_OPERAND_NONE; + self->value.num = value; +} + +void rc_operand_set_float_const(rc_operand_t* self, double value) { + self->size = RC_MEMSIZE_FLOAT; + self->type = RC_OPERAND_FP; + self->memref_access_type = RC_OPERAND_NONE; + self->value.dbl = value; +} + +int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) { + if (left->type != right->type) + return 0; + + switch (left->type) { + case RC_OPERAND_CONST: + return (left->value.num == right->value.num); + case RC_OPERAND_FP: + return (left->value.dbl == right->value.dbl); + case RC_OPERAND_RECALL: + return 1; + default: + break; + } + + /* comparing two memrefs - look for exact matches on type and size */ + if (left->size != right->size || left->value.memref->value.memref_type != right->value.memref->value.memref_type) + return 0; + + switch (left->value.memref->value.memref_type) { + case RC_MEMREF_TYPE_MODIFIED_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; + return (left_memref->modifier_type == right_memref->modifier_type && + rc_operands_are_equal(&left_memref->parent, &right_memref->parent) && + rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier)); + } + + default: + return (left->value.memref->address == right->value.memref->address && + left->value.memref->value.size == right->value.memref->value.size); + } +} + +int rc_operator_is_modifying(int oper) { + switch (oper) { + case RC_OPERATOR_AND: + case RC_OPERATOR_XOR: + case RC_OPERATOR_DIV: + case RC_OPERATOR_MULT: + case RC_OPERATOR_MOD: + case RC_OPERATOR_ADD: + case RC_OPERATOR_SUB: + case RC_OPERATOR_NONE: /* NONE operator implies "* 1" */ + return 1; + + default: + return 0; + } +} + +static int rc_memsize_is_float(uint8_t size) { + switch (size) { case RC_MEMSIZE_FLOAT: case RC_MEMSIZE_FLOAT_BE: case RC_MEMSIZE_DOUBLE32: @@ -347,8 +436,24 @@ int rc_operand_is_float_memref(const rc_operand_t* self) { } } -int rc_operand_is_memref(const rc_operand_t* self) { - switch (self->type) { +int rc_operand_is_float_memref(const rc_operand_t* self) { + if (!rc_operand_is_memref(self)) + return 0; + + if (self->type == RC_OPERAND_RECALL) + return rc_memsize_is_float(self->memref_access_type); + + if (self->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) { + const rc_modified_memref_t* memref = (const rc_modified_memref_t*)self->value.memref; + if (memref->modifier_type != RC_OPERATOR_INDIRECT_READ) + return rc_memsize_is_float(self->value.memref->value.size); + } + + return rc_memsize_is_float(self->size); +} + +int rc_operand_type_is_memref(uint8_t type) { + switch (type) { case RC_OPERAND_CONST: case RC_OPERAND_FP: case RC_OPERAND_LUA: @@ -360,6 +465,10 @@ int rc_operand_is_memref(const rc_operand_t* self) { } } +int rc_operand_is_memref(const rc_operand_t* self) { + return rc_operand_type_is_memref(self->type); +} + int rc_operand_is_recall(const rc_operand_t* self) { switch (self->type) { case RC_OPERAND_RECALL: @@ -377,7 +486,7 @@ int rc_operand_is_float(const rc_operand_t* self) { return rc_operand_is_float_memref(self); } -uint32_t rc_transform_operand_value(uint32_t value, const rc_operand_t* self) { +static uint32_t rc_transform_operand_value(uint32_t value, const rc_operand_t* self) { switch (self->type) { case RC_OPERAND_BCD: @@ -465,7 +574,44 @@ uint32_t rc_transform_operand_value(uint32_t value, const rc_operand_t* self) { return value; } -void rc_evaluate_operand(rc_typed_value_t* result, rc_operand_t* self, rc_eval_state_t* eval_state) { +void rc_operand_addsource(rc_operand_t* self, rc_parse_state_t* parse, uint8_t new_size) { + rc_modified_memref_t* modified_memref; + + if (rc_operand_is_memref(&parse->addsource_parent)) { + rc_operand_t modifier; + + if ((self->type == RC_OPERAND_DELTA || self->type == RC_OPERAND_PRIOR) && + self->type == parse->addsource_parent.type) { + /* if adding prev(x) and prev(y), just add x and y and take the prev of that */ + memcpy(&modifier, self, sizeof(modifier)); + modifier.type = parse->addsource_parent.type = RC_OPERAND_ADDRESS; + + modified_memref = rc_alloc_modified_memref(parse, + new_size, &parse->addsource_parent, parse->addsource_oper, &modifier); + } + else { + modified_memref = rc_alloc_modified_memref(parse, + new_size, &parse->addsource_parent, parse->addsource_oper, self); + } + } + else { + /* N + A => A + N */ + /* -N + A => A - N */ + modified_memref = rc_alloc_modified_memref(parse, + new_size, &parse->addsource_parent, parse->addsource_oper, self); + } + + self->value.memref = (rc_memref_t*)modified_memref; + + /* if adding a constant, change the type to be address (current value) */ + if (!rc_operand_is_memref(self)) + self->type = self->memref_access_type = RC_OPERAND_ADDRESS; + + /* result of an AddSource operation is always a 32-bit integer (even if parent or modifier is a float) */ + self->size = RC_MEMSIZE_32_BITS; +} + +void rc_evaluate_operand(rc_typed_value_t* result, const rc_operand_t* self, rc_eval_state_t* eval_state) { #ifndef RC_DISABLE_LUA rc_luapeek_t luapeek; #endif /* RC_DISABLE_LUA */ @@ -510,16 +656,28 @@ void rc_evaluate_operand(rc_typed_value_t* result, rc_operand_t* self, rc_eval_s #endif /* RC_DISABLE_LUA */ - break; - - case RC_OPERAND_RECALL: - result->type = eval_state->recall_value.type; - result->value = eval_state->recall_value.value; return; + case RC_OPERAND_RECALL: + if (!rc_operand_type_is_memref(self->memref_access_type)) { + rc_operand_t recall; + memcpy(&recall, self, sizeof(recall)); + recall.type = self->memref_access_type; + rc_evaluate_operand(result, &recall, eval_state); + return; + } + + if (!self->value.memref) { + result->type = RC_VALUE_TYPE_UNSIGNED; + result->value.u32 = 0; + return; + } + + rc_get_memref_value(result, self->value.memref, self->memref_access_type); + break; + default: - result->type = RC_VALUE_TYPE_UNSIGNED; - result->value.u32 = rc_get_memref_value(self->value.memref, self->type, eval_state); + rc_get_memref_value(result, self->value.memref, self->type); break; } diff --git a/dep/rcheevos/src/rcheevos/rc_internal.h b/dep/rcheevos/src/rcheevos/rc_internal.h index fa913fec8..d412be831 100644 --- a/dep/rcheevos/src/rcheevos/rc_internal.h +++ b/dep/rcheevos/src/rcheevos/rc_internal.h @@ -13,27 +13,115 @@ typedef struct rc_scratch_string { } rc_scratch_string_t; -#define RC_ALLOW_ALIGN(T) struct __align_ ## T { char ch; T t; }; +typedef struct rc_modified_memref_t { + 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 modifier; /* The modifier to apply to the parent. */ + uint8_t modifier_type; /* How to apply the modifier to the parent. (RC_OPERATOR_*) */ +} +rc_modified_memref_t; + +typedef struct rc_memref_list_t { + rc_memref_t* items; + struct rc_memref_list_t* next; + uint16_t count; + uint16_t capacity; + uint8_t allocated; +} rc_memref_list_t; + +typedef struct rc_modified_memref_list_t { + rc_modified_memref_t* items; + struct rc_modified_memref_list_t* next; + uint16_t count; + uint16_t capacity; + uint8_t allocated; +} rc_modified_memref_list_t; + +typedef struct rc_memrefs_t { + rc_memref_list_t memrefs; + rc_modified_memref_list_t modified_memrefs; +} rc_memrefs_t; + +typedef struct rc_trigger_with_memrefs_t { + rc_trigger_t trigger; + rc_memrefs_t memrefs; +} rc_trigger_with_memrefs_t; + +typedef struct rc_lboard_with_memrefs_t { + rc_lboard_t lboard; + rc_memrefs_t memrefs; +} rc_lboard_with_memrefs_t; + +typedef struct rc_richpresence_with_memrefs_t { + rc_richpresence_t richpresence; + rc_memrefs_t memrefs; +} rc_richpresence_with_memrefs_t; + +typedef struct rc_value_with_memrefs_t { + rc_value_t value; + rc_memrefs_t memrefs; +} rc_value_with_memrefs_t; + +/* enum helpers for natvis expansion. Have to use a struct to define the mapping, + * and a single field to allow the conditional logic to switch on the value */ +typedef struct __rc_bool_enum_t { uint8_t value; } __rc_bool_enum_t; +typedef struct __rc_memsize_enum_t { uint8_t value; } __rc_memsize_enum_t; +typedef struct __rc_memsize_enum_func_t { uint8_t value; } __rc_memsize_enum_func_t; +typedef struct __rc_operand_enum_t { uint8_t value; } __rc_operand_enum_t; +typedef struct __rc_value_type_enum_t { uint8_t value; } __rc_value_type_enum_t; +typedef struct __rc_memref_type_enum_t { uint8_t value; } __rc_memref_type_enum_t; +typedef struct __rc_condition_enum_t { uint8_t value; } __rc_condition_enum_t; +typedef struct __rc_condition_enum_str_t { uint8_t value; } __rc_condition_enum_str_t; +typedef struct __rc_condset_list_t { rc_condset_t* first_condset; } __rc_condset_list_t; +typedef struct __rc_operator_enum_t { uint8_t value; } __rc_operator_enum_t; +typedef struct __rc_operator_enum_str_t { uint8_t value; } __rc_operator_enum_str_t; +typedef struct __rc_operand_memref_t { rc_operand_t operand; } __rc_operand_memref_t; /* requires &rc_operand_t to be the same as &rc_operand_t.value.memref */ +typedef struct __rc_value_list_t { rc_value_t* first_value; } __rc_value_list_t; +typedef struct __rc_trigger_state_enum_t { uint8_t value; } __rc_trigger_state_enum_t; +typedef struct __rc_lboard_state_enum_t { uint8_t value; } __rc_lboard_state_enum_t; +typedef struct __rc_richpresence_display_list_t { rc_richpresence_display_t* first_display; } __rc_richpresence_display_list_t; +typedef struct __rc_richpresence_display_part_list_t { rc_richpresence_display_part_t* display; } __rc_richpresence_display_part_list_t; +typedef struct __rc_richpresence_lookup_list_t { rc_richpresence_lookup_t* first_lookup; } __rc_richpresence_lookup_list_t; +typedef struct __rc_format_enum_t { uint8_t value; } __rc_format_enum_t; + +#define RC_ALLOW_ALIGN(T) struct __align_ ## T { uint8_t ch; T t; }; + RC_ALLOW_ALIGN(rc_condition_t) RC_ALLOW_ALIGN(rc_condset_t) +RC_ALLOW_ALIGN(rc_modified_memref_t) RC_ALLOW_ALIGN(rc_lboard_t) +RC_ALLOW_ALIGN(rc_lboard_with_memrefs_t) RC_ALLOW_ALIGN(rc_memref_t) +RC_ALLOW_ALIGN(rc_memref_list_t) +RC_ALLOW_ALIGN(rc_memrefs_t) +RC_ALLOW_ALIGN(rc_modified_memref_list_t) RC_ALLOW_ALIGN(rc_operand_t) RC_ALLOW_ALIGN(rc_richpresence_t) RC_ALLOW_ALIGN(rc_richpresence_display_t) RC_ALLOW_ALIGN(rc_richpresence_display_part_t) RC_ALLOW_ALIGN(rc_richpresence_lookup_t) RC_ALLOW_ALIGN(rc_richpresence_lookup_item_t) +RC_ALLOW_ALIGN(rc_richpresence_with_memrefs_t) RC_ALLOW_ALIGN(rc_scratch_string_t) RC_ALLOW_ALIGN(rc_trigger_t) +RC_ALLOW_ALIGN(rc_trigger_with_memrefs_t) RC_ALLOW_ALIGN(rc_value_t) +RC_ALLOW_ALIGN(rc_value_with_memrefs_t) RC_ALLOW_ALIGN(char) #define RC_ALIGNOF(T) (sizeof(struct __align_ ## T) - sizeof(T)) -#define RC_OFFSETOF(o, t) (int)((char*)&(o.t) - (char*)&(o)) +#define RC_OFFSETOF(o, t) (int)((uint8_t*)&(o.t) - (uint8_t*)&(o)) #define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t))) #define RC_ALLOC_SCRATCH(t, p) ((t*)rc_alloc_scratch((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t))) +#define RC_ALLOC_ARRAY(t, n, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, (n) * sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t))) +#define RC_ALLOC_ARRAY_SCRATCH(t, n, p) ((t*)rc_alloc_scratch((p)->buffer, &(p)->offset, (n) * sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t))) + +#define RC_ALLOC_WITH_TRAILING(container_type, trailing_type, trailing_field, trailing_count, parse) ((container_type*)rc_alloc(\ + (parse)->buffer, &(parse)->offset, \ + RC_OFFSETOF((*(container_type*)NULL),trailing_field) + trailing_count * sizeof(trailing_type), \ + RC_ALIGNOF(container_type), &(parse)->scratch, 0)) +#define RC_GET_TRAILING(container_pointer, container_type, trailing_type, trailing_field) (trailing_type*)(&((container_type*)(container_pointer))->trailing_field) /* 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)) @@ -45,17 +133,49 @@ typedef struct { struct objs { rc_condition_t* __rc_condition_t; rc_condset_t* __rc_condset_t; + rc_modified_memref_t* __rc_modified_memref_t; rc_lboard_t* __rc_lboard_t; + rc_lboard_with_memrefs_t* __rc_lboard_with_memrefs_t; rc_memref_t* __rc_memref_t; + rc_memref_list_t* __rc_memref_list_t; + rc_memrefs_t* __rc_memrefs_t; + rc_modified_memref_list_t* __rc_modified_memref_list_t; rc_operand_t* __rc_operand_t; rc_richpresence_t* __rc_richpresence_t; rc_richpresence_display_t* __rc_richpresence_display_t; rc_richpresence_display_part_t* __rc_richpresence_display_part_t; rc_richpresence_lookup_t* __rc_richpresence_lookup_t; rc_richpresence_lookup_item_t* __rc_richpresence_lookup_item_t; + rc_richpresence_with_memrefs_t* __rc_richpresence_with_memrefs_t; rc_scratch_string_t __rc_scratch_string_t; rc_trigger_t* __rc_trigger_t; + rc_trigger_with_memrefs_t* __rc_trigger_with_memrefs_t; rc_value_t* __rc_value_t; + rc_value_with_memrefs_t* __rc_value_with_memrefs_t; + + /* these fields aren't actually used by the code, but they force the + * virtual enum wrapper types to exist so natvis can use them */ + union { + __rc_bool_enum_t boolean; + __rc_memsize_enum_t memsize; + __rc_memsize_enum_func_t memsize_func; + __rc_operand_enum_t operand; + __rc_value_type_enum_t value_type; + __rc_memref_type_enum_t memref_type; + __rc_condition_enum_t condition; + __rc_condition_enum_str_t condition_str; + __rc_condset_list_t condset_list; + __rc_operator_enum_t oper; + __rc_operator_enum_str_t oper_str; + __rc_operand_memref_t operand_memref; + __rc_value_list_t value_list; + __rc_trigger_state_enum_t trigger_state; + __rc_lboard_state_enum_t lboard_state; + __rc_richpresence_display_list_t richpresence_display_list; + __rc_richpresence_display_part_list_t richpresence_display_part_list; + __rc_richpresence_lookup_list_t richpresence_lookup_list; + __rc_format_enum_t format; + } natvis_extension; } objs; } rc_scratch_t; @@ -78,72 +198,128 @@ typedef struct { } rc_typed_value_t; +enum { + RC_MEMREF_TYPE_MEMREF, /* rc_memref_t */ + RC_MEMREF_TYPE_MODIFIED_MEMREF, /* rc_modified_memref_t */ + RC_MEMREF_TYPE_VALUE /* rc_value_t */ +}; + #define RC_MEASURED_UNKNOWN 0xFFFFFFFF +#define RC_OPERAND_NONE 0xFF typedef struct { - rc_typed_value_t add_value;/* AddSource/SubSource */ - int32_t add_hits; /* AddHits */ - uint32_t add_address; /* AddAddress */ - + /* memory accessors */ rc_peek_t peek; void* peek_userdata; +#ifndef RC_DISABLE_LUA lua_State* L; +#endif - rc_typed_value_t measured_value; /* Measured */ - rc_typed_value_t recall_value; /* Set by RC_CONDITION_REMEMBER */ - uint8_t was_reset; /* ResetIf triggered */ - uint8_t has_hits; /* one of more hit counts is non-zero */ - uint8_t primed; /* true if all non-Trigger conditions are true */ + /* processing state */ + rc_typed_value_t measured_value; /* captured Measured value */ + int32_t add_hits; /* AddHits/SubHits accumulator */ + uint8_t is_true; /* true if all conditions are true */ + uint8_t is_primed; /* true if all non-Trigger conditions are true */ + uint8_t is_paused; /* true if one or more PauseIf conditions is true */ + uint8_t can_measure; /* false if the measured value should be ignored */ uint8_t measured_from_hits; /* true if the measured_value came from a condition's hit count */ - uint8_t was_cond_reset; /* ResetNextIf triggered */ + uint8_t and_next; /* true if the previous condition was AndNext true */ + uint8_t or_next; /* true if the previous condition was OrNext true */ + uint8_t reset_next; /* true if the previous condition was ResetNextIf true */ + uint8_t stop_processing; /* true to abort the processing loop */ + + /* result state */ + uint8_t has_hits; /* true if one of more hit counts is non-zero */ + uint8_t was_reset; /* true if one or more ResetIf conditions is true */ + uint8_t was_cond_reset; /* true if one or more ResetNextIf conditions is true */ + + /* control settings */ + uint8_t can_short_curcuit; /* allows logic processing to stop as soon as a false condition is encountered */ } rc_eval_state_t; typedef struct { int32_t offset; +#ifndef RC_DISABLE_LUA lua_State* L; int funcs_ndx; +#endif void* buffer; rc_scratch_t scratch; - rc_memref_t** first_memref; + rc_memrefs_t* memrefs; + rc_memrefs_t* existing_memrefs; rc_value_t** variables; uint32_t measured_target; int lines_read; + rc_operand_t addsource_parent; + rc_operand_t indirect_parent; + rc_operand_t remember; + uint8_t addsource_oper; + + uint8_t is_value; uint8_t has_required_hits; uint8_t measured_as_percent; } rc_parse_state_t; +typedef struct rc_preparse_state_t { + rc_parse_state_t parse; + rc_memrefs_t memrefs; +} rc_preparse_state_t; + void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx); -void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs); -void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables); +void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memrefs_t* memrefs); +void rc_reset_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, int funcs_ndx); void rc_destroy_parse_state(rc_parse_state_t* parse); +void rc_init_preparse_state(rc_preparse_state_t* preparse, lua_State* L, int funcs_ndx); +void rc_preparse_alloc_memrefs(rc_memrefs_t* memrefs, rc_preparse_state_t* preparse); +void rc_preparse_reserve_memrefs(rc_preparse_state_t* preparse, rc_memrefs_t* memrefs); +void rc_preparse_copy_memrefs(rc_parse_state_t* parse, rc_memrefs_t* memrefs); +void rc_destroy_preparse_state(rc_preparse_state_t *preparse); +void rc_copy_memrefs_into_parse_state(rc_parse_state_t* parse, rc_memref_t* memrefs); +void rc_sync_operand(rc_operand_t* operand, rc_parse_state_t* parse, const rc_memref_t* memrefs); void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset); 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); char* rc_alloc_str(rc_parse_state_t* parse, const char* text, size_t length); -rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, uint32_t address, uint8_t size, uint8_t is_indirect); +rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, uint32_t address, uint8_t size); +rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t size, const rc_operand_t* parent, + uint8_t modifier_type, const rc_operand_t* modifier); int rc_parse_memref(const char** memaddr, uint8_t* size, uint32_t* address); -void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud); +void rc_update_memref_values(rc_memrefs_t* memrefs, rc_peek_t peek, void* ud); void rc_update_memref_value(rc_memref_value_t* memref, uint32_t value); -uint32_t rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state); +void rc_get_memref_value(rc_typed_value_t* value, rc_memref_t* memref, int operand_type); +uint32_t rc_get_modified_memref_value(const rc_modified_memref_t* memref, rc_peek_t peek, void* ud); uint8_t rc_memref_shared_size(uint8_t size); uint32_t rc_memref_mask(uint8_t size); void rc_transform_memref_value(rc_typed_value_t* value, uint8_t size); uint32_t rc_peek_value(uint32_t address, uint8_t size, rc_peek_t peek, void* ud); +void rc_memrefs_init(rc_memrefs_t* memrefs); +void rc_memrefs_destroy(rc_memrefs_t* memrefs); +uint32_t rc_memrefs_count_memrefs(const rc_memrefs_t* memrefs); +uint32_t rc_memrefs_count_modified_memrefs(const rc_memrefs_t* memrefs); + void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse); int rc_trigger_state_active(int state); +rc_memrefs_t* rc_trigger_get_memrefs(rc_trigger_t* self); -rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value); +typedef struct rc_condset_with_trailing_conditions_t { + rc_condset_t condset; + rc_condition_t conditions[2]; +} rc_condset_with_trailing_conditions_t; +RC_ALLOW_ALIGN(rc_condset_with_trailing_conditions_t) + +rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse); int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state); void rc_reset_condset(rc_condset_t* self); +rc_condition_t* rc_condset_get_conditions(rc_condset_t* self); enum { RC_PROCESSING_COMPARE_DEFAULT = 0, @@ -161,24 +337,35 @@ enum { RC_PROCESSING_COMPARE_ALWAYS_FALSE }; -rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, uint8_t is_indirect); +rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse); +void rc_parse_condition_internal(rc_condition_t* self, const char** memaddr, rc_parse_state_t* parse); +void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t* parse); int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state); void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state); int rc_condition_is_combining(const rc_condition_t* self); +void rc_condition_convert_to_operand(const rc_condition_t* condition, rc_operand_t* operand, rc_parse_state_t* parse); -int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indirect, rc_parse_state_t* parse); -void rc_evaluate_operand(rc_typed_value_t* value, rc_operand_t* self, rc_eval_state_t* eval_state); +int rc_parse_operand(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse); +void rc_evaluate_operand(rc_typed_value_t* value, const rc_operand_t* self, rc_eval_state_t* eval_state); +int rc_operator_is_modifying(int oper); int rc_operand_is_float_memref(const rc_operand_t* self); int rc_operand_is_float(const rc_operand_t* self); int rc_operand_is_recall(const rc_operand_t* self); +int rc_operand_type_is_memref(uint8_t type); +int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right); +void rc_operand_addsource(rc_operand_t* self, rc_parse_state_t* parse, uint8_t new_size); +void rc_operand_set_const(rc_operand_t* self, uint32_t value); +void rc_operand_set_float_const(rc_operand_t* self, double value); int rc_is_valid_variable_character(char ch, int is_first); void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse); int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud, lua_State* L); void rc_reset_value(rc_value_t* self); int rc_value_from_hits(rc_value_t* self); -rc_value_t* rc_alloc_helper_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse); -void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L); +rc_value_t* rc_alloc_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse); +uint32_t rc_count_values(const rc_value_t* values); +void rc_update_values(rc_value_t* values, rc_peek_t peek, void* ud, lua_State* L); +void rc_reset_values(rc_value_t* values); void rc_typed_value_convert(rc_typed_value_t* value, char new_type); void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount); @@ -187,6 +374,7 @@ void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amou void rc_typed_value_modulus(rc_typed_value_t* value, const rc_typed_value_t* amount); void rc_typed_value_negate(rc_typed_value_t* value); int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper); +void rc_typed_value_combine(rc_typed_value_t* value, rc_typed_value_t* amount, uint8_t oper); void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref); int rc_format_typed_value(char* buffer, size_t size, const rc_typed_value_t* value, int format); @@ -195,6 +383,11 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s int rc_lboard_state_active(int state); void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse); +rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self); +void rc_reset_richpresence_triggers(rc_richpresence_t* self); + +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); RC_END_C_DECLS diff --git a/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis b/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis new file mode 100644 index 000000000..2acf75beb --- /dev/null +++ b/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis @@ -0,0 +1,539 @@ + + + + + + {value.u32} (RC_VALUE_TYPE_UNSIGNED) + {value.f32} (RC_VALUE_TYPE_FLOAT) + {value.i32} (RC_VALUE_TYPE_SIGNED) + none (RC_VALUE_TYPE_NONE) + {value.i32} (unknown) + + + false + true + true ({value}) + + + {RC_MEMSIZE_8_BITS} + {RC_MEMSIZE_16_BITS} + {RC_MEMSIZE_24_BITS} + {RC_MEMSIZE_32_BITS} + {RC_MEMSIZE_LOW} + {RC_MEMSIZE_HIGH} + {RC_MEMSIZE_BIT_0} + {RC_MEMSIZE_BIT_1} + {RC_MEMSIZE_BIT_2} + {RC_MEMSIZE_BIT_3} + {RC_MEMSIZE_BIT_4} + {RC_MEMSIZE_BIT_5} + {RC_MEMSIZE_BIT_6} + {RC_MEMSIZE_BIT_7} + {RC_MEMSIZE_BITCOUNT} + {RC_MEMSIZE_16_BITS_BE} + {RC_MEMSIZE_24_BITS_BE} + {RC_MEMSIZE_32_BITS_BE} + {RC_MEMSIZE_FLOAT} + {RC_MEMSIZE_MBF32} + {RC_MEMSIZE_MBF32_LE} + {RC_MEMSIZE_FLOAT_BE} + {RC_MEMSIZE_DOUBLE32} + {RC_MEMSIZE_DOUBLE32_BE} + {RC_MEMSIZE_VARIABLE} + unknown ({value}) + + + {RC_VALUE_TYPE_NONE} + {RC_VALUE_TYPE_UNSIGNED} + {RC_VALUE_TYPE_SIGNED} + {RC_VALUE_TYPE_FLOAT} + unknown ({value}) + + + {RC_MEMREF_TYPE_MEMREF} + {RC_MEMREF_TYPE_MODIFIED_MEMREF} + RC_MEMREF_TYPE_VALUE + unknown ({value}) + + + byte + word + tbyte + dword + lower4 + upper4 + bit0 + bit1 + bit2 + bit3 + bit4 + bit5 + bit6 + bit7 + bitcount + word_be + tbyte_be + dword_be + float + mbf32 + mbf32_le + float_be + double32 + double32_be + var + unknown + + + {{value={(float)*((float*)&value)} prior={(float)*((float*)&prior)}}} + {{value={(int)value} prior={(int)prior}}} + {{value={value,x} prior={prior,x}}} + + value + prior + *((__rc_memsize_enum_t*)&size) + *((__rc_bool_enum_t*)&changed) + *((__rc_value_type_enum_t*)&type) + *((__rc_memref_type_enum_t*)&memref_type) + + + + {*(rc_modified_memref_t*)&value} + {*(rc_value_t*)&value} + var + {*((__rc_memsize_enum_func_t*)&value.size)}({address,x}) + + (rc_modified_memref_t*)&value + value + address + + + + {{count = {count}}} + + next + count + capacity + + count + items + + + + + ... {*((__rc_memsize_enum_func_t*)&operand.size)} {operand.value.memref->address,x} + value {((rc_value_t*)operand.value.memref)->name,s} + {*((__rc_memsize_enum_func_t*)&operand.size)} {operand.value.memref->address,x} + + (rc_modified_memref_t*)&operand.value + operand.value.memref->value + operand.value.memref->address + + + + {RC_OPERAND_ADDRESS} + {RC_OPERAND_DELTA} + {RC_OPERAND_CONST} + {RC_OPERAND_FP} + {RC_OPERAND_LUA} + {RC_OPERAND_PRIOR} + {RC_OPERAND_BCD} + {RC_OPERAND_INVERTED} + {RC_OPERAND_RECALL} + RC_OPERAND_NONE (255) + unknown ({value}) + + + {{{*(__rc_operand_memref_t*)&value.memref}}} + {{delta {*(__rc_operand_memref_t*)&value.memref}}} + {{prior {*(__rc_operand_memref_t*)&value.memref}}} + {{bcd {*(__rc_operand_memref_t*)&value.memref}}} + {{inverted {*(__rc_operand_memref_t*)&value.memref}}} + {{value {(int)value.num}}} + {{value {value.num,x}}} + {{value {value.num}}} + {{value {value.dbl}}} + {{recall}} + {{lua @{value.luafunc}}} + {{none}} + {{unknown}} + + value.num + value.dbl + value.num + value.dbl + value.memref + value.memref + (rc_modified_memref_t*)value.memref + *((__rc_operand_enum_t*)&type) + *((__rc_memsize_enum_t*)&size) + *((__rc_operand_enum_t*)&memref_access_type) + + + + {RC_CONDITION_STANDARD} + {RC_CONDITION_PAUSE_IF} + {RC_CONDITION_RESET_IF} + {RC_CONDITION_MEASURED_IF} + {RC_CONDITION_TRIGGER} + {RC_CONDITION_MEASURED} + {RC_CONDITION_ADD_SOURCE} + {RC_CONDITION_SUB_SOURCE} + {RC_CONDITION_ADD_ADDRESS} + {RC_CONDITION_REMEMBER} + {RC_CONDITION_ADD_HITS} + {RC_CONDITION_SUB_HITS} + {RC_CONDITION_RESET_NEXT_IF} + {RC_CONDITION_AND_NEXT} + {RC_CONDITION_OR_NEXT} + unknown ({value}) + + + + PauseIf + ResetIf + MeasuredIf + Trigger + Measured + AddSource + SubSource + AddAddress + Remember + AddHits + SubHits + ResetNextIf + AndNext + OrNext + {value} + + + {RC_OPERATOR_EQ} + {RC_OPERATOR_LT} + {RC_OPERATOR_LE} + {RC_OPERATOR_GT} + {RC_OPERATOR_GE} + {RC_OPERATOR_NE} + {RC_OPERATOR_NONE} + {RC_OPERATOR_MULT} + {RC_OPERATOR_DIV} + {RC_OPERATOR_AND} + {RC_OPERATOR_XOR} + {RC_OPERATOR_MOD} + {RC_OPERATOR_ADD} + {RC_OPERATOR_SUB} + {RC_OPERATOR_SUB_PARENT} + {RC_OPERATOR_INDIRECT_READ} + unknown ({value}) + + + == + < + <= + > + >= + != + + * + / + & + ^ + % + + + - + subtracted from + $ + unknown ({value}) + + + {*((__rc_condition_enum_str_t*)&type)} {operand1} + {*((__rc_condition_enum_str_t*)&type)} {operand1} ({required_hits}) + {*((__rc_condition_enum_str_t*)&type)} {operand1} {*((__rc_operator_enum_str_t*)&oper)} {operand2} + {*((__rc_condition_enum_str_t*)&type)} {operand1} {*((__rc_operator_enum_str_t*)&oper)} {operand2} ({required_hits}) + + *((__rc_condition_enum_t*)&type) + operand1 + *((__rc_operator_enum_t*)&oper) + operand2 + required_hits + current_hits + next + + + + {{count={num_pause_conditions+num_reset_conditions+num_hittarget_conditions+num_measured_conditions+num_other_conditions+num_indirect_conditions}}} + + + conditions + next + this + + + + + {{}} + + + first_condset + next + this + + + + + $({parent} + {modifier}) + ({modifier} - {parent}) + ({parent} {*((__rc_operator_enum_str_t*)&modifier_type)} {modifier}) + + memref.value + parent + *((__rc_operator_enum_t*)&modifier_type) + modifier + + + + {{count = {count}}} + + next + count + capacity + + count + items + + + + + {RC_TRIGGER_STATE_INACTIVE} + {RC_TRIGGER_STATE_WAITING} + {RC_TRIGGER_STATE_ACTIVE} + {RC_TRIGGER_STATE_PAUSED} + {RC_TRIGGER_STATE_RESET} + {RC_TRIGGER_STATE_TRIGGERED} + {RC_TRIGGER_STATE_PRIMED} + {RC_TRIGGER_STATE_DISABLED} + unknown ({value}) + + + + *((__rc_trigger_state_enum_t*)&state) + *((__rc_bool_enum_t*)&has_hits) + *((__rc_bool_enum_t*)&measured_as_percent) + requirement + *((__rc_condset_list_t*)&alternative) + + + + {value} {name,s} + + value + conditions + name + + + + {{count = {count}}} + + count + capacity + + count + items + + + + + {RC_LBOARD_STATE_INACTIVE} + {RC_LBOARD_STATE_WAITING} + {RC_LBOARD_STATE_ACTIVE} + {RC_LBOARD_STATE_STARTED} + {RC_LBOARD_STATE_CANCELED} + {RC_LBOARD_STATE_TRIGGERED} + {RC_LBOARD_STATE_DISABLED} + unknown ({value}) + + + + *((__rc_lboard_state_enum_t*)&state) + start + submit + cancel + value + + + + {RC_FORMAT_FRAMES} + {RC_FORMAT_SECONDS} + {RC_FORMAT_CENTISECS} + {RC_FORMAT_SCORE} + {RC_FORMAT_VALUE} + {RC_FORMAT_MINUTES} + {RC_FORMAT_SECONDS_AS_MINUTES} + {RC_FORMAT_FLOAT1} + {RC_FORMAT_FLOAT2} + {RC_FORMAT_FLOAT3} + {RC_FORMAT_FLOAT4} + {RC_FORMAT_FLOAT5} + {RC_FORMAT_FLOAT6} + {RC_FORMAT_FIXED1} + {RC_FORMAT_FIXED2} + {RC_FORMAT_FIXED3} + {RC_FORMAT_TENS} + {RC_FORMAT_HUNDREDS} + {RC_FORMAT_THOUSANDS} + RC_FORMAT_STRING (101) + RC_FORMAT_LOOKUP (102) + RC_FORMAT_UNKNOWN_MACRO (103) + RC_FORMAT_ASCIICHAR (104) + RC_FORMAT_UNICODECHAR (105) + unknown ({value}) + + + {text,s} + [Unknown macro]{text,sb} + @{text,sb}({value,na}) + @{lookup->name,sb}({value,na}) + + text + lookup + value + *((__rc_format_enum_t*)&display_type) + + + + {display,na} {display->next,na} {display->next->next,na} + {display,na} {display->next,na} + {display,na} + + + display + next + this + + + + + + *((__rc_richpresence_display_part_list_t*)&display) + trigger + + + + {{NULL}} + {(void*)&first_display,na} + + + first_display + next + this + + + + + {first}: {label,na} + {first}-{last}: {label,na} + + + {name,na} + + name + *((__rc_format_enum_t*)&format) + default_label + + root + left + right + this + + + + + {{NULL}} + {(void*)&first_lookup,na} + + + first_lookup + next + this + + + + + + ((__rc_richpresence_display_list_t*)&first_display) + ((__rc_richpresence_lookup_list_t*)&first_lookup) + + + + {{NULL}} + {(void*)&first_value,na} + + + first_value + next + this + + + + + {{offset={offset} addsource_parent={addsource_parent} indirect_parent={indirect_parent}}} + + offset + memrefs + existing_memrefs + variables + ((__rc_value_list_t*)&variables) + addsource_parent + *((__rc_operator_enum_t*)&addsource_oper) + indirect_parent + remember + *((__rc_bool_enum_t*)&is_value) + *((__rc_bool_enum_t*)&has_required_hits) + *((__rc_bool_enum_t*)&measured_as_percent) + + + + {{used={write-start} size={end-start}}} + + end-start + write-start + end-write + next + + + + + + + &chunk + next + this + + + + + {{count={runtime.trigger_count}}} + + + runtime.trigger_count + runtime.triggers[$i] + + + + + {{count={runtime.lboard_count}}} + + + runtime.lboard_count + runtime.lboards[$i] + + + + + {{trigger_count={trigger_count} lboard_count={lboard_count}}} + + *((__rc_runtime_trigger_list_t*)this) + *((__rc_runtime_lboard_list_t*)this) + richpresence + memrefs + + + diff --git a/dep/rcheevos/src/rcheevos/rc_validate.c b/dep/rcheevos/src/rcheevos/rc_validate.c index 770f9dc98..cb8cf5c1a 100644 --- a/dep/rcheevos/src/rcheevos/rc_validate.c +++ b/dep/rcheevos/src/rcheevos/rc_validate.c @@ -17,6 +17,7 @@ static int rc_validate_memref(const rc_memref_t* memref, char result[], const si switch (console_id) { case RC_CONSOLE_NINTENDO: + case RC_CONSOLE_FAMICOM_DISK_SYSTEM: if (memref->address >= 0x0800 && memref->address <= 0x1FFF) { snprintf(result, result_size, "Mirror RAM may not be exposed by emulator (address %04X)", memref->address); return 0; @@ -42,14 +43,19 @@ static int rc_validate_memref(const rc_memref_t* memref, char result[], const si return 1; } -int rc_validate_memrefs(const rc_memref_t* memref, char result[], const size_t result_size, uint32_t max_address) +int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address) { - while (memref) { - if (!rc_validate_memref(memref, result, result_size, 0, max_address)) - return 0; + const rc_memref_list_t* memref_list = &memrefs->memrefs; + do { + const rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_stop = memref + memref_list->count; + for (; memref < memref_stop; ++memref) { + if (!rc_validate_memref(memref, result, result_size, 0, max_address)) + return 0; + } - memref = memref->next; - } + memref_list = memref_list->next; + } while (memref_list); return 1; } @@ -64,15 +70,22 @@ static uint32_t rc_console_max_address(uint32_t console_id) return 0xFFFFFFFF; } -int rc_validate_memrefs_for_console(const rc_memref_t* memref, 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) { const uint32_t max_address = rc_console_max_address(console_id); - while (memref) { - if (!rc_validate_memref(memref, result, result_size, console_id, max_address)) - return 0; + const rc_memref_list_t* memref_list = &memrefs->memrefs; + do + { + const rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_stop = memref + memref_list->count; + for (; memref < memref_stop; ++memref) + { + if (!rc_validate_memref(memref, result, result_size, console_id, max_address)) + return 0; + } - memref = memref->next; - } + memref_list = memref_list->next; + } while (memref_list); return 1; } @@ -104,6 +117,7 @@ static uint32_t rc_max_value(const rc_operand_t* operand) return 8; case RC_MEMSIZE_8_BITS: + /* NOTE: BCD should max out at 0x99, but because each digit can be 15, it actually maxes at 15*10 + 15 */ return (operand->type == RC_OPERAND_BCD) ? 165 : 0xFF; case RC_MEMSIZE_16_BITS: @@ -160,10 +174,18 @@ static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t* case RC_OPERATOR_SUB: { - if (operand->type == RC_OPERAND_CONST) - return value - operand->value.num; - else if (value > rc_max_value(operand)) - return value - rc_max_value(operand); + const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand); + if (value > op_max) + return value - op_max; + + return 0xFFFFFFFF; + } + + case RC_OPERATOR_SUB_PARENT: + { + const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand); + if (op_max > value) + return op_max - value; return 0xFFFFFFFF; } @@ -173,6 +195,17 @@ static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t* } } +static uint32_t rc_max_chain_value(const rc_operand_t* operand) +{ + if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) { + const rc_modified_memref_t* modified_memref = (const rc_modified_memref_t*)operand->value.memref; + const uint32_t op_max = rc_max_chain_value(&modified_memref->parent); + return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier); + } + + return rc_max_value(operand); +} + static int rc_validate_get_condition_index(const rc_condset_t* condset, const rc_condition_t* condition) { int index = 1; @@ -256,18 +289,14 @@ static int rc_validate_range(uint32_t min_val, uint32_t max_val, char oper, uint return 1; } -int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address) +static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address) { const rc_condition_t* cond; char buffer[128]; - uint32_t max_val; int index = 1; - unsigned long long add_source_max = 0; int in_add_hits = 0; int in_add_address = 0; int is_combining = 0; - int remember_used = 0; - int remember_used_in_pause = 0; if (!condset) { *result = '\0'; @@ -275,10 +304,8 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con } for (cond = condset->conditions; cond; cond = cond->next, ++index) { - uint32_t max = rc_max_value(&cond->operand1); const int is_memref1 = rc_operand_is_memref(&cond->operand1); const int is_memref2 = rc_operand_is_memref(&cond->operand2); - const int uses_recall = rc_operand_is_recall(&cond->operand1) || rc_operand_is_recall(&cond->operand2); if (!in_add_address) { if (is_memref1 && !rc_validate_memref(cond->operand1.value.memref, buffer, sizeof(buffer), console_id, max_address)) { @@ -294,39 +321,24 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con in_add_address = 0; } - if (!remember_used && uses_recall) { - if (!cond->pause && condset->has_pause) { - /* pause conditions will be processed before non-pause conditions. - * scan forward for any remembers in yet-to-be-processed pause conditions */ - const rc_condition_t* cond_rem_pause_check = cond->next; - for (; cond_rem_pause_check; cond_rem_pause_check = cond_rem_pause_check->next) { - if (cond_rem_pause_check->type == RC_CONDITION_REMEMBER && cond_rem_pause_check->pause) { - remember_used = 1; /* do not set remember_used_in_pause here because we don't know at which poing in the pause processing this remember is occurring. */ - break; - } - } - } - if (!remember_used) { + if (rc_operand_is_recall(&cond->operand1)) { + if (rc_operand_type_is_memref(cond->operand1.memref_access_type) && !cond->operand1.value.memref) { snprintf(result, result_size, "Condition %d: Recall used before Remember", index); return 0; } } - else if (cond->pause && uses_recall && !remember_used_in_pause) { - snprintf(result, result_size, "Condition %d: Recall used in Pause processing before Remember was used in Pause processing", index); - return 0; + + if (rc_operand_is_recall(&cond->operand2)) { + if (rc_operand_type_is_memref(cond->operand2.memref_access_type) && !cond->operand2.value.memref) { + snprintf(result, result_size, "Condition %d: Recall used before Remember", index); + return 0; + } } switch (cond->type) { case RC_CONDITION_ADD_SOURCE: - max = rc_scale_value(max, cond->oper, &cond->operand2); - add_source_max += max; - is_combining = 1; - continue; - case RC_CONDITION_SUB_SOURCE: - max = rc_scale_value(max, cond->oper, &cond->operand2); - if (add_source_max < max) /* potential underflow - may be expected */ - add_source_max = 0xFFFFFFFF; + case RC_CONDITION_REMEMBER: is_combining = 1; continue; @@ -339,12 +351,6 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con is_combining = 1; continue; - case RC_CONDITION_REMEMBER: - is_combining = 1; - remember_used = 1; - remember_used_in_pause += cond->pause; - continue; - case RC_CONDITION_ADD_HITS: case RC_CONDITION_SUB_HITS: in_add_hits = 1; @@ -377,18 +383,11 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con break; } - /* if we're in an add source chain, check for overflow */ - if (add_source_max) { - const unsigned long long overflow = add_source_max + max; - if (overflow > 0xFFFFFFFFUL) - max = 0xFFFFFFFF; - else - max += (unsigned)add_source_max; - } - /* check for comparing two differently sized memrefs */ - max_val = rc_max_value(&cond->operand2); - if (max_val != max && add_source_max == 0 && is_memref1 && is_memref2) { + if (is_memref1 && is_memref2 && + cond->operand1.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && + cond->operand2.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && + rc_max_value(&cond->operand1) != rc_max_value(&cond->operand2)) { snprintf(result, result_size, "Condition %d: Comparing different memory sizes", index); return 0; } @@ -396,19 +395,23 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con if (is_memref1 && rc_operand_is_float(&cond->operand1)) { /* if left side is a float, right side will be converted to a float, so don't do range validation */ } - else if (is_memref1 || is_memref2 || add_source_max) { - /* if either side is a memref, or there's a running add source chain, check for impossible comparisons */ + else if (is_memref1 || is_memref2) { + /* if either side is a memref, check for impossible comparisons */ const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index); const rc_operand_t* operand1 = &cond->operand1; const rc_operand_t* operand2 = &cond->operand2; uint8_t oper = cond->oper; + uint32_t max = rc_max_chain_value(operand1); + uint32_t max_val = rc_max_value(operand2); uint32_t min_val; - if (!is_memref1 && !add_source_max) { + if (!is_memref1) { /* pretend constant was on right side */ operand1 = &cond->operand2; operand2 = &cond->operand1; - max = max_val; + max_val = max; + max = rc_max_value(&cond->operand2); + switch (oper) { case RC_OPERATOR_LT: oper = RC_OPERATOR_GT; break; case RC_OPERATOR_LE: oper = RC_OPERATOR_GE; break; @@ -426,8 +429,7 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con min_val = (int)operand2->value.dbl; /* cannot compare an integer memory reference to a non-integral floating point value */ - if (!add_source_max && !rc_operand_is_float_memref(operand1) && - (float)min_val != operand2->value.dbl) { + if (!rc_operand_is_float_memref(operand1) && (float)min_val != operand2->value.dbl) { switch (oper) { case RC_OPERATOR_EQ: snprintf(result + prefix_length, result_size - prefix_length, "Comparison is never true"); @@ -455,8 +457,6 @@ int rc_validate_condset_internal(const rc_condset_t* condset, char result[], con if (!rc_validate_range(min_val, max_val, oper, max, result + prefix_length, result_size - prefix_length)) return 0; } - - add_source_max = 0; } if (is_combining) { @@ -697,24 +697,6 @@ static int rc_validate_comparison_overlap(int comparison1, uint32_t value1, int return RC_OVERLAP_NONE; } -static int rc_validate_are_operands_equal(const rc_operand_t* oper1, const rc_operand_t* oper2) -{ - if (oper1->type != oper2->type) - return 0; - - switch (oper1->type) - { - case RC_OPERAND_CONST: - return (oper1->value.num == oper2->value.num); - case RC_OPERAND_FP: - return (oper1->value.dbl == oper2->value.dbl); - case RC_OPERAND_RECALL: - return (oper2->type == RC_OPERAND_RECALL); - default: - return (oper1->value.memref->address == oper2->value.memref->address && oper1->size == oper2->size); - } -} - static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, const rc_condset_t* compare_conditions, const char* prefix, const char* compare_prefix, char result[], const size_t result_size) { @@ -736,7 +718,7 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co for (condition = conditions->conditions; condition != NULL; condition = condition->next) { condition_chain_start = condition; - while (rc_condition_is_combining(condition)) + while (condition && rc_condition_is_combining(condition)) condition = condition->next; if (!condition) break; @@ -787,14 +769,14 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co if (compare_condition->type != condition_chain_iter->type || compare_condition->oper != condition_chain_iter->oper || compare_condition->required_hits != condition_chain_iter->required_hits || - !rc_validate_are_operands_equal(&compare_condition->operand1, &condition_chain_iter->operand1)) + !rc_operands_are_equal(&compare_condition->operand1, &condition_chain_iter->operand1)) { chain_matches = 0; break; } if (compare_condition->oper != RC_OPERATOR_NONE && - !rc_validate_are_operands_equal(&compare_condition->operand2, &condition_chain_iter->operand2)) + !rc_operands_are_equal(&compare_condition->operand2, &condition_chain_iter->operand2)) { chain_matches = 0; break; diff --git a/dep/rcheevos/src/rcheevos/rc_validate.h b/dep/rcheevos/src/rcheevos/rc_validate.h index 09c90ab7f..cfda94075 100644 --- a/dep/rcheevos/src/rcheevos/rc_validate.h +++ b/dep/rcheevos/src/rcheevos/rc_validate.h @@ -7,13 +7,9 @@ RC_BEGIN_C_DECLS -int rc_validate_memrefs(const rc_memref_t* memref, char result[], const size_t result_size, uint32_t max_address); - int rc_validate_condset(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t max_address); int rc_validate_trigger(const rc_trigger_t* trigger, char result[], const size_t result_size, uint32_t max_address); -int rc_validate_memrefs_for_console(const rc_memref_t* memref, char result[], const size_t result_size, uint32_t console_id); - int rc_validate_condset_for_console(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id); int rc_validate_trigger_for_console(const rc_trigger_t* trigger, char result[], const size_t result_size, uint32_t console_id); diff --git a/dep/rcheevos/src/rcheevos/richpresence.c b/dep/rcheevos/src/rcheevos/richpresence.c index 468171f9d..6b27d064d 100644 --- a/dep/rcheevos/src/rcheevos/richpresence.c +++ b/dep/rcheevos/src/rcheevos/richpresence.c @@ -13,29 +13,67 @@ enum { RC_FORMAT_UNICODECHAR = 105 }; -static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* memaddr, int memaddr_len, rc_parse_state_t* parse) { - const char* end; - rc_value_t* variable; - uint32_t address; - uint8_t size; +static void rc_alloc_helper_variable_memref_value(rc_richpresence_display_part_t* part, const char* memaddr, int memaddr_len, rc_parse_state_t* parse) { + rc_preparse_state_t preparse; + const char* test_memaddr = memaddr; + rc_condset_t* condset; + rc_value_t* value; + int32_t size; - /* single memory reference lookups without a modifier flag can be handled without a variable */ - end = memaddr; - if (rc_parse_memref(&end, &size, &address) == RC_OK) { - /* make sure the entire memaddr was consumed. if not, there's an operator and it's a comparison, not a memory reference */ - if (end == &memaddr[memaddr_len]) { - /* if it's not a derived size, we can reference the memref directly */ - if (rc_memref_shared_size(size) == size) - return &rc_alloc_memref(parse, address, size, 0)->value; + part->value.type = RC_OPERAND_NONE; + + /* if the expression can be represented as just a memory reference, do so */ + rc_init_preparse_state(&preparse, NULL, 0); + preparse.parse.existing_memrefs = parse->memrefs; + value = RC_ALLOC(rc_value_t, &preparse.parse); + rc_parse_value_internal(value, &test_memaddr, &preparse.parse); + + size = preparse.parse.offset; + if (size < 0) { + parse->offset = size; + rc_destroy_preparse_state(&preparse); + return; + } + + /* ensure new needed memrefs are allocated in the primary buffer */ + rc_preparse_copy_memrefs(parse, &preparse.memrefs); + + /* parse the value into the scratch buffer so we can look at it */ + rc_reset_parse_state(&preparse.parse, rc_buffer_alloc(&preparse.parse.scratch.buffer, (size_t)size), NULL, 0); + preparse.parse.memrefs = parse->memrefs; + preparse.parse.existing_memrefs = parse->existing_memrefs; + value = RC_ALLOC(rc_value_t, &preparse.parse); + test_memaddr = memaddr; + rc_parse_value_internal(value, &test_memaddr, &preparse.parse); + + condset = value->conditions; + if (condset && !condset->next) { + /* single value - if it's only "measured" and "indirect" conditions, we can simplify to a memref */ + if (condset->num_measured_conditions && + !condset->num_pause_conditions && !condset->num_reset_conditions && + !condset->num_other_conditions && !condset->num_hittarget_conditions) { + rc_condition_t* condition = condset->conditions; + for (; condition; condition = condition->next) { + if (condition->type == RC_CONDITION_MEASURED && condition->required_hits == 0) { + memcpy(&part->value, &condition->operand1, sizeof(condition->operand1)); + break; + } + } } } - /* not a simple memory reference, need to create a variable */ - variable = rc_alloc_helper_variable(memaddr, memaddr_len, parse); - if (!variable) - return NULL; + rc_destroy_preparse_state(&preparse); - return &variable->value; + /* could not express value with just a memory reference, create a helper variable */ + if (part->value.type == RC_OPERAND_NONE) { + value = rc_alloc_variable(memaddr, memaddr_len, parse); + if (value) { + part->value.value.memref = (rc_memref_t*)&value->value; + part->value.type = RC_OPERAND_ADDRESS; + part->value.size = RC_MEMSIZE_32_BITS; + part->value.memref_access_type = RC_OPERAND_ADDRESS; + } + } } static const char* rc_parse_line(const char* line, const char** end, rc_parse_state_t* parse) { @@ -220,7 +258,7 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c part->text = rc_alloc_str(parse, in, (int)(ptr - in)); } else if (part->display_type != RC_FORMAT_UNKNOWN_MACRO) { - part->value = rc_alloc_helper_variable_memref_value(line, (int)(ptr - line), parse); + rc_alloc_helper_variable_memref_value(part, line, (int)(ptr - line), parse); if (parse->offset < 0) return 0; @@ -468,6 +506,8 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, int display_line = 0; int chars; + self->values = NULL; + /* special case for empty script to return 1 line read */ if (!*script) { parse->lines_read = 1; @@ -558,11 +598,20 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, *nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, firstlookup); if (parse->offset < 0) return; + trigger = &((*nextdisplay)->trigger); rc_parse_trigger_internal(trigger, &line, parse); - trigger->memrefs = 0; if (parse->offset < 0) return; + + if (line != ptr) { + /* incomplete read */ + parse->offset = RC_INVALID_OPERATOR; + return; + } + + (*nextdisplay)->has_required_hits = parse->has_required_hits; + if (parse->buffer) nextdisplay = &((*nextdisplay)->next); } @@ -593,6 +642,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, /* finalize */ *nextdisplay = 0; + self->has_memrefs = 0; if (!hasdisplay && parse->offset > 0) { parse->offset = RC_MISSING_DISPLAY_STRING; @@ -600,22 +650,20 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, } int rc_richpresence_size_lines(const char* script, int* lines_read) { - rc_richpresence_t* self; - rc_parse_state_t parse; - rc_memref_t* first_memref; - rc_value_t* variables; - rc_init_parse_state(&parse, 0, 0, 0); - rc_init_parse_state_memrefs(&parse, &first_memref); - rc_init_parse_state_variables(&parse, &variables); + rc_richpresence_with_memrefs_t* richpresence; + rc_preparse_state_t preparse; + rc_init_preparse_state(&preparse, NULL, 0); - self = RC_ALLOC(rc_richpresence_t, &parse); - rc_parse_richpresence_internal(self, script, &parse); + richpresence = RC_ALLOC(rc_richpresence_with_memrefs_t, &preparse.parse); + preparse.parse.variables = &richpresence->richpresence.values; + rc_parse_richpresence_internal(&richpresence->richpresence, script, &preparse.parse); + rc_preparse_alloc_memrefs(NULL, &preparse); if (lines_read) - *lines_read = parse.lines_read; + *lines_read = preparse.parse.lines_read; - rc_destroy_parse_state(&parse); - return parse.offset; + rc_destroy_preparse_state(&preparse); + return preparse.parse.offset; } int rc_richpresence_size(const char* script) { @@ -623,32 +671,53 @@ int rc_richpresence_size(const char* script) { } rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx) { - rc_richpresence_t* self; - rc_parse_state_t parse; + rc_richpresence_with_memrefs_t* richpresence; + rc_preparse_state_t preparse; if (!buffer || !script) return NULL; - rc_init_parse_state(&parse, buffer, L, funcs_ndx); + rc_init_preparse_state(&preparse, L, funcs_ndx); + richpresence = RC_ALLOC(rc_richpresence_with_memrefs_t, &preparse.parse); + preparse.parse.variables = &richpresence->richpresence.values; + rc_parse_richpresence_internal(&richpresence->richpresence, script, &preparse.parse); - self = RC_ALLOC(rc_richpresence_t, &parse); - rc_init_parse_state_memrefs(&parse, &self->memrefs); - rc_init_parse_state_variables(&parse, &self->variables); + rc_reset_parse_state(&preparse.parse, buffer, L, funcs_ndx); + richpresence = RC_ALLOC(rc_richpresence_with_memrefs_t, &preparse.parse); + preparse.parse.variables = &richpresence->richpresence.values; + rc_preparse_alloc_memrefs(&richpresence->memrefs, &preparse); - rc_parse_richpresence_internal(self, script, &parse); + rc_parse_richpresence_internal(&richpresence->richpresence, script, &preparse.parse); + richpresence->richpresence.has_memrefs = 1; - rc_destroy_parse_state(&parse); - return (parse.offset >= 0) ? self : NULL; + rc_destroy_preparse_state(&preparse); + return (preparse.parse.offset >= 0) ? &richpresence->richpresence : NULL; +} + +static void rc_update_richpresence_memrefs(rc_richpresence_t* self, rc_peek_t peek, void* ud) { + if (self->has_memrefs) { + rc_richpresence_with_memrefs_t* richpresence = (rc_richpresence_with_memrefs_t*)self; + rc_update_memref_values(&richpresence->memrefs, peek, ud); + } +} + +rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self) { + if (self->has_memrefs) { + rc_richpresence_with_memrefs_t* richpresence = (rc_richpresence_with_memrefs_t*)self; + return &richpresence->memrefs; + } + + return NULL; } void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, lua_State* L) { rc_richpresence_display_t* display; - rc_update_memref_values(richpresence->memrefs, peek, peek_ud); - rc_update_variables(richpresence->variables, peek, peek_ud, L); + rc_update_richpresence_memrefs(richpresence, peek, peek_ud); + rc_update_values(richpresence->values, peek, peek_ud, L); for (display = richpresence->first_display; display; display = display->next) { - if (display->trigger.has_required_hits) + if (display->has_required_hits) rc_test_trigger(&display->trigger, peek, peek_ud, L); } } @@ -671,7 +740,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part break; case RC_FORMAT_LOOKUP: - rc_typed_value_from_memref_value(&value, part->value); + rc_evaluate_operand(&value, &part->value, NULL); rc_typed_value_convert(&value, RC_VALUE_TYPE_UNSIGNED); text = part->lookup->default_label; @@ -698,7 +767,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part value.type = RC_VALUE_TYPE_UNSIGNED; do { - value.value.u32 = part->value->value; + rc_evaluate_operand(&value, &part->value, NULL); if (value.value.u32 == 0) { /* null terminator - skip over remaining character macros */ while (part->next && part->next->display_type == RC_FORMAT_ASCIICHAR) @@ -725,7 +794,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part value.type = RC_VALUE_TYPE_UNSIGNED; do { - value.value.u32 = part->value->value; + rc_evaluate_operand(&value, &part->value, NULL); if (value.value.u32 == 0) { /* null terminator - skip over remaining character macros */ while (part->next && part->next->display_type == RC_FORMAT_UNICODECHAR) @@ -770,7 +839,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part break; default: - rc_typed_value_from_memref_value(&value, part->value); + rc_evaluate_operand(&value, &part->value, NULL); chars = rc_format_typed_value(tmp, sizeof(tmp), &value, part->display_type); text = tmp; break; @@ -806,7 +875,7 @@ int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* bu return rc_evaluate_richpresence_display(display->display, buffer, buffersize); /* triggers with required hits will be updated in rc_update_richpresence */ - if (!display->trigger.has_required_hits) + if (!display->has_required_hits) rc_test_trigger(&display->trigger, peek, peek_ud, L); /* if we've found a valid condition, process it */ @@ -823,15 +892,16 @@ int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, size return rc_get_richpresence_display_string(richpresence, buffer, buffersize, peek, peek_ud, L); } -void rc_reset_richpresence(rc_richpresence_t* self) { +void rc_reset_richpresence_triggers(rc_richpresence_t* self) { rc_richpresence_display_t* display; - rc_value_t* variable; for (display = self->first_display; display; display = display->next) rc_reset_trigger(&display->trigger); +} - for (variable = self->variables; variable; variable = variable->next) - rc_reset_value(variable); +void rc_reset_richpresence(rc_richpresence_t* self) { + rc_reset_richpresence_triggers(self); + rc_reset_values(self->values); } static int rc_get_richpresence_strings_has_string(const char** buffer, size_t written, const char* search) { diff --git a/dep/rcheevos/src/rcheevos/runtime.c b/dep/rcheevos/src/rcheevos/runtime.c index 5d9f60d9a..def76c088 100644 --- a/dep/rcheevos/src/rcheevos/runtime.c +++ b/dep/rcheevos/src/rcheevos/runtime.c @@ -9,8 +9,34 @@ #define RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE 256 +/* ===== natvis extensions ===== */ + +typedef struct __rc_runtime_trigger_list_t { rc_runtime_t runtime; } __rc_runtime_trigger_list_t; +typedef struct __rc_runtime_lboard_list_t { rc_runtime_t runtime; } __rc_runtime_lboard_list_t; + +static void rc_runtime_natvis_helper(const rc_runtime_event_t* runtime_event) +{ + struct natvis_extensions { + __rc_runtime_trigger_list_t trigger_list; + __rc_runtime_lboard_list_t lboard_list; + } natvis; + + memset(&natvis, 0, sizeof(natvis)); + (void)runtime_event; + + natvis.lboard_list.runtime.lboard_count = 0; +} + +/* ============================= */ + rc_runtime_t* rc_runtime_alloc(void) { - rc_runtime_t* self = malloc(sizeof(rc_runtime_t)); + rc_runtime_t* self; + + /* create a reference to rc_runtime_natvis_helper so the extensions get compiled in. */ + rc_runtime_event_handler_t unused = &rc_runtime_natvis_helper; + (void)unused; + + self = malloc(sizeof(rc_runtime_t)); if (self) { rc_runtime_init(self); @@ -22,16 +48,19 @@ rc_runtime_t* rc_runtime_alloc(void) { void rc_runtime_init(rc_runtime_t* self) { memset(self, 0, sizeof(rc_runtime_t)); - self->next_memref = &self->memrefs; - self->next_variable = &self->variables; + + self->memrefs = (rc_memrefs_t*)malloc(sizeof(*self->memrefs)); + rc_memrefs_init(self->memrefs); } void rc_runtime_destroy(rc_runtime_t* self) { uint32_t i; if (self->triggers) { - for (i = 0; i < self->trigger_count; ++i) - free(self->triggers[i].buffer); + for (i = 0; i < self->trigger_count; ++i) { + if (self->triggers[i].buffer) + free(self->triggers[i].buffer); + } free(self->triggers); self->triggers = NULL; @@ -40,8 +69,10 @@ void rc_runtime_destroy(rc_runtime_t* self) { } if (self->lboards) { - for (i = 0; i < self->lboard_count; ++i) - free(self->lboards[i].buffer); + for (i = 0; i < self->lboard_count; ++i) { + if (self->lboards[i].buffer) + free(self->lboards[i].buffer); + } free(self->lboards); self->lboards = NULL; @@ -49,20 +80,17 @@ void rc_runtime_destroy(rc_runtime_t* self) { self->lboard_count = self->lboard_capacity = 0; } - while (self->richpresence) { - rc_runtime_richpresence_t* previous = self->richpresence->previous; - - free(self->richpresence->buffer); + if (self->richpresence) { + if (self->richpresence->buffer) + free(self->richpresence->buffer); free(self->richpresence); - self->richpresence = previous; } - self->next_memref = 0; - self->memrefs = 0; + if (self->memrefs) + rc_memrefs_destroy(self->memrefs); - if (self->owns_self) { + if (self->owns_self) free(self); - } } void rc_runtime_checksum(const char* memaddr, uint8_t* md5) { @@ -72,45 +100,12 @@ void rc_runtime_checksum(const char* memaddr, uint8_t* md5) { md5_finish(&state, md5); } -static char rc_runtime_allocated_memrefs(rc_runtime_t* self) { - char owns_memref = 0; - - /* if at least one memref was allocated within the object, we can't free the buffer when the object is deactivated */ - if (*self->next_memref != NULL) { - owns_memref = 1; - /* advance through the new memrefs so we're ready for the next allocation */ - do { - self->next_memref = &(*self->next_memref)->next; - } while (*self->next_memref != NULL); - } - - /* if at least one variable was allocated within the object, we can't free the buffer when the object is deactivated */ - if (*self->next_variable != NULL) { - owns_memref = 1; - /* advance through the new variables so we're ready for the next allocation */ - do { - self->next_variable = &(*self->next_variable)->next; - } while (*self->next_variable != NULL); - } - - return owns_memref; -} - static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, uint32_t index) { - if (self->triggers[index].owns_memrefs) { - /* if the trigger has one or more memrefs in its buffer, we can't free the buffer. - * just null out the trigger so the runtime processor will skip it - */ - rc_reset_trigger(self->triggers[index].trigger); - self->triggers[index].trigger = NULL; - } - else { - /* trigger doesn't own any memrefs, go ahead and free it, then replace it with the last trigger */ - free(self->triggers[index].buffer); + /* free the trigger, then replace it with the last trigger */ + free(self->triggers[index].buffer); - if (--self->trigger_count > index) - memcpy(&self->triggers[index], &self->triggers[self->trigger_count], sizeof(rc_runtime_trigger_t)); - } + if (--self->trigger_count > index) + memcpy(&self->triggers[index], &self->triggers[self->trigger_count], sizeof(rc_runtime_trigger_t)); } void rc_runtime_deactivate_achievement(rc_runtime_t* self, uint32_t id) { @@ -126,7 +121,8 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* void* trigger_buffer; rc_trigger_t* trigger; rc_runtime_trigger_t* runtime_trigger; - rc_parse_state_t parse; + rc_preparse_state_t preparse; + const char* preparse_memaddr = memaddr; uint8_t md5[16]; int32_t size; uint32_t i; @@ -169,7 +165,12 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* } /* item has not been previously registered, determine how much space we need for it, and allocate it */ - size = rc_trigger_size(memaddr); + rc_init_preparse_state(&preparse, NULL, 0); + preparse.parse.existing_memrefs = self->memrefs; + trigger = RC_ALLOC(rc_trigger_t, &preparse.parse); + rc_parse_trigger_internal(trigger, &preparse_memaddr, &preparse.parse); + + size = preparse.parse.offset; if (size < 0) return size; @@ -178,16 +179,15 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* return RC_OUT_OF_MEMORY; /* populate the item, using the communal memrefs pool */ - rc_init_parse_state(&parse, trigger_buffer, L, funcs_idx); - parse.first_memref = &self->memrefs; - trigger = RC_ALLOC(rc_trigger_t, &parse); - rc_parse_trigger_internal(trigger, &memaddr, &parse); - rc_destroy_parse_state(&parse); + rc_reset_parse_state(&preparse.parse, trigger_buffer, L, funcs_idx); + rc_preparse_reserve_memrefs(&preparse, self->memrefs); + trigger = RC_ALLOC(rc_trigger_t, &preparse.parse); + rc_parse_trigger_internal(trigger, &memaddr, &preparse.parse); + rc_destroy_preparse_state(&preparse); - if (parse.offset < 0) { + if (preparse.parse.offset < 0) { free(trigger_buffer); - *self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */ - return parse.offset; + return preparse.parse.offset; } /* grow the trigger buffer if necessary */ @@ -200,7 +200,6 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* if (!self->triggers) { free(trigger_buffer); - *self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */ return RC_OUT_OF_MEMORY; } } @@ -213,11 +212,9 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* runtime_trigger->invalid_memref = NULL; memcpy(runtime_trigger->md5, md5, 16); runtime_trigger->serialized_size = 0; - runtime_trigger->owns_memrefs = rc_runtime_allocated_memrefs(self); ++self->trigger_count; /* reset it, and return it */ - trigger->memrefs = NULL; rc_reset_trigger(trigger); return RC_OK; } @@ -285,20 +282,11 @@ int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, uint32_t } static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, uint32_t index) { - if (self->lboards[index].owns_memrefs) { - /* if the lboard has one or more memrefs in its buffer, we can't free the buffer. - * just null out the lboard so the runtime processor will skip it - */ - rc_reset_lboard(self->lboards[index].lboard); - self->lboards[index].lboard = NULL; - } - else { - /* lboard doesn't own any memrefs, go ahead and free it, then replace it with the last lboard */ - free(self->lboards[index].buffer); + /* free the lboard, then replace it with the last lboard */ + free(self->lboards[index].buffer); - if (--self->lboard_count > index) - memcpy(&self->lboards[index], &self->lboards[self->lboard_count], sizeof(rc_runtime_lboard_t)); - } + if (--self->lboard_count > index) + memcpy(&self->lboards[index], &self->lboards[self->lboard_count], sizeof(rc_runtime_lboard_t)); } void rc_runtime_deactivate_lboard(rc_runtime_t* self, uint32_t id) { @@ -314,7 +302,7 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* mema void* lboard_buffer; uint8_t md5[16]; rc_lboard_t* lboard; - rc_parse_state_t parse; + rc_preparse_state_t preparse; rc_runtime_lboard_t* runtime_lboard; int size; uint32_t i; @@ -357,7 +345,12 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* mema } /* item has not been previously registered, determine how much space we need for it, and allocate it */ - size = rc_lboard_size(memaddr); + rc_init_preparse_state(&preparse, NULL, 0); + preparse.parse.existing_memrefs = self->memrefs; + lboard = RC_ALLOC(rc_lboard_t, &preparse.parse); + rc_parse_lboard_internal(lboard, memaddr, &preparse.parse); + + size = preparse.parse.offset; if (size < 0) return size; @@ -366,16 +359,15 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* mema return RC_OUT_OF_MEMORY; /* populate the item, using the communal memrefs pool */ - rc_init_parse_state(&parse, lboard_buffer, L, funcs_idx); - lboard = RC_ALLOC(rc_lboard_t, &parse); - parse.first_memref = &self->memrefs; - rc_parse_lboard_internal(lboard, memaddr, &parse); - rc_destroy_parse_state(&parse); + rc_reset_parse_state(&preparse.parse, lboard_buffer, L, funcs_idx); + rc_preparse_reserve_memrefs(&preparse, self->memrefs); + lboard = RC_ALLOC(rc_lboard_t, &preparse.parse); + rc_parse_lboard_internal(lboard, memaddr, &preparse.parse); + rc_destroy_preparse_state(&preparse); - if (parse.offset < 0) { + if (preparse.parse.offset < 0) { free(lboard_buffer); - *self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */ - return parse.offset; + return preparse.parse.offset; } /* grow the lboard buffer if necessary */ @@ -388,7 +380,6 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* mema if (!self->lboards) { free(lboard_buffer); - *self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */ return RC_OUT_OF_MEMORY; } } @@ -402,10 +393,8 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* mema runtime_lboard->invalid_memref = NULL; memcpy(runtime_lboard->md5, md5, 16); runtime_lboard->serialized_size = 0; - runtime_lboard->owns_memrefs = rc_runtime_allocated_memrefs(self); /* reset it, and return it */ - lboard->memrefs = NULL; rc_reset_lboard(lboard); return RC_OK; } @@ -429,9 +418,7 @@ int rc_runtime_format_lboard_value(char* buffer, int size, int32_t value, int fo int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua_State* L, int funcs_idx) { rc_richpresence_t* richpresence; - rc_runtime_richpresence_t* previous; - rc_runtime_richpresence_t** previous_ptr; - rc_parse_state_t parse; + rc_preparse_state_t preparse; uint8_t md5[16]; int size; @@ -441,47 +428,29 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua rc_runtime_checksum(script, md5); /* look for existing match */ - previous_ptr = NULL; - previous = self->richpresence; - while (previous) { - if (previous && self->richpresence->richpresence && memcmp(self->richpresence->md5, md5, 16) == 0) { - /* unchanged. reset all of the conditions */ - rc_reset_richpresence(self->richpresence->richpresence); + if (self->richpresence && self->richpresence->richpresence && memcmp(self->richpresence->md5, md5, 16) == 0) { + /* unchanged. reset all of the conditions */ + rc_reset_richpresence(self->richpresence->richpresence); - /* move to front of linked list*/ - if (previous_ptr) { - *previous_ptr = previous->previous; - if (!self->richpresence->owns_memrefs) { - free(self->richpresence->buffer); - previous->previous = self->richpresence->previous; - } - else { - previous->previous = self->richpresence; - } - - self->richpresence = previous; - } - - /* return success*/ - return RC_OK; - } - - previous_ptr = &previous->previous; - previous = previous->previous; + /* return success*/ + return RC_OK; } /* no existing match found, parse script */ - size = rc_richpresence_size(script); + rc_init_preparse_state(&preparse, NULL, 0); + preparse.parse.existing_memrefs = self->memrefs; + richpresence = RC_ALLOC(rc_richpresence_t, &preparse.parse); + preparse.parse.variables = &richpresence->values; + rc_parse_richpresence_internal(richpresence, script, &preparse.parse); + + size = preparse.parse.offset; if (size < 0) return size; - /* if the previous script doesn't have any memrefs, free it */ - previous = self->richpresence; - if (previous) { - if (!previous->owns_memrefs) { - free(previous->buffer); - previous = previous->previous; - } + /* if there's a previous script, free it */ + if (self->richpresence) { + free(self->richpresence->buffer); + free(self->richpresence); } /* allocate and process the new script */ @@ -489,34 +458,26 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua if (!self->richpresence) return RC_OUT_OF_MEMORY; - self->richpresence->previous = previous; - self->richpresence->owns_memrefs = 0; memcpy(self->richpresence->md5, md5, sizeof(md5)); - self->richpresence->buffer = malloc(size); + self->richpresence->buffer = malloc(size); if (!self->richpresence->buffer) return RC_OUT_OF_MEMORY; - rc_init_parse_state(&parse, self->richpresence->buffer, L, funcs_idx); - self->richpresence->richpresence = richpresence = RC_ALLOC(rc_richpresence_t, &parse); - parse.first_memref = &self->memrefs; - parse.variables = &self->variables; - rc_parse_richpresence_internal(richpresence, script, &parse); - rc_destroy_parse_state(&parse); + rc_reset_parse_state(&preparse.parse, self->richpresence->buffer, L, funcs_idx); + rc_preparse_reserve_memrefs(&preparse, self->memrefs); + richpresence = RC_ALLOC(rc_richpresence_t, &preparse.parse); + preparse.parse.variables = &richpresence->values; + rc_parse_richpresence_internal(richpresence, script, &preparse.parse); + rc_destroy_preparse_state(&preparse); - if (parse.offset < 0) { + if (preparse.parse.offset < 0) { free(self->richpresence->buffer); free(self->richpresence); - self->richpresence = previous; - *self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */ - return parse.offset; + self->richpresence = NULL; + return preparse.parse.offset; } - self->richpresence->owns_memrefs = rc_runtime_allocated_memrefs(self); - - richpresence->memrefs = NULL; - richpresence->variables = NULL; - if (!richpresence->first_display || !richpresence->first_display->display) { /* non-existant rich presence */ self->richpresence->richpresence = NULL; @@ -524,6 +485,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua else { /* reset all of the conditions */ rc_reset_richpresence(richpresence); + self->richpresence->richpresence = richpresence; } return RC_OK; @@ -555,7 +517,6 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha runtime_event.value = 0; rc_update_memref_values(self->memrefs, peek, ud); - rc_update_variables(self->variables, peek, ud, L); for (i = self->trigger_count - 1; i >= 0; --i) { rc_trigger_t* trigger = self->triggers[i].trigger; @@ -725,7 +686,6 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha } void rc_runtime_reset(rc_runtime_t* self) { - rc_value_t* variable; uint32_t i; for (i = 0; i < self->trigger_count; ++i) { @@ -740,9 +700,6 @@ void rc_runtime_reset(rc_runtime_t* self) { if (self->richpresence && self->richpresence->richpresence) rc_reset_richpresence(self->richpresence->richpresence); - - for (variable = self->variables; variable; variable = variable->next) - rc_reset_value(variable); } static int rc_condset_contains_memref(const rc_condset_t* condset, const rc_memref_t* memref) { @@ -826,47 +783,41 @@ static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref } void rc_runtime_invalidate_address(rc_runtime_t* self, uint32_t address) { - rc_memref_t** last_memref = &self->memrefs; - rc_memref_t* memref = self->memrefs; + rc_memref_list_t* memref_list = &self->memrefs->memrefs; + do { + rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_stop = memref + memref_list->count; - while (memref) { - if (memref->address == address && !memref->value.is_indirect) { - /* remove the invalid memref from the chain so we don't try to evaluate it in the future. - * it's still there, so anything referencing it will continue to fetch 0. - */ - *last_memref = memref->next; - - rc_runtime_invalidate_memref(self, memref); - break; + for (; memref < memref_stop; ++memref) { + if (memref->address == address) { + memref->value.type = RC_VALUE_TYPE_NONE; + rc_runtime_invalidate_memref(self, memref); + } } - last_memref = &memref->next; - memref = *last_memref; - } + memref_list = memref_list->next; + } while (memref_list); } void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler) { - rc_memref_t** last_memref = &self->memrefs; - rc_memref_t* memref = self->memrefs; int num_invalid = 0; + rc_memref_list_t* memref_list = &self->memrefs->memrefs; + do { + rc_memref_t* memref = memref_list->items; + const rc_memref_t* memref_stop = memref + memref_list->count; - while (memref) { - if (!memref->value.is_indirect && !validate_handler(memref->address)) { - /* remove the invalid memref from the chain so we don't try to evaluate it in the future. - * it's still there, so anything referencing it will continue to fetch 0. - */ - *last_memref = memref->next; + for (; memref < memref_stop; ++memref) { + if (!validate_handler(memref->address)) { + memref->value.type = RC_VALUE_TYPE_NONE; + rc_runtime_invalidate_memref(self, memref); - rc_runtime_invalidate_memref(self, memref); - ++num_invalid; - } - else { - last_memref = &memref->next; + ++num_invalid; + } } - memref = *last_memref; - } + memref_list = memref_list->next; + } while (memref_list); if (num_invalid) { rc_runtime_event_t runtime_event; diff --git a/dep/rcheevos/src/rcheevos/runtime_progress.c b/dep/rcheevos/src/rcheevos/runtime_progress.c index 3e34d7da7..5565e4d6c 100644 --- a/dep/rcheevos/src/rcheevos/runtime_progress.c +++ b/dep/rcheevos/src/rcheevos/runtime_progress.c @@ -127,11 +127,7 @@ static void rc_runtime_progress_init(rc_runtime_progress_t* progress, const rc_r static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress) { - rc_memref_t* memref; - uint32_t count = 0; - - for (memref = progress->runtime->memrefs; memref; memref = memref->next) - ++count; + uint32_t count = rc_memrefs_count_memrefs(progress->runtime->memrefs); if (count == 0) return RC_OK; @@ -145,15 +141,24 @@ static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress) } else { uint32_t flags = 0; - for (memref = progress->runtime->memrefs; memref; memref = memref->next) { - flags = memref->value.size; - if (memref->value.changed) - flags |= RC_MEMREF_FLAG_CHANGED_THIS_FRAME; + const rc_memref_list_t* memref_list = &progress->runtime->memrefs->memrefs; + const rc_memref_t* memref; - rc_runtime_progress_write_uint(progress, memref->address); - rc_runtime_progress_write_uint(progress, flags); - rc_runtime_progress_write_uint(progress, memref->value.value); - rc_runtime_progress_write_uint(progress, memref->value.prior); + for (; memref_list; memref_list = memref_list->next) { + const rc_memref_t* memref_end; + + memref = memref_list->items; + memref_end = memref + memref_list->count; + for (; memref < memref_end; ++memref) { + flags = memref->value.size; + if (memref->value.changed) + flags |= RC_MEMREF_FLAG_CHANGED_THIS_FRAME; + + rc_runtime_progress_write_uint(progress, memref->address); + rc_runtime_progress_write_uint(progress, flags); + rc_runtime_progress_write_uint(progress, memref->value.value); + rc_runtime_progress_write_uint(progress, memref->value.prior); + } } } @@ -162,13 +167,77 @@ static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress) return RC_OK; } +static void rc_runtime_progress_update_modified_memrefs(rc_runtime_progress_t* progress) +{ + rc_typed_value_t value, prior_value, modifier, prior_modifier; + rc_modified_memref_list_t* modified_memref_list; + rc_modified_memref_t* modified_memref; + rc_operand_t prior_parent_operand, prior_modifier_operand; + rc_memref_t prior_parent_memref, prior_modifier_memref; + + modified_memref_list = &progress->runtime->memrefs->modified_memrefs; + for (; modified_memref_list; modified_memref_list = modified_memref_list->next) { + const rc_modified_memref_t* modified_memref_end; + modified_memref = modified_memref_list->items; + modified_memref_end = modified_memref + modified_memref_list->count; + for (; modified_memref < modified_memref_end; ++modified_memref) { + modified_memref->memref.value.changed = 0; + + /* indirect memref values are stored in conditions */ + if (modified_memref->modifier_type == RC_OPERATOR_INDIRECT_READ) + continue; + + /* non-indirect memref values can be reconstructed from the parents */ + memcpy(&prior_parent_operand, &modified_memref->parent, sizeof(prior_parent_operand)); + if (rc_operand_is_memref(&prior_parent_operand)) { + memcpy(&prior_parent_memref, modified_memref->parent.value.memref, sizeof(prior_parent_memref)); + prior_parent_memref.value.value = prior_parent_memref.value.prior; + modified_memref->memref.value.changed |= prior_parent_memref.value.changed; + prior_parent_operand.value.memref = &prior_parent_memref; + } + + memcpy(&prior_modifier_operand, &modified_memref->modifier, sizeof(prior_modifier_operand)); + if (rc_operand_is_memref(&prior_modifier_operand)) { + memcpy(&prior_modifier_memref, modified_memref->modifier.value.memref, sizeof(prior_modifier_memref)); + prior_modifier_memref.value.value = prior_modifier_memref.value.prior; + modified_memref->memref.value.changed |= prior_modifier_memref.value.changed; + prior_modifier_operand.value.memref = &prior_modifier_memref; + } + + rc_evaluate_operand(&value, &modified_memref->parent, NULL); + rc_evaluate_operand(&modifier, &modified_memref->modifier, NULL); + rc_evaluate_operand(&prior_value, &prior_parent_operand, NULL); + rc_evaluate_operand(&prior_modifier, &prior_modifier_operand, NULL); + + if (modified_memref->modifier_type == RC_OPERATOR_SUB_PARENT) { + rc_typed_value_negate(&value); + rc_typed_value_add(&value, &modifier); + + rc_typed_value_negate(&prior_value); + rc_typed_value_add(&prior_value, &prior_modifier); + } + else { + rc_typed_value_combine(&value, &modifier, modified_memref->modifier_type); + rc_typed_value_combine(&prior_value, &prior_modifier, modified_memref->modifier_type); + } + + rc_typed_value_convert(&value, modified_memref->memref.value.type); + modified_memref->memref.value.value = value.value.u32; + + rc_typed_value_convert(&prior_value, modified_memref->memref.value.type); + modified_memref->memref.value.prior = prior_value.value.u32; + } + } +} + static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress) { uint32_t entries; uint32_t address, flags, value, prior; uint8_t size; + rc_memref_list_t* unmatched_memref_list = &progress->runtime->memrefs->memrefs; + rc_memref_t* first_unmatched_memref = unmatched_memref_list->items; rc_memref_t* memref; - rc_memref_t* first_unmatched_memref = progress->runtime->memrefs; /* re-read the chunk size to determine how many memrefs are present */ progress->offset -= 4; @@ -183,24 +252,46 @@ static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress) size = flags & 0xFF; memref = first_unmatched_memref; - while (memref) { - if (memref->address == address && memref->value.size == size) { - memref->value.value = value; - memref->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0; - memref->value.prior = prior; + if (memref->address == address && memref->value.size == size) { + memref->value.value = value; + memref->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0; + memref->value.prior = prior; - if (memref == first_unmatched_memref) - first_unmatched_memref = memref->next; - - break; + first_unmatched_memref++; + if (first_unmatched_memref >= unmatched_memref_list->items + unmatched_memref_list->count) { + unmatched_memref_list = unmatched_memref_list->next; + if (!unmatched_memref_list) + break; + first_unmatched_memref = unmatched_memref_list->items; } + } + else { + rc_memref_list_t* memref_list = unmatched_memref_list; + do { + ++memref; + if (memref >= memref_list->items + memref_list->count) { + memref_list = memref_list->next; + if (!memref_list) + break; - memref = memref->next; + memref = memref_list->items; + } + + if (memref->address == address && memref->value.size == size) { + memref->value.value = value; + memref->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0; + memref->value.prior = prior; + break; + } + + } while (1); } --entries; } + rc_runtime_progress_update_modified_memrefs(progress); + return RC_OK; } @@ -215,7 +306,10 @@ static int rc_runtime_progress_is_indirect_memref(rc_operand_t* oper) return 0; default: - return oper->value.memref->value.is_indirect; + if (oper->value.memref->value.memref_type != RC_MEMREF_TYPE_MODIFIED_MEMREF) + return 0; + + return ((const rc_modified_memref_t*)oper->value.memref)->modifier_type == RC_OPERATOR_INDIRECT_READ; } } @@ -317,8 +411,8 @@ static uint32_t rc_runtime_progress_should_serialize_variable_condset(const rc_c { const rc_condition_t* condition; - /* predetermined presence of pause flag or indirect memrefs - must serialize */ - if (conditions->has_pause || conditions->has_indirect_memrefs) + /* predetermined presence of pause flag - must serialize */ + if (conditions->has_pause) return RC_VAR_FLAG_HAS_COND_DATA; /* if any conditions has required hits, must serialize */ @@ -358,12 +452,15 @@ static int rc_runtime_progress_write_variable(rc_runtime_progress_t* progress, c static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress) { - uint32_t count = 0; - const rc_value_t* variable; + uint32_t count; + const rc_value_t* value; int result; - for (variable = progress->runtime->variables; variable; variable = variable->next) - ++count; + if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence) + return RC_OK; + + value = progress->runtime->richpresence->richpresence->values; + count = rc_count_values(value); if (count == 0) return RC_OK; @@ -374,14 +471,14 @@ static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress) rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_VARIABLES); rc_runtime_progress_write_uint(progress, count); - for (variable = progress->runtime->variables; variable; variable = variable->next) { - uint32_t djb2 = rc_djb2(variable->name); + for (; value; value = value->next) { + const uint32_t djb2 = rc_djb2(value->name); if (progress->offset + 16 > progress->buffer_size) return RC_INSUFFICIENT_BUFFER; rc_runtime_progress_write_uint(progress, djb2); - result = rc_runtime_progress_write_variable(progress, variable); + result = rc_runtime_progress_write_variable(progress, value); if (result != RC_OK) return result; } @@ -418,19 +515,20 @@ static int rc_runtime_progress_read_variables(rc_runtime_progress_t* progress) }; struct rc_pending_value_t local_pending_variables[32]; struct rc_pending_value_t* pending_variables; - rc_value_t* variable; + rc_value_t* value; uint32_t count, serialized_count; int result; - uint32_t i; + int32_t i; serialized_count = rc_runtime_progress_read_uint(progress); if (serialized_count == 0) return RC_OK; - count = 0; - for (variable = progress->runtime->variables; variable; variable = variable->next) - ++count; + if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence) + return RC_OK; + value = progress->runtime->richpresence->richpresence->values; + count = rc_count_values(value); if (count == 0) return RC_OK; @@ -443,22 +541,22 @@ static int rc_runtime_progress_read_variables(rc_runtime_progress_t* progress) return RC_OUT_OF_MEMORY; } - count = 0; - for (variable = progress->runtime->variables; variable; variable = variable->next) { - pending_variables[count].variable = variable; - pending_variables[count].djb2 = rc_djb2(variable->name); - ++count; + i = (int32_t)count; + for (; value; value = value->next) { + --i; + pending_variables[i].variable = value; + pending_variables[i].djb2 = rc_djb2(value->name); } result = RC_OK; for (; serialized_count > 0 && result == RC_OK; --serialized_count) { uint32_t djb2 = rc_runtime_progress_read_uint(progress); - for (i = 0; i < count; ++i) { + for (i = (int32_t)count - 1; i >= 0; --i) { if (pending_variables[i].djb2 == djb2) { - variable = pending_variables[i].variable; - result = rc_runtime_progress_read_variable(progress, variable); + value = pending_variables[i].variable; + result = rc_runtime_progress_read_variable(progress, value); if (result == RC_OK) { - if (i < count - 1) + if (i < (int32_t)count - 1) memcpy(&pending_variables[i], &pending_variables[count - 1], sizeof(struct rc_pending_value_t)); count--; } @@ -742,7 +840,7 @@ static int rc_runtime_progress_read_rich_presence(rc_runtime_progress_t* progres return RC_OK; if (!rc_runtime_progress_match_md5(progress, progress->runtime->richpresence->md5)) { - rc_reset_richpresence(progress->runtime->richpresence->richpresence); + rc_reset_richpresence_triggers(progress->runtime->richpresence->richpresence); return RC_OK; } @@ -958,7 +1056,7 @@ int rc_runtime_deserialize_progress_sized(rc_runtime_t* runtime, const uint8_t* } if (!seen_rich_presence && runtime->richpresence && runtime->richpresence->richpresence) - rc_reset_richpresence(runtime->richpresence->richpresence); + rc_reset_richpresence_triggers(runtime->richpresence->richpresence); } return result; diff --git a/dep/rcheevos/src/rcheevos/trigger.c b/dep/rcheevos/src/rcheevos/trigger.c index 71e186c01..1d529e01a 100644 --- a/dep/rcheevos/src/rcheevos/trigger.c +++ b/dep/rcheevos/src/rcheevos/trigger.c @@ -16,21 +16,20 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars parse->measured_as_percent = 0; if (*aux == 's' || *aux == 'S') { - self->requirement = 0; + self->requirement = NULL; } else { - self->requirement = rc_parse_condset(&aux, parse, 0); + self->requirement = rc_parse_condset(&aux, parse); - if (parse->offset < 0) { + if (parse->offset < 0) return; - } - self->requirement->next = 0; + self->requirement->next = NULL; } while (*aux == 's' || *aux == 'S') { aux++; - *next = rc_parse_condset(&aux, parse, 0); + *next = rc_parse_condset(&aux, parse); if (parse->offset < 0) { return; @@ -39,7 +38,7 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars next = &(*next)->next; } - *next = 0; + *next = NULL; *memaddr = aux; self->measured_target = parse->measured_target; @@ -47,39 +46,46 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars self->measured_as_percent = parse->measured_as_percent; self->state = RC_TRIGGER_STATE_WAITING; self->has_hits = 0; - self->has_required_hits = parse->has_required_hits; + self->has_memrefs = 0; } int rc_trigger_size(const char* memaddr) { - rc_trigger_t* self; - rc_parse_state_t parse; - rc_memref_t* memrefs; - rc_init_parse_state(&parse, 0, 0, 0); - rc_init_parse_state_memrefs(&parse, &memrefs); + rc_trigger_with_memrefs_t* trigger; + rc_preparse_state_t preparse; + rc_init_preparse_state(&preparse, NULL, 0); - self = RC_ALLOC(rc_trigger_t, &parse); - rc_parse_trigger_internal(self, &memaddr, &parse); + trigger = RC_ALLOC(rc_trigger_with_memrefs_t, &preparse.parse); + rc_parse_trigger_internal(&trigger->trigger, &memaddr, &preparse.parse); + rc_preparse_alloc_memrefs(NULL, &preparse); - rc_destroy_parse_state(&parse); - return parse.offset; + rc_destroy_preparse_state(&preparse); + return preparse.parse.offset; } rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) { - rc_trigger_t* self; - rc_parse_state_t parse; + rc_trigger_with_memrefs_t* trigger; + rc_preparse_state_t preparse; + const char* preparse_memaddr = memaddr; if (!buffer || !memaddr) return NULL; - rc_init_parse_state(&parse, buffer, L, funcs_ndx); + /* first pass : determine how many memrefs are needed */ + rc_init_preparse_state(&preparse, L, funcs_ndx); + trigger = RC_ALLOC(rc_trigger_with_memrefs_t, &preparse.parse); + rc_parse_trigger_internal(&trigger->trigger, &preparse_memaddr, &preparse.parse); - self = RC_ALLOC(rc_trigger_t, &parse); - rc_init_parse_state_memrefs(&parse, &self->memrefs); + /* allocate the trigger and memrefs */ + rc_reset_parse_state(&preparse.parse, buffer, L, funcs_ndx); + trigger = RC_ALLOC(rc_trigger_with_memrefs_t, &preparse.parse); + rc_preparse_alloc_memrefs(&trigger->memrefs, &preparse); - rc_parse_trigger_internal(self, &memaddr, &parse); + /* parse the trigger */ + rc_parse_trigger_internal(&trigger->trigger, &memaddr, &preparse.parse); + trigger->trigger.has_memrefs = 1; - rc_destroy_parse_state(&parse); - return (parse.offset >= 0) ? self : NULL; + rc_destroy_preparse_state(&preparse); + return (preparse.parse.offset >= 0) ? &trigger->trigger : NULL; } int rc_trigger_state_active(int state) @@ -124,9 +130,27 @@ static void rc_reset_trigger_hitcounts(rc_trigger_t* self) { } } +static void rc_update_trigger_memrefs(rc_trigger_t* self, rc_peek_t peek, void* ud) { + if (self->has_memrefs) { + rc_trigger_with_memrefs_t* trigger = (rc_trigger_with_memrefs_t*)self; + rc_update_memref_values(&trigger->memrefs, peek, ud); + } +} + +rc_memrefs_t* rc_trigger_get_memrefs(rc_trigger_t* self) { + if (self->has_memrefs) { + rc_trigger_with_memrefs_t* trigger = (rc_trigger_with_memrefs_t*)self; + return &trigger->memrefs; + } + + return NULL; +} + int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L) { rc_eval_state_t eval_state; rc_condset_t* condset; + rc_typed_value_t measured_value; + int measured_from_hits = 0; int ret; char is_paused; char is_primed; @@ -143,7 +167,7 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* case RC_TRIGGER_STATE_INACTIVE: /* not yet active. update the memrefs so deltas are correct when it becomes active, then return INACTIVE */ - rc_update_memref_values(self->memrefs, peek, ud); + rc_update_trigger_memrefs(self, peek, ud); return RC_TRIGGER_STATE_INACTIVE; default: @@ -151,18 +175,29 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* } /* update the memory references */ - rc_update_memref_values(self->memrefs, peek, ud); + rc_update_trigger_memrefs(self, peek, ud); /* process the trigger */ memset(&eval_state, 0, sizeof(eval_state)); eval_state.peek = peek; eval_state.peek_userdata = ud; +#ifndef RC_DISABLE_LUA eval_state.L = L; +#else + (void)L; +#endif + + measured_value.type = RC_VALUE_TYPE_NONE; if (self->requirement != NULL) { ret = rc_test_condset(self->requirement, &eval_state); - is_paused = self->requirement->is_paused; - is_primed = eval_state.primed; + is_paused = eval_state.is_paused; + is_primed = eval_state.is_primed; + + if (eval_state.measured_value.type != RC_VALUE_TYPE_NONE) { + memcpy(&measured_value, &eval_state.measured_value, sizeof(measured_value)); + measured_from_hits = eval_state.measured_from_hits; + } } else { ret = 1; is_paused = 0; @@ -177,8 +212,17 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* do { sub |= rc_test_condset(condset, &eval_state); - sub_paused &= condset->is_paused; - sub_primed |= eval_state.primed; + sub_paused &= eval_state.is_paused; + sub_primed |= eval_state.is_primed; + + if (eval_state.measured_value.type != RC_VALUE_TYPE_NONE) { + /* if no previous Measured value was captured, or the new one is greater, keep the new one */ + if (measured_value.type == RC_VALUE_TYPE_NONE || + rc_typed_value_compare(&eval_state.measured_value, &measured_value, RC_OPERATOR_GT)) { + memcpy(&measured_value, &eval_state.measured_value, sizeof(measured_value)); + measured_from_hits = eval_state.measured_from_hits; + } + } condset = condset->next; } while (condset); @@ -193,15 +237,15 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* /* if paused, the measured value may not be captured, keep the old value */ if (!is_paused) { - rc_typed_value_convert(&eval_state.measured_value, RC_VALUE_TYPE_UNSIGNED); - self->measured_value = eval_state.measured_value.value.u32; + rc_typed_value_convert(&measured_value, RC_VALUE_TYPE_UNSIGNED); + self->measured_value = measured_value.value.u32; } /* if any ResetIf condition was true, reset the hit counts */ if (eval_state.was_reset) { /* if the measured value came from a hit count, reset it. do this before calling * rc_reset_trigger_hitcounts in case we need to call rc_condset_is_measured_from_hitcount */ - if (eval_state.measured_from_hits) { + if (measured_from_hits) { self->measured_value = 0; } else if (is_paused && self->measured_value) { diff --git a/dep/rcheevos/src/rcheevos/value.c b/dep/rcheevos/src/rcheevos/value.c index 25f2204e3..34fbdefbe 100644 --- a/dep/rcheevos/src/rcheevos/value.c +++ b/dep/rcheevos/src/rcheevos/value.c @@ -5,8 +5,6 @@ #include /* FLT_EPSILON */ #include /* fmod */ - - int rc_is_valid_variable_character(char ch, int is_first) { if (is_first) { if (!isalpha((unsigned char)ch)) @@ -27,7 +25,7 @@ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse do { parse->measured_target = 0; /* passing is_value=1 should prevent any conflicts, but clear it out anyway */ - *next_clause = rc_parse_condset(memaddr, parse, 1); + *next_clause = rc_parse_condset(memaddr, parse); if (parse->offset < 0) { return; } @@ -52,174 +50,238 @@ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse (*next_clause)->next = 0; } -void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) { +static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) { + rc_condset_with_trailing_conditions_t* condset_with_conditions; rc_condition_t** next; rc_condset_t** next_clause; + rc_condset_t* condset; + rc_condition_t local_cond; rc_condition_t* cond; + uint32_t num_measured_conditions; char buffer[64] = "A:"; const char* buffer_ptr; char* ptr; + char c; /* convert legacy format into condset */ - self->conditions = RC_ALLOC(rc_condset_t, parse); - memset(self->conditions, 0, sizeof(rc_condset_t)); + next_clause = &self->conditions; + do { + num_measured_conditions = 0; - next = &self->conditions->conditions; - next_clause = &self->conditions->next; - - for (;; ++(*memaddr)) { - buffer[0] = 'A'; /* reset to AddSource */ - ptr = &buffer[2]; - - /* extract the next clause */ - for (;; ++(*memaddr)) { - switch (**memaddr) { - case '_': /* add next */ - case '$': /* maximum of */ - case '\0': /* end of string */ - case ':': /* end of leaderboard clause */ - case ')': /* end of rich presence macro */ - *ptr = '\0'; - break; - - case '*': - *ptr++ = '*'; - - buffer_ptr = *memaddr + 1; - if (*buffer_ptr == '-') { - buffer[0] = 'B'; /* change to SubSource */ - ++(*memaddr); /* don't copy sign */ - ++buffer_ptr; /* ignore sign when doing floating point check */ - } - else if (*buffer_ptr == '+') { - ++buffer_ptr; /* ignore sign when doing floating point check */ - } - - /* if it looks like a floating point number, add the 'f' prefix */ - while (isdigit((unsigned char)*buffer_ptr)) - ++buffer_ptr; - if (*buffer_ptr == '.') - *ptr++ = 'f'; - continue; - - default: - *ptr++ = **memaddr; - continue; + /* count the number of joiners and add one to determine the number of clauses. */ + num_measured_conditions = 1; + buffer_ptr = *memaddr; + while ((c = *buffer_ptr++) && c != '$') { + if (c == '_') { + ++num_measured_conditions; + buffer[0] = 'A'; /* reset to AddSource */ + } + else if (c == '*' && *buffer_ptr == '-') { + /* multiplication by a negative number will convert to SubSource */ + ++buffer_ptr; + buffer[0] = 'B'; } - - break; } - /* process the clause */ - buffer_ptr = buffer; - cond = rc_parse_condition(&buffer_ptr, parse, 0); + /* if last condition is SubSource, we'll need to add a dummy condition for the Measured */ + if (buffer[0] == 'B') + ++num_measured_conditions; + + condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t, + rc_condition_t, conditions, num_measured_conditions, parse); if (parse->offset < 0) return; - if (*buffer_ptr) { - /* whatever we copied as a single condition was not fully consumed */ - parse->offset = RC_INVALID_COMPARISON; - return; - } + condset = (rc_condset_t*)condset_with_conditions; + memset(condset, 0, sizeof(*condset)); + condset->num_measured_conditions = num_measured_conditions; + cond = &condset_with_conditions->conditions[0]; + + next = &condset->conditions; + + for (;; ++(*memaddr)) { + buffer[0] = 'A'; /* reset to AddSource */ + ptr = &buffer[2]; + + /* extract the next clause */ + for (;; ++(*memaddr)) { + switch (**memaddr) { + case '_': /* add next */ + case '$': /* maximum of */ + case '\0': /* end of string */ + case ':': /* end of leaderboard clause */ + case ')': /* end of rich presence macro */ + *ptr = '\0'; + break; + + case '*': + *ptr++ = '*'; + + buffer_ptr = *memaddr + 1; + if (*buffer_ptr == '-') { + buffer[0] = 'B'; /* change to SubSource */ + ++(*memaddr); /* don't copy sign */ + ++buffer_ptr; /* ignore sign when doing floating point check */ + } + else if (*buffer_ptr == '+') { + ++buffer_ptr; /* ignore sign when doing floating point check */ + } + + /* if it looks like a floating point number, add the 'f' prefix */ + while (isdigit((unsigned char)*buffer_ptr)) + ++buffer_ptr; + if (*buffer_ptr == '.') + *ptr++ = 'f'; + continue; + + default: + *ptr++ = **memaddr; + continue; + } - switch (cond->oper) { - case RC_OPERATOR_MULT: - case RC_OPERATOR_DIV: - case RC_OPERATOR_AND: - case RC_OPERATOR_XOR: - case RC_OPERATOR_MOD: - case RC_OPERATOR_ADD: - case RC_OPERATOR_SUB: - case RC_OPERATOR_NONE: break; + } - default: + /* process the clause */ + if (!parse->buffer) + cond = &local_cond; + + buffer_ptr = buffer; + rc_parse_condition_internal(cond, &buffer_ptr, parse); + if (parse->offset < 0) + return; + + if (*buffer_ptr) { + /* whatever we copied as a single condition was not fully consumed */ + parse->offset = RC_INVALID_COMPARISON; + return; + } + + if (!rc_operator_is_modifying(cond->oper)) { parse->offset = RC_INVALID_OPERATOR; return; - } + } - *next = cond; - - if (**memaddr == '_') { - /* add next */ + *next = cond; next = &cond->next; - continue; + + if (**memaddr != '_') /* add next */ + break; + + rc_condition_update_parse_state(cond, parse); + ++cond; } + /* end of clause */ if (cond->type == RC_CONDITION_SUB_SOURCE) { /* cannot change SubSource to Measured. add a dummy condition */ - next = &cond->next; + rc_condition_update_parse_state(cond, parse); + if (parse->buffer) + ++cond; + buffer_ptr = "A:0"; - cond = rc_parse_condition(&buffer_ptr, parse, 0); + rc_parse_condition_internal(cond, &buffer_ptr, parse); *next = cond; + next = &cond->next; } /* convert final AddSource condition to Measured */ cond->type = RC_CONDITION_MEASURED; - cond->next = 0; + cond->next = NULL; + rc_condition_update_parse_state(cond, parse); + + /* finalize clause */ + *next_clause = condset; + next_clause = &condset->next; if (**memaddr != '$') { /* end of valid string */ - *next_clause = 0; + *next_clause = NULL; break; } /* max of ($), start a new clause */ - *next_clause = RC_ALLOC(rc_condset_t, parse); - - if (parse->buffer) /* don't clear in sizing mode or pointer will break */ - memset(*next_clause, 0, sizeof(rc_condset_t)); - - next = &(*next_clause)->conditions; - next_clause = &(*next_clause)->next; - } + ++(*memaddr); + } while (1); } void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) { + const uint8_t was_value = parse->is_value; + const rc_condition_t* condition; + parse->is_value = 1; + /* if it starts with a condition flag (M: A: B: C:), parse the conditions */ - if ((*memaddr)[1] == ':') { + if ((*memaddr)[1] == ':') rc_parse_cond_value(self, memaddr, parse); - } - else { + else rc_parse_legacy_value(self, memaddr, parse); + + if (parse->offset >= 0 && parse->buffer) { + self->name = "(unnamed)"; + self->value.value = self->value.prior = 0; + self->value.memref_type = RC_MEMREF_TYPE_VALUE; + self->value.changed = 0; + self->has_memrefs = 0; + + for (condition = self->conditions->conditions; condition; condition = condition->next) { + if (condition->type == RC_CONDITION_MEASURED) { + if (rc_operand_is_float(&condition->operand1)) { + self->value.size = RC_MEMSIZE_FLOAT; + self->value.type = RC_VALUE_TYPE_FLOAT; + } + else { + self->value.size = RC_MEMSIZE_32_BITS; + self->value.type = RC_VALUE_TYPE_UNSIGNED; + } + break; + } + } } - self->name = "(unnamed)"; - self->value.value = self->value.prior = 0; - self->value.changed = 0; - self->next = 0; + parse->is_value = was_value; } int rc_value_size(const char* memaddr) { - rc_value_t* self; - rc_parse_state_t parse; - rc_memref_t* first_memref; - rc_init_parse_state(&parse, 0, 0, 0); - rc_init_parse_state_memrefs(&parse, &first_memref); + rc_value_with_memrefs_t* value; + rc_preparse_state_t preparse; + rc_init_preparse_state(&preparse, NULL, 0); - self = RC_ALLOC(rc_value_t, &parse); - rc_parse_value_internal(self, &memaddr, &parse); + value = RC_ALLOC(rc_value_with_memrefs_t, &preparse.parse); + rc_parse_value_internal(&value->value, &memaddr, &preparse.parse); + rc_preparse_alloc_memrefs(NULL, &preparse); - rc_destroy_parse_state(&parse); - return parse.offset; + rc_destroy_preparse_state(&preparse); + return preparse.parse.offset; } rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) { - rc_value_t* self; - rc_parse_state_t parse; + rc_value_with_memrefs_t* value; + rc_preparse_state_t preparse; + const char* preparse_memaddr = memaddr; if (!buffer || !memaddr) return NULL; - rc_init_parse_state(&parse, buffer, L, funcs_ndx); + rc_init_preparse_state(&preparse, L, funcs_ndx); + value = RC_ALLOC(rc_value_with_memrefs_t, &preparse.parse); + rc_parse_value_internal(&value->value, &preparse_memaddr, &preparse.parse); - self = RC_ALLOC(rc_value_t, &parse); - rc_init_parse_state_memrefs(&parse, &self->memrefs); + rc_reset_parse_state(&preparse.parse, buffer, L, funcs_ndx); + value = RC_ALLOC(rc_value_with_memrefs_t, &preparse.parse); + rc_preparse_alloc_memrefs(&value->memrefs, &preparse); - rc_parse_value_internal(self, &memaddr, &parse); + rc_parse_value_internal(&value->value, &memaddr, &preparse.parse); + value->value.has_memrefs = 1; - rc_destroy_parse_state(&parse); - return (parse.offset >= 0) ? self : NULL; + rc_destroy_preparse_state(&preparse); + return (preparse.parse.offset >= 0) ? &value->value : NULL; +} + +static void rc_update_value_memrefs(rc_value_t* self, rc_peek_t peek, void* ud) { + if (self->has_memrefs) { + rc_value_with_memrefs_t* value = (rc_value_with_memrefs_t*)self; + rc_update_memref_values(&value->memrefs, peek, ud); + } } int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud, lua_State* L) { @@ -227,7 +289,7 @@ int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t rc_condset_t* condset; int valid = 0; - rc_update_memref_values(self->memrefs, peek, ud); + rc_update_value_memrefs(self, peek, ud); value->value.i32 = 0; value->type = RC_VALUE_TYPE_SIGNED; @@ -236,7 +298,11 @@ int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t memset(&eval_state, 0, sizeof(eval_state)); eval_state.peek = peek; eval_state.peek_userdata = ud; +#ifndef RC_DISABLE_LUA eval_state.L = L; +#else + (void)L; +#endif rc_test_condset(condset, &eval_state); @@ -248,25 +314,21 @@ int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t * NOTE: ResetIf only affects the current condset when used in values! */ rc_reset_condset(condset); - - /* if the measured value came from a hit count, reset it too */ - if (eval_state.measured_from_hits) { - eval_state.measured_value.value.u32 = 0; - eval_state.measured_value.type = RC_VALUE_TYPE_UNSIGNED; - } } - if (!valid) { - /* capture the first valid measurement */ - memcpy(value, &eval_state.measured_value, sizeof(*value)); - valid = 1; - } - else { - /* multiple condsets are currently only used for the MAX_OF operation. - * only keep the condset's value if it's higher than the current highest value. - */ - if (rc_typed_value_compare(&eval_state.measured_value, value, RC_OPERATOR_GT)) + if (eval_state.measured_value.type != RC_VALUE_TYPE_NONE) { + if (!valid) { + /* capture the first valid measurement, which may be negative */ memcpy(value, &eval_state.measured_value, sizeof(*value)); + valid = 1; + } + else { + /* multiple condsets are currently only used for the MAX_OF operation. + * only keep the condset's value if it's higher than the current highest value. + */ + if (rc_typed_value_compare(&eval_state.measured_value, value, RC_OPERATOR_GT)) + memcpy(value, &eval_state.measured_value, sizeof(*value)); + } } } @@ -317,84 +379,81 @@ int rc_value_from_hits(rc_value_t* self) return 0; } -void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables) { - parse->variables = variables; - *variables = 0; -} - -rc_value_t* rc_alloc_helper_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse) -{ - rc_value_t** variables = parse->variables; +rc_value_t* rc_alloc_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse) { + rc_value_t** value_ptr = parse->variables; rc_value_t* value; const char* name; uint32_t measured_target; - while ((value = *variables) != NULL) { + if (!value_ptr) + return NULL; + + while (*value_ptr) { + value = *value_ptr; if (strncmp(value->name, memaddr, memaddr_len) == 0 && value->name[memaddr_len] == 0) return value; - variables = &value->next; + value_ptr = &value->next; } - value = RC_ALLOC_SCRATCH(rc_value_t, parse); - memset(&value->value, 0, sizeof(value->value)); - value->value.size = RC_MEMSIZE_VARIABLE; - value->memrefs = NULL; - /* capture name before calling parse as parse will update memaddr pointer */ name = rc_alloc_str(parse, memaddr, memaddr_len); if (!name) return NULL; + /* no match found, create a new entry */ + value = RC_ALLOC_SCRATCH(rc_value_t, parse); + memset(value, 0, sizeof(value->value)); + value->value.size = RC_MEMSIZE_VARIABLE; + value->next = NULL; + /* the helper variable likely has a Measured condition. capture the current measured_target so we can restore it * after generating the variable so the variable's Measured target doesn't conflict with the rest of the trigger. */ measured_target = parse->measured_target; - - /* disable variable resolution when defining a variable to prevent infinite recursion */ - variables = parse->variables; - parse->variables = NULL; rc_parse_value_internal(value, &memaddr, parse); - parse->variables = variables; - - /* restore the measured target */ parse->measured_target = measured_target; /* store name after calling parse as parse will set name to (unnamed) */ value->name = name; - /* append the new variable to the end of the list (have to re-evaluate in case any others were added) */ - while (*variables != NULL) - variables = &(*variables)->next; - *variables = value; - + *value_ptr = value; return value; } -void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L) { +uint32_t rc_count_values(const rc_value_t* values) { + uint32_t count = 0; + while (values) { + ++count; + values = values->next; + } + + return count; +} + +void rc_update_values(rc_value_t* values, rc_peek_t peek, void* ud, lua_State* L) { rc_typed_value_t result; - while (variable) { - if (rc_evaluate_value_typed(variable, &result, peek, ud, L)) { + rc_value_t* value = values; + for (; value; value = value->next) { + if (rc_evaluate_value_typed(value, &result, peek, ud, L)) { /* store the raw bytes and type to be restored by rc_typed_value_from_memref_value */ - rc_update_memref_value(&variable->value, result.value.u32); - variable->value.type = result.type; + rc_update_memref_value(&value->value, result.value.u32); + value->value.type = result.type; } - - variable = variable->next; } } -void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref) { - value->value.u32 = memref->value; +void rc_reset_values(rc_value_t* values) { + rc_value_t* value = values; - if (memref->size == RC_MEMSIZE_VARIABLE) { - /* a variable can be any of the supported types, but the raw data was copied into u32 */ - value->type = memref->type; - } - else { - /* not a variable, only u32 is supported */ - value->type = RC_VALUE_TYPE_UNSIGNED; - } + for (; value; value = value->next) + rc_reset_value(value); +} + +void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref) { + /* raw value is always u32, type can mark it as something else */ + value->value.u32 = memref->value; + value->type = memref->type; } void rc_typed_value_convert(rc_typed_value_t* value, char new_type) { @@ -483,8 +542,12 @@ void rc_typed_value_negate(rc_typed_value_t* value) { void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount) { rc_typed_value_t converted; - if (amount->type != value->type && value->type != RC_VALUE_TYPE_NONE) - amount = rc_typed_value_convert_into(&converted, amount, value->type); + if (amount->type != value->type && value->type != RC_VALUE_TYPE_NONE) { + if (amount->type == RC_VALUE_TYPE_FLOAT) + rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT); + else + amount = rc_typed_value_convert_into(&converted, amount, value->type); + } switch (value->type) { @@ -651,56 +714,56 @@ void rc_typed_value_modulus(rc_typed_value_t* value, const rc_typed_value_t* amo switch (amount->type) { - case RC_VALUE_TYPE_UNSIGNED: - if (amount->value.u32 == 0) { /* divide by zero */ - value->type = RC_VALUE_TYPE_NONE; - return; - } + case RC_VALUE_TYPE_UNSIGNED: + if (amount->value.u32 == 0) { /* divide by zero */ + value->type = RC_VALUE_TYPE_NONE; + return; + } - switch (value->type) { - case RC_VALUE_TYPE_UNSIGNED: /* integer math */ - value->value.u32 %= amount->value.u32; - return; - case RC_VALUE_TYPE_SIGNED: /* integer math */ - value->value.i32 %= (int)amount->value.u32; - return; - case RC_VALUE_TYPE_FLOAT: - amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT); + switch (value->type) { + case RC_VALUE_TYPE_UNSIGNED: /* integer math */ + value->value.u32 %= amount->value.u32; + return; + case RC_VALUE_TYPE_SIGNED: /* integer math */ + value->value.i32 %= (int)amount->value.u32; + return; + case RC_VALUE_TYPE_FLOAT: + amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT); + break; + default: + value->type = RC_VALUE_TYPE_NONE; + return; + } break; + + case RC_VALUE_TYPE_SIGNED: + if (amount->value.i32 == 0) { /* divide by zero */ + value->type = RC_VALUE_TYPE_NONE; + return; + } + + switch (value->type) { + case RC_VALUE_TYPE_SIGNED: /* integer math */ + value->value.i32 %= amount->value.i32; + return; + case RC_VALUE_TYPE_UNSIGNED: /* integer math */ + value->value.u32 %= (unsigned)amount->value.i32; + return; + case RC_VALUE_TYPE_FLOAT: + amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT); + break; + default: + value->type = RC_VALUE_TYPE_NONE; + return; + } + break; + + case RC_VALUE_TYPE_FLOAT: + break; + default: value->type = RC_VALUE_TYPE_NONE; return; - } - break; - - case RC_VALUE_TYPE_SIGNED: - if (amount->value.i32 == 0) { /* divide by zero */ - value->type = RC_VALUE_TYPE_NONE; - return; - } - - switch (value->type) { - case RC_VALUE_TYPE_SIGNED: /* integer math */ - value->value.i32 %= amount->value.i32; - return; - case RC_VALUE_TYPE_UNSIGNED: /* integer math */ - value->value.u32 %= (unsigned)amount->value.i32; - return; - case RC_VALUE_TYPE_FLOAT: - amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT); - break; - default: - value->type = RC_VALUE_TYPE_NONE; - return; - } - break; - - case RC_VALUE_TYPE_FLOAT: - break; - - default: - value->type = RC_VALUE_TYPE_NONE; - return; } if (amount->value.f32 == 0.0) { /* divide by zero */ @@ -712,6 +775,44 @@ void rc_typed_value_modulus(rc_typed_value_t* value, const rc_typed_value_t* amo value->value.f32 = (float)fmod(value->value.f32, amount->value.f32); } +void rc_typed_value_combine(rc_typed_value_t* value, rc_typed_value_t* amount, uint8_t oper) { + switch (oper) { + case RC_OPERATOR_MULT: + rc_typed_value_multiply(value, amount); + break; + + case RC_OPERATOR_DIV: + rc_typed_value_divide(value, amount); + break; + + case RC_OPERATOR_AND: + rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED); + rc_typed_value_convert(amount, RC_VALUE_TYPE_UNSIGNED); + value->value.u32 &= amount->value.u32; + break; + + case RC_OPERATOR_XOR: + rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED); + rc_typed_value_convert(amount, RC_VALUE_TYPE_UNSIGNED); + value->value.u32 ^= amount->value.u32; + break; + + case RC_OPERATOR_MOD: + rc_typed_value_modulus(value, amount); + break; + + case RC_OPERATOR_ADD: + rc_typed_value_add(value, amount); + break; + + case RC_OPERATOR_SUB: + rc_typed_value_negate(amount); + rc_typed_value_add(value, amount); + break; + } +} + + static int rc_typed_value_compare_floats(float f1, float f2, char oper) { if (f1 == f2) { /* exactly equal */ diff --git a/dep/rcheevos/src/rhash/hash.c b/dep/rcheevos/src/rhash/hash.c index 267be706a..df3dab162 100644 --- a/dep/rcheevos/src/rhash/hash.c +++ b/dep/rcheevos/src/rhash/hash.c @@ -77,7 +77,7 @@ static void* filereader_open(const char* path) return NULL; } - #if defined(__STDC_WANT_SECURE_LIB__) + #if defined(__STDC_SECURE_LIB__) /* have to use _SH_DENYNO because some cores lock the file while its loaded */ fp = _wfsopen(wpath, L"rb", _SH_DENYNO); #else @@ -90,7 +90,7 @@ static void* filereader_open(const char* path) #else /* !WINVER >= 0x0500 */ static void* filereader_open(const char* path) { - #if defined(__STDC_WANT_SECURE_LIB__) + #if defined(__STDC_SECURE_LIB__) #if defined(WINVER) /* have to use _SH_DENYNO because some cores lock the file while its loaded */ return _fsopen(path, "rb", _SH_DENYNO); @@ -99,7 +99,7 @@ static void* filereader_open(const char* path) fopen_s(&fp, path, "rb"); return fp; #endif - #else /* !__STDC_WANT_SECURE_LIB__ */ + #else /* !__STDC_SECURE_LIB__ */ return fopen(path, "rb"); #endif } @@ -2513,6 +2513,7 @@ int rc_hash_generate_from_buffer(char hash[33], uint32_t console_id, const uint8 case RC_CONSOLE_ATARI_LYNX: return rc_hash_lynx(hash, buffer, buffer_size); + case RC_CONSOLE_FAMICOM_DISK_SYSTEM: case RC_CONSOLE_NINTENDO: return rc_hash_nes(hash, buffer, buffer_size); @@ -2818,6 +2819,7 @@ int rc_hash_generate_from_file(char hash[33], uint32_t console_id, const char* p case RC_CONSOLE_ARDUBOY: case RC_CONSOLE_ATARI_7800: case RC_CONSOLE_ATARI_LYNX: + case RC_CONSOLE_FAMICOM_DISK_SYSTEM: case RC_CONSOLE_NINTENDO: case RC_CONSOLE_PC_ENGINE: case RC_CONSOLE_SUPER_CASSETTEVISION: @@ -3015,7 +3017,11 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* break; case 'a': - if (rc_path_compare_extension(ext, "a78")) + if (rc_path_compare_extension(ext, "a26")) + { + iterator->consoles[0] = RC_CONSOLE_ATARI_2600; + } + else if (rc_path_compare_extension(ext, "a78")) { iterator->consoles[0] = RC_CONSOLE_ATARI_7800; } @@ -3157,7 +3163,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* } else if (rc_path_compare_extension(ext, "fds")) { - iterator->consoles[0] = RC_CONSOLE_NINTENDO; + iterator->consoles[0] = RC_CONSOLE_FAMICOM_DISK_SYSTEM; } else if (rc_path_compare_extension(ext, "fd")) {