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"))
{