dep/rcheevos: Bump to 3a91a58

This commit is contained in:
Stenzek 2024-12-26 15:00:24 +10:00
parent fa993849f7
commit 750dd1cb87
No known key found for this signature in database
29 changed files with 4103 additions and 1440 deletions

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -48,6 +48,10 @@
<ClInclude Include="src\rc_version.h" />
<ClInclude Include="src\rhash\md5.h" />
</ItemGroup>
<ItemGroup>
<Natvis Include="src\rcheevos\rc_runtime_types.natvis" />
<Natvis Include="src\rc_client_types.natvis" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{4BA0A6D4-3AE1-42B2-9347-096FD023FF64}</ProjectGuid>
</PropertyGroup>

View File

@ -138,4 +138,10 @@
<Filter>include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="src\rc_client_types.natvis" />
<Natvis Include="src\rcheevos\rc_runtime_types.natvis">
<Filter>rcheevos</Filter>
</Natvis>
</ItemGroup>
</Project>

View File

@ -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)

View File

@ -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);

View File

@ -0,0 +1,394 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<!-- https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022 -->
<!-- https://learn.microsoft.com/en-us/visualstudio/debugger/format-specifiers-in-cpp?view=vs-2022 -->
<Type Name="rc_client_user_t">
<DisplayString>{{display_name={display_name,s} score={score}}}</DisplayString>
</Type>
<Type Name="rc_client_game_t">
<DisplayString>{{title={title,s} id={id}}}</DisplayString>
</Type>
<Type Name="rc_client_subset_t">
<DisplayString>{{title={title,s} id={id}}}</DisplayString>
</Type>
<Type Name="__rc_client_achievement_state_enum_t">
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE">{RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE">{RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED">{RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_STATE_DISABLED">{RC_CLIENT_ACHIEVEMENT_STATE_DISABLED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_achievement_category_enum_t">
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_CATEGORY_NONE">{RC_CLIENT_ACHIEVEMENT_CATEGORY_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE">{RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_CATEGORY_UNOFFICIAL">{RC_CLIENT_ACHIEVEMENT_CATEGORY_UNOFFICIAL}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL">{RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_achievement_type_enum_t">
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD">{RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE">{RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION">{RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_TYPE_WIN">{RC_CLIENT_ACHIEVEMENT_TYPE_WIN}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_achievement_bucket_enum_t">
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_UNKNOWN">{RC_CLIENT_ACHIEVEMENT_BUCKET_UNKNOWN}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED">{RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED">{RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED">{RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL">{RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED">{RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE">{RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED">{RC_CLIENT_ACHIEVEMENT_BUCKET_ALMOST_THERE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_BUCKET_UNSYNCED">{RC_CLIENT_ACHIEVEMENT_BUCKET_UNSYNCED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_achievement_unlocked_enum_t">
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE">{RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE">{RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE">{RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_ACHIEVEMENT_UNLOCKED_BOTH">{RC_CLIENT_ACHIEVEMENT_UNLOCKED_BOTH}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_achievement_t">
<DisplayString>{{title={title,s} id={id}}}</DisplayString>
<Expand>
<Item Name="title">title</Item>
<Item Name="description">description</Item>
<Item Name="points">points</Item>
<Item Name="id">id</Item>
<Item Name="state">*((__rc_client_achievement_state_enum_t*)&amp;state)</Item>
<Item Name="type">*((__rc_client_achievement_type_enum_t*)&amp;state)</Item>
<Item Name="category">*((__rc_client_achievement_category_enum_t*)&amp;category)</Item>
<Item Name="bucket">*((__rc_client_achievement_state_enum_t*)&amp;bucket)</Item>
<Item Name="unlocked">*((__rc_client_achievement_unlocked_enum_t*)&amp;unlocked)</Item>
</Expand>
</Type>
<Type Name="rc_client_achievement_bucket_t">
<DisplayString>{{label={label,s} count={num_achievements}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>num_achievements</Size>
<ValueNode>achievements[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="rc_client_achievement_list_t">
<DisplayString>{{count={num_buckets}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>num_buckets</Size>
<ValueNode>buckets[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="__rc_client_leaderboard_state_enum_t">
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_STATE_INACTIVE">{RC_CLIENT_LEADERBOARD_STATE_INACTIVE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_STATE_ACTIVE">{RC_CLIENT_LEADERBOARD_STATE_ACTIVE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_STATE_TRACKING">{RC_CLIENT_LEADERBOARD_STATE_TRACKING}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_STATE_DISABLED">{RC_CLIENT_LEADERBOARD_STATE_DISABLED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_leaderboard_format_enum_t">
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_FORMAT_TIME">{RC_CLIENT_LEADERBOARD_FORMAT_TIME}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_FORMAT_SCORE">{RC_CLIENT_LEADERBOARD_FORMAT_SCORE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LEADERBOARD_FORMAT_VALUE">{RC_CLIENT_LEADERBOARD_FORMAT_VALUE}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_leaderboard_t">
<DisplayString>{{title={title,s} id={id}}}</DisplayString>
<Expand>
<Item Name="title">title</Item>
<Item Name="description">description</Item>
<Item Name="tracker_value">tracker_value</Item>
<Item Name="id">id</Item>
<Item Name="state">*((__rc_client_leaderboard_state_enum_t*)&amp;state)</Item>
<Item Name="format">*((__rc_client_leaderboard_format_enum_t*)&amp;format)</Item>
<Item Name="lower_is_better">*((__rc_bool_enum_t*)&amp;lower_is_better)</Item>
</Expand>
</Type>
<Type Name="rc_client_leaderboard_bucket_t">
<DisplayString>{{label={label,s} count={num_leaderboards}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>num_leaderboards</Size>
<ValueNode>leaderboards[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="rc_client_leaderboard_list_t">
<DisplayString>{{count={num_buckets}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>num_buckets</Size>
<ValueNode>buckets[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="rc_client_leaderboard_scoreboard_entry_t">
<DisplayString>{{rank={rank} score={score,s} username={username}}}</DisplayString>
</Type>
<Type Name="rc_client_leaderboard_scoreboard_t">
<DisplayString>{{leaderboard_id={leaderboard_id} num_entries={num_entries}}}</DisplayString>
<Expand>
<Item Name="leaderboard_id">leaderboard_id</Item>
<Item Name="submitted_score">submitted_score</Item>
<Item Name="best_score">best_score</Item>
<Item Name="new_rank">new_rank</Item>
<Item Name="num_entries">num_entries</Item>
<IndexListItems>
<Size>num_top_entries</Size>
<ValueNode>top_entries[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="__rc_client_event_type_enum_t">
<DisplayString Condition="value==RC_CLIENT_EVENT_TYPE_NONE">{RC_CLIENT_EVENT_TYPE_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED">{RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_STARTED">{RC_CLIENT_EVENT_LEADERBOARD_STARTED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_FAILED">{RC_CLIENT_EVENT_LEADERBOARD_FAILED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED">{RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW">{RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE">{RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW">{RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE">{RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE">{RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW">{RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE">{RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE">{RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD">{RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_RESET">{RC_CLIENT_EVENT_RESET}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_GAME_COMPLETED">{RC_CLIENT_EVENT_GAME_COMPLETED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_SERVER_ERROR">{RC_CLIENT_EVENT_SERVER_ERROR}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_DISCONNECTED">{RC_CLIENT_EVENT_DISCONNECTED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_EVENT_RECONNECTED">{RC_CLIENT_EVENT_RECONNECTED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_event_t">
<DisplayString>{{type={*((__rc_client_event_type_enum_t*)&amp;type)}}}</DisplayString>
<Expand>
<Item Name="type">*((__rc_client_event_type_enum_t*)&amp;type)</Item>
<Item Condition="type==RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED" Name="achievement">*achievement</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_STARTED" Name="leaderboard">*leaderboard</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_FAILED" Name="leaderboard">*leaderboard</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED" Name="leaderboard">*leaderboard</Item>
<Item Condition="type==RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW" Name="achievement">*achievement</Item>
<Item Condition="type==RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE" Name="achievement">*achievement</Item>
<Item Condition="type==RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW" Name="achievement">*achievement</Item>
<Item Condition="type==RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE" Name="achievement">*achievement</Item>
<Item Condition="type==RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE" Name="achievement">*achievement</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW" Name="leaderboard_tracker">*leaderboard_tracker</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE" Name="leaderboard_tracker">*leaderboard_tracker</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE" Name="leaderboard_tracker">*leaderboard_tracker</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD" Name="leaderboard_scoreboard">*leaderboard_scoreboard</Item>
<Item Condition="type==RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD" Name="leaderboard">*leaderboard</Item>
<Item Condition="type==RC_CLIENT_EVENT_SERVER_ERROR" Name="server_error">*server_error</Item>
</Expand>
</Type>
<Type Name="__rc_client_subset_info_achievements_list_t">
<DisplayString>{{count={info.public_.num_achievements}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>info.public_.num_achievements</Size>
<ValueNode>info.achievements[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="__rc_client_subset_info_leaderboards_list_t">
<DisplayString>{{count={info.public_.num_leaderboards}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>info.public_.num_leaderboards</Size>
<ValueNode>info.leaderboards[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="__rc_client_mastery_state_enum_t">
<DisplayString Condition="value==RC_CLIENT_MASTERY_STATE_NONE">{RC_CLIENT_MASTERY_STATE_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_MASTERY_STATE_PENDING">{RC_CLIENT_MASTERY_STATE_PENDING}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_MASTERY_STATE_SHOWN">{RC_CLIENT_MASTERY_STATE_SHOWN}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_subset_info_t">
<DisplayString>{{title={public_.title,s} id={public_.id}}}</DisplayString>
<Expand>
<Item Name="public_">public_</Item>
<Item Name="active">*((__rc_bool_enum_t*)&amp;active)</Item>
<Item Name="mastery">*((__rc_client_mastery_state_enum_t*)&amp;mastery)</Item>
<Item Name="achievements">*((__rc_client_subset_info_achievements_list_t*)this)</Item>
<Item Name="leaderboards">*((__rc_client_subset_info_leaderboards_list_t*)this)</Item>
</Expand>
</Type>
<Type Name="__rc_client_leaderboard_tracker_list_t">
<DisplayString Condition="first==0">{{NULL}}</DisplayString>
<DisplayString>{(void**)&amp;first,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="__rc_client_subset_info_list_t">
<DisplayString Condition="first==0">{{NULL}}</DisplayString>
<DisplayString>{(void**)&amp;first,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="__rc_client_media_hash_list_t">
<DisplayString Condition="first==0">{{NULL}}</DisplayString>
<DisplayString>{(void**)&amp;first,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="rc_client_achievement_info_t">
<DisplayString>{{title={public_.title,s} id={public_.id}}}</DisplayString>
</Type>
<Type Name="rc_client_leaderboard_info_t">
<DisplayString>{{title={public_.title,s} id={public_.id}}}</DisplayString>
</Type>
<Type Name="rc_client_game_info_t">
<DisplayString>{{title={public_.title,s} id={public_.id}}}</DisplayString>
<Expand>
<Item Name="public_">public_</Item>
<Item Name="subsets">*((__rc_client_subset_info_list_t*)&amp;subsets)</Item>
<Item Name="media_hash">*((__rc_client_media_hash_list_t*)&amp;media_hash)</Item>
<Item Name="leaderboard_trackers">*((__rc_client_leaderboard_tracker_list_t*)&amp;leaderboard_trackers)</Item>
<Item Name="progress_tracker">progress_tracker</Item>
<Item Name="runtime">runtime</Item>
</Expand>
</Type>
<Type Name="rc_client_game_hash_t">
<DisplayString>{{hash={hash,s} game_id={game_id}}}</DisplayString>
</Type>
<Type Name="__rc_client_game_hash_list_t">
<DisplayString>{client.hashes}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>client.hashes</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="__rc_client_load_game_state_enum_t">
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_NONE">{RC_CLIENT_LOAD_GAME_STATE_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_IDENTIFYING_GAME">{RC_CLIENT_LOAD_GAME_STATE_IDENTIFYING_GAME}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_AWAIT_LOGIN">{RC_CLIENT_LOAD_GAME_STATE_AWAIT_LOGIN}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_FETCHING_GAME_DATA">{RC_CLIENT_LOAD_GAME_STATE_FETCHING_GAME_DATA}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_STARTING_SESSION">{RC_CLIENT_LOAD_GAME_STATE_STARTING_SESSION}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_DONE">{RC_CLIENT_LOAD_GAME_STATE_DONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOAD_GAME_STATE_ABORTED">{RC_CLIENT_LOAD_GAME_STATE_ABORTED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_load_state_t">
<Expand>
<Item Name="progress">*((__rc_client_load_game_state_enum_t*)&amp;progress)</Item>
<Item Name="game">*game</Item>
<Item Name="subset">subset</Item>
<Item Name="hash">*hash</Item>
<Item Name="pending_media">pending_media</Item>
<Item Name="start_session_response">start_session_response</Item>
<Item Name="outstanding_requests">(int)outstanding_requests</Item>
</Expand>
</Type>
<Type Name="rc_client_scheduled_callback_data_t">
<DisplayString>{{when={when} callback={callback,na}}}</DisplayString>
</Type>
<Type Name="__rc_client_scheduled_callback_list_t">
<DisplayString>{state.scheduled_callbacks}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>state.scheduled_callbacks</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="__rc_client_log_level_enum_t">
<DisplayString Condition="value==RC_CLIENT_LOG_LEVEL_NONE">{RC_CLIENT_LOG_LEVEL_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOG_LEVEL_ERROR">{RC_CLIENT_LOG_LEVEL_ERROR}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOG_LEVEL_WARN">{RC_CLIENT_LOG_LEVEL_WARN}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOG_LEVEL_INFO">{RC_CLIENT_LOG_LEVEL_INFO}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_LOG_LEVEL_VERBOSE">{RC_CLIENT_LOG_LEVEL_VERBOSE}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_user_state_enum_t">
<DisplayString Condition="value==RC_CLIENT_USER_STATE_NONE">{RC_CLIENT_USER_STATE_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_USER_STATE_LOGIN_REQUESTED">{RC_CLIENT_USER_STATE_LOGIN_REQUESTED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_USER_STATE_LOGGED_IN">{RC_CLIENT_USER_STATE_LOGGED_IN}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_spectator_mode_enum_t">
<DisplayString Condition="value==RC_CLIENT_SPECTATOR_MODE_OFF">{RC_CLIENT_SPECTATOR_MODE_OFF}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_SPECTATOR_MODE_ON">{RC_CLIENT_SPECTATOR_MODE_ON}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_SPECTATOR_MODE_LOCKED">{RC_CLIENT_SPECTATOR_MODE_LOCKED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_client_disconnect_enum_t">
<DisplayString Condition="value==RC_CLIENT_DISCONNECT_HIDDEN">{RC_CLIENT_DISCONNECT_HIDDEN}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_DISCONNECT_VISIBLE">{RC_CLIENT_DISCONNECT_VISIBLE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_DISCONNECT_SHOW_PENDING">{RC_CLIENT_DISCONNECT_SHOW_PENDING}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_DISCONNECT_HIDE_PENDING">{RC_CLIENT_DISCONNECT_HIDE_PENDING}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_DISCONNECT_VISIBLE|RC_CLIENT_DISCONNECT_HIDE_PENDING">{RC_CLIENT_DISCONNECT_VISIBLE|RC_CLIENT_DISCONNECT_HIDE_PENDING}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_state_t">
<Expand>
<Item Name="hardcore">*((__rc_bool_enum_t*)&amp;hardcore)</Item>
<Item Name="unofficial_enabled">*((__rc_bool_enum_t*)&amp;unofficial_enabled)</Item>
<Item Name="encore_mode">*((__rc_bool_enum_t*)&amp;encore_mode)</Item>
<Item Name="spectator_mode">*((__rc_client_spectator_mode_enum_t*)&amp;spectator_mode)</Item>
<Item Name="disconnect">*((__rc_client_disconnect_enum_t*)&amp;disconnect)</Item>
<Item Name="log_level">*((__rc_client_log_level_enum_t*)&amp;log_level)</Item>
<Item Name="user">*((__rc_client_user_state_enum_t*)&amp;user)</Item>
<Item Name="scheduled_callbacks">*((__rc_client_scheduled_callback_list_t*)this)</Item>
<Item Name="load">load</Item>
</Expand>
</Type>
<Type Name="rc_client_t">
<Expand>
<Item Name="game">game</Item>
<Item Name="hashes">*((__rc_client_game_hash_list_t*)&amp;hashes)</Item>
<Item Name="user">user</Item>
<Item Name="callbacks">callbacks</Item>
<Item Name="state">state</Item>
</Expand>
</Type>
<Type Name="rc_client_raintegration_menu_t">
<DisplayString>{{count={num_items}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>num_items</Size>
<ValueNode>items[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="__rc_client_raintegration_event_enum_t">
<DisplayString Condition="value==RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE">{RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED">{RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED">{RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_RAINTEGRATION_EVENT_PAUSE">{RC_CLIENT_RAINTEGRATION_EVENT_PAUSE}</DisplayString>
<DisplayString Condition="value==RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED">{RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_client_raintegration_event_t">
<DisplayString>{{type={*((__rc_client_raintegration_event_enum_t*)&amp;type)}}}</DisplayString>
<Expand>
<Item Name="type">*((__rc_client_raintegration_event_enum_t*)&amp;type)</Item>
<Item Condition="type==RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED" Name="menu_item">menu_item</Item>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -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 <assert.h>
#include <ctype.h>
#include <stdarg.h>
@ -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 <windows.h>
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)

View File

@ -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 <windows.h>
#endif
#include "rc_export.h"
#include <stdio.h>
@ -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 <ogcsys.h>
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 <pthread.h>
typedef pthread_mutex_t rc_mutex_t;

View File

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

View File

@ -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;

View File

@ -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)

View File

@ -1,6 +1,7 @@
#include "rc_internal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,539 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<!-- https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022 -->
<!-- https://learn.microsoft.com/en-us/visualstudio/debugger/format-specifiers-in-cpp?view=vs-2022 -->
<Type Name="rc_typed_value_t">
<DisplayString Condition="type==RC_VALUE_TYPE_UNSIGNED">{value.u32} (RC_VALUE_TYPE_UNSIGNED)</DisplayString>
<DisplayString Condition="type==RC_VALUE_TYPE_FLOAT">{value.f32} (RC_VALUE_TYPE_FLOAT)</DisplayString>
<DisplayString Condition="type==RC_VALUE_TYPE_SIGNED">{value.i32} (RC_VALUE_TYPE_SIGNED)</DisplayString>
<DisplayString Condition="type==RC_VALUE_TYPE_NONE">none (RC_VALUE_TYPE_NONE)</DisplayString>
<DisplayString>{value.i32} (unknown)</DisplayString>
</Type>
<Type Name="__rc_bool_enum_t">
<DisplayString Condition="value==0">false</DisplayString>
<DisplayString Condition="value==1">true</DisplayString>
<DisplayString>true ({value})</DisplayString>
</Type>
<Type Name="__rc_memsize_enum_t">
<DisplayString Condition="value==RC_MEMSIZE_8_BITS">{RC_MEMSIZE_8_BITS}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_16_BITS">{RC_MEMSIZE_16_BITS}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_24_BITS">{RC_MEMSIZE_24_BITS}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_32_BITS">{RC_MEMSIZE_32_BITS}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_LOW">{RC_MEMSIZE_LOW}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_HIGH">{RC_MEMSIZE_HIGH}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_0">{RC_MEMSIZE_BIT_0}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_1">{RC_MEMSIZE_BIT_1}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_2">{RC_MEMSIZE_BIT_2}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_3">{RC_MEMSIZE_BIT_3}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_4">{RC_MEMSIZE_BIT_4}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_5">{RC_MEMSIZE_BIT_5}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_6">{RC_MEMSIZE_BIT_6}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_7">{RC_MEMSIZE_BIT_7}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BITCOUNT">{RC_MEMSIZE_BITCOUNT}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_16_BITS_BE">{RC_MEMSIZE_16_BITS_BE}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_24_BITS_BE">{RC_MEMSIZE_24_BITS_BE}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_32_BITS_BE">{RC_MEMSIZE_32_BITS_BE}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_FLOAT">{RC_MEMSIZE_FLOAT}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_MBF32">{RC_MEMSIZE_MBF32}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_MBF32_LE">{RC_MEMSIZE_MBF32_LE}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_FLOAT_BE">{RC_MEMSIZE_FLOAT_BE}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_DOUBLE32">{RC_MEMSIZE_DOUBLE32}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_DOUBLE32_BE">{RC_MEMSIZE_DOUBLE32_BE}</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_VARIABLE">{RC_MEMSIZE_VARIABLE}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_value_type_enum_t">
<DisplayString Condition="value==RC_VALUE_TYPE_NONE">{RC_VALUE_TYPE_NONE}</DisplayString>
<DisplayString Condition="value==RC_VALUE_TYPE_UNSIGNED">{RC_VALUE_TYPE_UNSIGNED}</DisplayString>
<DisplayString Condition="value==RC_VALUE_TYPE_SIGNED">{RC_VALUE_TYPE_SIGNED}</DisplayString>
<DisplayString Condition="value==RC_VALUE_TYPE_FLOAT">{RC_VALUE_TYPE_FLOAT}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_memref_type_enum_t">
<DisplayString Condition="value==RC_MEMREF_TYPE_MEMREF">{RC_MEMREF_TYPE_MEMREF}</DisplayString>
<DisplayString Condition="value==RC_MEMREF_TYPE_MODIFIED_MEMREF">{RC_MEMREF_TYPE_MODIFIED_MEMREF}</DisplayString>
<DisplayString Condition="value==RC_MEMREF_TYPE_VALUE">RC_MEMREF_TYPE_VALUE</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_memsize_enum_func_t">
<DisplayString Condition="value==RC_MEMSIZE_8_BITS">byte</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_16_BITS">word</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_24_BITS">tbyte</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_32_BITS">dword</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_LOW">lower4</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_HIGH">upper4</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_0">bit0</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_1">bit1</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_2">bit2</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_3">bit3</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_4">bit4</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_5">bit5</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_6">bit6</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BIT_7">bit7</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_BITCOUNT">bitcount</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_16_BITS_BE">word_be</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_24_BITS_BE">tbyte_be</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_32_BITS_BE">dword_be</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_FLOAT">float</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_MBF32">mbf32</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_MBF32_LE">mbf32_le</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_FLOAT_BE">float_be</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_DOUBLE32">double32</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_DOUBLE32_BE">double32_be</DisplayString>
<DisplayString Condition="value==RC_MEMSIZE_VARIABLE">var</DisplayString>
<DisplayString>unknown</DisplayString>
</Type>
<Type Name="rc_memref_value_t">
<DisplayString Condition="type==RC_VALUE_TYPE_FLOAT">{{value={(float)*((float*)&amp;value)} prior={(float)*((float*)&amp;prior)}}}</DisplayString>
<DisplayString Condition="type==RC_VALUE_TYPE_SIGNED">{{value={(int)value} prior={(int)prior}}}</DisplayString>
<DisplayString>{{value={value,x} prior={prior,x}}}</DisplayString>
<Expand>
<Item Name="value">value</Item>
<Item Name="prior">prior</Item>
<Item Name="size">*((__rc_memsize_enum_t*)&amp;size)</Item>
<Item Name="changed">*((__rc_bool_enum_t*)&amp;changed)</Item>
<Item Name="type">*((__rc_value_type_enum_t*)&amp;type)</Item>
<Item Name="memref_type">*((__rc_memref_type_enum_t*)&amp;memref_type)</Item>
</Expand>
</Type>
<Type Name="rc_memref_t">
<DisplayString Condition="value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">{*(rc_modified_memref_t*)&amp;value}</DisplayString>
<DisplayString Condition="value.memref_type==RC_MEMREF_TYPE_VALUE">{*(rc_value_t*)&amp;value}</DisplayString>
<DisplayString Condition="value.size==RC_MEMSIZE_VARIABLE">var</DisplayString>
<DisplayString>{*((__rc_memsize_enum_func_t*)&amp;value.size)}({address,x})</DisplayString>
<Expand>
<Item Name="[rc_modified_memref_t]" Condition="value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">(rc_modified_memref_t*)&amp;value</Item>
<Item Name="value">value</Item>
<Item Name="address">address</Item>
</Expand>
</Type>
<Type Name="rc_memref_list_t">
<DisplayString>{{count = {count}}}</DisplayString>
<Expand>
<Item Name="next" Condition="next!=0">next</Item>
<Item Name="count">count</Item>
<Item Name="capacity">capacity</Item>
<ArrayItems>
<Size>count</Size>
<ValuePointer>items</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="__rc_operand_memref_t">
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">... {*((__rc_memsize_enum_func_t*)&amp;operand.size)} {operand.value.memref->address,x}</DisplayString>
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_VALUE">value {((rc_value_t*)operand.value.memref)->name,s}</DisplayString>
<DisplayString>{*((__rc_memsize_enum_func_t*)&amp;operand.size)} {operand.value.memref->address,x}</DisplayString>
<Expand>
<Item Name="[rc_modified_memref_t]" Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">(rc_modified_memref_t*)&amp;operand.value</Item>
<Item Name="value">operand.value.memref->value</Item>
<Item Name="address">operand.value.memref->address</Item>
</Expand>
</Type>
<Type Name="__rc_operand_enum_t">
<DisplayString Condition="value==RC_OPERAND_ADDRESS">{RC_OPERAND_ADDRESS}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_DELTA">{RC_OPERAND_DELTA}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_CONST">{RC_OPERAND_CONST}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_FP">{RC_OPERAND_FP}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_LUA">{RC_OPERAND_LUA}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_PRIOR">{RC_OPERAND_PRIOR}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_BCD">{RC_OPERAND_BCD}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_INVERTED">{RC_OPERAND_INVERTED}</DisplayString>
<DisplayString Condition="value==RC_OPERAND_RECALL">{RC_OPERAND_RECALL}</DisplayString>
<DisplayString Condition="value==0xFF">RC_OPERAND_NONE (255)</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_operand_t">
<DisplayString Condition="type==RC_OPERAND_ADDRESS">{{{*(__rc_operand_memref_t*)&amp;value.memref}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_DELTA">{{delta {*(__rc_operand_memref_t*)&amp;value.memref}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_PRIOR">{{prior {*(__rc_operand_memref_t*)&amp;value.memref}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_BCD">{{bcd {*(__rc_operand_memref_t*)&amp;value.memref}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_INVERTED">{{inverted {*(__rc_operand_memref_t*)&amp;value.memref}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_CONST &amp;&amp; value.num &gt; 0xFFFF0000">{{value {(int)value.num}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_CONST &amp;&amp; value.num &gt; 0x01000000">{{value {value.num,x}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_CONST">{{value {value.num}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_FP">{{value {value.dbl}}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_RECALL">{{recall}}</DisplayString>
<DisplayString Condition="type==RC_OPERAND_LUA">{{lua @{value.luafunc}}}</DisplayString>
<DisplayString Condition="type==0xFF">{{none}}</DisplayString>
<DisplayString>{{unknown}}</DisplayString>
<Expand>
<Item Name="value" Condition="type==RC_OPERAND_CONST">value.num</Item>
<Item Name="value" Condition="type==RC_OPERAND_FP">value.dbl</Item>
<Item Name="value" Condition="type==RC_OPERAND_RECALL &amp;&amp; memref_access_type==RC_OPERAND_CONST">value.num</Item>
<Item Name="value" Condition="type==RC_OPERAND_RECALL &amp;&amp; memref_access_type==RC_OPERAND_FP">value.dbl</Item>
<Item Name="value" Condition="type!=RC_OPERAND_CONST &amp;&amp; type!=RC_OPERAND_FP &amp;&amp; value.memref==0">value.memref</Item>
<Item Name="value" Condition="type!=RC_OPERAND_CONST &amp;&amp; type!=RC_OPERAND_FP &amp;&amp; ((rc_memref_t*)value.memref)->value.memref_type!=RC_MEMREF_TYPE_MODIFIED_MEMREF">value.memref</Item>
<Item Name="value" Condition="type!=RC_OPERAND_CONST &amp;&amp; type!=RC_OPERAND_FP &amp;&amp; ((rc_memref_t*)value.memref)->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">(rc_modified_memref_t*)value.memref</Item>
<Item Name="type">*((__rc_operand_enum_t*)&amp;type)</Item>
<Item Name="size">*((__rc_memsize_enum_t*)&amp;size)</Item>
<Item Name="memref_access_type">*((__rc_operand_enum_t*)&amp;memref_access_type)</Item>
</Expand>
</Type>
<Type Name="__rc_condition_enum_t">
<DisplayString Condition="value==RC_CONDITION_STANDARD">{RC_CONDITION_STANDARD}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_PAUSE_IF">{RC_CONDITION_PAUSE_IF}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_RESET_IF">{RC_CONDITION_RESET_IF}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_MEASURED_IF">{RC_CONDITION_MEASURED_IF}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_TRIGGER">{RC_CONDITION_TRIGGER}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_MEASURED">{RC_CONDITION_MEASURED}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_ADD_SOURCE">{RC_CONDITION_ADD_SOURCE}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_SUB_SOURCE">{RC_CONDITION_SUB_SOURCE}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_ADD_ADDRESS">{RC_CONDITION_ADD_ADDRESS}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_REMEMBER">{RC_CONDITION_REMEMBER}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_ADD_HITS">{RC_CONDITION_ADD_HITS}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_SUB_HITS">{RC_CONDITION_SUB_HITS}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_RESET_NEXT_IF">{RC_CONDITION_RESET_NEXT_IF}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_AND_NEXT">{RC_CONDITION_AND_NEXT}</DisplayString>
<DisplayString Condition="value==RC_CONDITION_OR_NEXT">{RC_CONDITION_OR_NEXT}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_condition_enum_str_t">
<DisplayString Condition="value==RC_CONDITION_STANDARD"></DisplayString>
<DisplayString Condition="value==RC_CONDITION_PAUSE_IF">PauseIf </DisplayString>
<DisplayString Condition="value==RC_CONDITION_RESET_IF">ResetIf </DisplayString>
<DisplayString Condition="value==RC_CONDITION_MEASURED_IF">MeasuredIf </DisplayString>
<DisplayString Condition="value==RC_CONDITION_TRIGGER">Trigger </DisplayString>
<DisplayString Condition="value==RC_CONDITION_MEASURED">Measured </DisplayString>
<DisplayString Condition="value==RC_CONDITION_ADD_SOURCE">AddSource </DisplayString>
<DisplayString Condition="value==RC_CONDITION_SUB_SOURCE">SubSource </DisplayString>
<DisplayString Condition="value==RC_CONDITION_ADD_ADDRESS">AddAddress </DisplayString>
<DisplayString Condition="value==RC_CONDITION_REMEMBER">Remember </DisplayString>
<DisplayString Condition="value==RC_CONDITION_ADD_HITS">AddHits </DisplayString>
<DisplayString Condition="value==RC_CONDITION_SUB_HITS">SubHits </DisplayString>
<DisplayString Condition="value==RC_CONDITION_RESET_NEXT_IF">ResetNextIf </DisplayString>
<DisplayString Condition="value==RC_CONDITION_AND_NEXT">AndNext </DisplayString>
<DisplayString Condition="value==RC_CONDITION_OR_NEXT">OrNext </DisplayString>
<DisplayString>{value} </DisplayString>
</Type>
<Type Name="__rc_operator_enum_t">
<DisplayString Condition="value==RC_OPERATOR_EQ">{RC_OPERATOR_EQ}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_LT">{RC_OPERATOR_LT}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_LE">{RC_OPERATOR_LE}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_GT">{RC_OPERATOR_GT}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_GE">{RC_OPERATOR_GE}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_NE">{RC_OPERATOR_NE}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_NONE">{RC_OPERATOR_NONE}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_MULT">{RC_OPERATOR_MULT}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_DIV">{RC_OPERATOR_DIV}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_AND">{RC_OPERATOR_AND}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_XOR">{RC_OPERATOR_XOR}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_MOD">{RC_OPERATOR_MOD}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_ADD">{RC_OPERATOR_ADD}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_SUB">{RC_OPERATOR_SUB}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_SUB_PARENT">{RC_OPERATOR_SUB_PARENT}</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_INDIRECT_READ">{RC_OPERATOR_INDIRECT_READ}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="__rc_operator_enum_str_t">
<DisplayString Condition="value==RC_OPERATOR_EQ">==</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_LT">&lt;</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_LE">&lt;=</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_GT">&gt;</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_GE">&gt;=</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_NE">!=</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_NONE"> </DisplayString>
<DisplayString Condition="value==RC_OPERATOR_MULT">*</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_DIV">/</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_AND">&amp;</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_XOR">^</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_MOD">%</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_ADD">+</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_SUB">-</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_SUB_PARENT">subtracted from</DisplayString>
<DisplayString Condition="value==RC_OPERATOR_INDIRECT_READ">$</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_condition_t">
<DisplayString Condition="oper==RC_OPERATOR_NONE &amp;&amp; required_hits==0">{*((__rc_condition_enum_str_t*)&amp;type)} {operand1}</DisplayString>
<DisplayString Condition="oper==RC_OPERATOR_NONE">{*((__rc_condition_enum_str_t*)&amp;type)} {operand1} ({required_hits})</DisplayString>
<DisplayString Condition="required_hits==0">{*((__rc_condition_enum_str_t*)&amp;type)} {operand1} {*((__rc_operator_enum_str_t*)&amp;oper)} {operand2}</DisplayString>
<DisplayString>{*((__rc_condition_enum_str_t*)&amp;type)} {operand1} {*((__rc_operator_enum_str_t*)&amp;oper)} {operand2} ({required_hits})</DisplayString>
<Expand>
<Item Name="type">*((__rc_condition_enum_t*)&amp;type)</Item>
<Item Name="operand1">operand1</Item>
<Item Name="oper">*((__rc_operator_enum_t*)&amp;oper)</Item>
<Item Name="operand2">operand2</Item>
<Item Name="required_hits">required_hits</Item>
<Item Name="current_hits">current_hits</Item>
<Item Name="next">next</Item>
</Expand>
</Type>
<Type Name="rc_condset_t">
<DisplayString>{{count={num_pause_conditions+num_reset_conditions+num_hittarget_conditions+num_measured_conditions+num_other_conditions+num_indirect_conditions}}}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>conditions</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="__rc_condset_list_t">
<DisplayString Condition="first_condset==0">{{}}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first_condset</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="rc_modified_memref_t">
<DisplayString Condition="modifier_type==RC_OPERATOR_INDIRECT_READ">$({parent} + {modifier})</DisplayString>
<DisplayString Condition="modifier_type==RC_OPERATOR_SUB_PARENT">({modifier} - {parent})</DisplayString>
<DisplayString>({parent} {*((__rc_operator_enum_str_t*)&amp;modifier_type)} {modifier})</DisplayString>
<Expand>
<Item Name="value">memref.value</Item>
<Item Name="parent">parent</Item>
<Item Name="modifier_type">*((__rc_operator_enum_t*)&amp;modifier_type)</Item>
<Item Name="modifier">modifier</Item>
</Expand>
</Type>
<Type Name="rc_modified_memref_list_t">
<DisplayString>{{count = {count}}}</DisplayString>
<Expand>
<Item Name="next" Condition="next!=0">next</Item>
<Item Name="count">count</Item>
<Item Name="capacity">capacity</Item>
<ArrayItems>
<Size>count</Size>
<ValuePointer>items</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="__rc_trigger_state_enum_t">
<DisplayString Condition="value==RC_TRIGGER_STATE_INACTIVE">{RC_TRIGGER_STATE_INACTIVE}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_WAITING">{RC_TRIGGER_STATE_WAITING}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_ACTIVE">{RC_TRIGGER_STATE_ACTIVE}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_PAUSED">{RC_TRIGGER_STATE_PAUSED}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_RESET">{RC_TRIGGER_STATE_RESET}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_TRIGGERED">{RC_TRIGGER_STATE_TRIGGERED}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_PRIMED">{RC_TRIGGER_STATE_PRIMED}</DisplayString>
<DisplayString Condition="value==RC_TRIGGER_STATE_DISABLED">{RC_TRIGGER_STATE_DISABLED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_trigger_t">
<Expand>
<Item Name="state">*((__rc_trigger_state_enum_t*)&amp;state)</Item>
<Item Name="has_hits">*((__rc_bool_enum_t*)&amp;has_hits)</Item>
<Item Name="measured_as_percent">*((__rc_bool_enum_t*)&amp;measured_as_percent)</Item>
<Item Name="requirement">requirement</Item>
<Item Name="alternative">*((__rc_condset_list_t*)&amp;alternative)</Item>
</Expand>
</Type>
<Type Name="rc_value_t">
<DisplayString>{value} {name,s}</DisplayString>
<Expand>
<Item Name="value">value</Item>
<Item Name="conditions">conditions</Item>
<Item Name="name">name</Item>
</Expand>
</Type>
<Type Name="rc_value_list_t">
<DisplayString>{{count = {count}}}</DisplayString>
<Expand>
<Item Name="count">count</Item>
<Item Name="capacity">capacity</Item>
<ArrayItems>
<Size>count</Size>
<ValuePointer>items</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="__rc_lboard_state_enum_t">
<DisplayString Condition="value==RC_LBOARD_STATE_INACTIVE">{RC_LBOARD_STATE_INACTIVE}</DisplayString>
<DisplayString Condition="value==RC_LBOARD_STATE_WAITING">{RC_LBOARD_STATE_WAITING}</DisplayString>
<DisplayString Condition="value==RC_LBOARD_STATE_ACTIVE">{RC_LBOARD_STATE_ACTIVE}</DisplayString>
<DisplayString Condition="value==RC_LBOARD_STATE_STARTED">{RC_LBOARD_STATE_STARTED}</DisplayString>
<DisplayString Condition="value==RC_LBOARD_STATE_CANCELED">{RC_LBOARD_STATE_CANCELED}</DisplayString>
<DisplayString Condition="value==RC_LBOARD_STATE_TRIGGERED">{RC_LBOARD_STATE_TRIGGERED}</DisplayString>
<DisplayString Condition="value==RC_LBOARD_STATE_DISABLED">{RC_LBOARD_STATE_DISABLED}</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_lboard_t">
<Expand>
<Item Name="state">*((__rc_lboard_state_enum_t*)&amp;state)</Item>
<Item Name="start">start</Item>
<Item Name="submit">submit</Item>
<Item Name="cancel">cancel</Item>
<Item Name="value">value</Item>
</Expand>
</Type>
<Type Name="__rc_format_enum_t">
<DisplayString Condition="value==RC_FORMAT_FRAMES">{RC_FORMAT_FRAMES}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_SECONDS">{RC_FORMAT_SECONDS}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_CENTISECS">{RC_FORMAT_CENTISECS}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_SCORE">{RC_FORMAT_SCORE}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_VALUE">{RC_FORMAT_VALUE}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_MINUTES">{RC_FORMAT_MINUTES}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_SECONDS_AS_MINUTES">{RC_FORMAT_SECONDS_AS_MINUTES}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FLOAT1">{RC_FORMAT_FLOAT1}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FLOAT2">{RC_FORMAT_FLOAT2}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FLOAT3">{RC_FORMAT_FLOAT3}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FLOAT4">{RC_FORMAT_FLOAT4}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FLOAT5">{RC_FORMAT_FLOAT5}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FLOAT6">{RC_FORMAT_FLOAT6}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FIXED1">{RC_FORMAT_FIXED1}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FIXED2">{RC_FORMAT_FIXED2}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_FIXED3">{RC_FORMAT_FIXED3}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_TENS">{RC_FORMAT_TENS}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_HUNDREDS">{RC_FORMAT_HUNDREDS}</DisplayString>
<DisplayString Condition="value==RC_FORMAT_THOUSANDS">{RC_FORMAT_THOUSANDS}</DisplayString>
<DisplayString Condition="value==101">RC_FORMAT_STRING (101)</DisplayString>
<DisplayString Condition="value==102">RC_FORMAT_LOOKUP (102)</DisplayString>
<DisplayString Condition="value==103">RC_FORMAT_UNKNOWN_MACRO (103)</DisplayString>
<DisplayString Condition="value==104">RC_FORMAT_ASCIICHAR (104)</DisplayString>
<DisplayString Condition="value==105">RC_FORMAT_UNICODECHAR (105)</DisplayString>
<DisplayString>unknown ({value})</DisplayString>
</Type>
<Type Name="rc_richpresence_display_part_t">
<DisplayString Condition="display_type==101">{text,s}</DisplayString>
<DisplayString Condition="display_type==103">[Unknown macro]{text,sb}</DisplayString>
<DisplayString Condition="lookup==0">@{text,sb}({value,na})</DisplayString>
<DisplayString>@{lookup->name,sb}({value,na})</DisplayString>
<Expand>
<Item Name="text" Condition="display_type==101||display_type==103">text</Item>
<Item Name="lookup" Condition="display_type!=101&amp;&amp;display_type!=103">lookup</Item>
<Item Name="value" Condition="display_type!=101&amp;&amp;display_type!=103">value</Item>
<Item Name="display_type">*((__rc_format_enum_t*)&amp;display_type)</Item>
</Expand>
</Type>
<Type Name="__rc_richpresence_display_part_list_t">
<DisplayString Condition="display->next->next!=0">{display,na} {display->next,na} {display->next->next,na}</DisplayString>
<DisplayString Condition="display->next!=0">{display,na} {display->next,na}</DisplayString>
<DisplayString>{display,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>display</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="rc_richpresence_display_t">
<Expand>
<Item Name="displays">*((__rc_richpresence_display_part_list_t*)&amp;display)</Item>
<Item Name="trigger">trigger</Item>
</Expand>
</Type>
<Type Name="__rc_richpresence_display_list_t">
<DisplayString Condition="first_display==0">{{NULL}}</DisplayString>
<DisplayString>{(void*)&amp;first_display,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first_display</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="rc_richpresence_lookup_item_t">
<DisplayString Condition="first==last">{first}: {label,na}</DisplayString>
<DisplayString>{first}-{last}: {label,na}</DisplayString>
</Type>
<Type Name="rc_richpresence_lookup_t">
<DisplayString>{name,na}</DisplayString>
<Expand>
<Item Name="name">name</Item>
<Item Name="format">*((__rc_format_enum_t*)&amp;format)</Item>
<Item Name="default_label" Condition="format>101">default_label</Item>
<TreeItems>
<HeadPointer>root</HeadPointer>
<LeftPointer>left</LeftPointer>
<RightPointer>right</RightPointer>
<ValueNode>this</ValueNode>
</TreeItems>
</Expand>
</Type>
<Type Name="__rc_richpresence_lookup_list_t">
<DisplayString Condition="first_lookup==0">{{NULL}}</DisplayString>
<DisplayString>{(void*)&amp;first_lookup,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first_lookup</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="rc_richpresence_t">
<Expand>
<Item Name="displays">((__rc_richpresence_display_list_t*)&amp;first_display)</Item>
<Item Name="lookups">((__rc_richpresence_lookup_list_t*)&amp;first_lookup)</Item>
</Expand>
</Type>
<Type Name="__rc_value_list_t">
<DisplayString Condition="first_value==0">{{NULL}}</DisplayString>
<DisplayString>{(void*)&amp;first_value,na}</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>first_value</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="rc_parse_state_t">
<DisplayString>{{offset={offset} addsource_parent={addsource_parent} indirect_parent={indirect_parent}}}</DisplayString>
<Expand>
<Item Name="offset">offset</Item>
<Item Name="memrefs">memrefs</Item>
<Item Name="existing_memrefs">existing_memrefs</Item>
<Item Name="variables" Condition="variables==0">variables</Item>
<Item Name="variables" Condition="variables!=0">((__rc_value_list_t*)&amp;variables)</Item>
<Item Name="addsource_parent">addsource_parent</Item>
<Item Name="addsource_oper">*((__rc_operator_enum_t*)&amp;addsource_oper)</Item>
<Item Name="indirect_parent">indirect_parent</Item>
<Item Name="remember">remember</Item>
<Item Name="is_value">*((__rc_bool_enum_t*)&amp;is_value)</Item>
<Item Name="has_required_hits">*((__rc_bool_enum_t*)&amp;has_required_hits)</Item>
<Item Name="measured_as_percent">*((__rc_bool_enum_t*)&amp;measured_as_percent)</Item>
</Expand>
</Type>
<Type Name="rc_buffer_chunk_t">
<DisplayString>{{used={write-start} size={end-start}}}</DisplayString>
<Expand>
<Item Name="[size]">end-start</Item>
<Item Name="[used]">write-start</Item>
<Item Name="[available]">end-write</Item>
<Item Name="next">next</Item>
</Expand>
</Type>
<Type Name="rc_buffer_t">
<DisplayString></DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>&amp;chunk</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="__rc_runtime_trigger_list_t">
<DisplayString>{{count={runtime.trigger_count}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>runtime.trigger_count</Size>
<ValueNode>runtime.triggers[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="__rc_runtime_lboard_list_t">
<DisplayString>{{count={runtime.lboard_count}}}</DisplayString>
<Expand>
<IndexListItems>
<Size>runtime.lboard_count</Size>
<ValueNode>runtime.lboards[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="rc_runtime_t">
<DisplayString>{{trigger_count={trigger_count} lboard_count={lboard_count}}}</DisplayString>
<Expand>
<Item Name="triggers">*((__rc_runtime_trigger_list_t*)this)</Item>
<Item Name="lboards">*((__rc_runtime_lboard_list_t*)this)</Item>
<Item Name="richpresence">richpresence</Item>
<Item Name="memrefs">memrefs</Item>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -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;

View File

@ -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);

View File

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

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -5,8 +5,6 @@
#include <float.h> /* FLT_EPSILON */
#include <math.h> /* 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 */

View File

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