dep/rcheevos: Bump to 3a91a58
This commit is contained in:
parent
fa993849f7
commit
750dd1cb87
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*)&state)</Item>
|
||||
<Item Name="type">*((__rc_client_achievement_type_enum_t*)&state)</Item>
|
||||
<Item Name="category">*((__rc_client_achievement_category_enum_t*)&category)</Item>
|
||||
<Item Name="bucket">*((__rc_client_achievement_state_enum_t*)&bucket)</Item>
|
||||
<Item Name="unlocked">*((__rc_client_achievement_unlocked_enum_t*)&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*)&state)</Item>
|
||||
<Item Name="format">*((__rc_client_leaderboard_format_enum_t*)&format)</Item>
|
||||
<Item Name="lower_is_better">*((__rc_bool_enum_t*)&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*)&type)}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="type">*((__rc_client_event_type_enum_t*)&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*)&active)</Item>
|
||||
<Item Name="mastery">*((__rc_client_mastery_state_enum_t*)&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**)&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**)&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**)&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*)&subsets)</Item>
|
||||
<Item Name="media_hash">*((__rc_client_media_hash_list_t*)&media_hash)</Item>
|
||||
<Item Name="leaderboard_trackers">*((__rc_client_leaderboard_tracker_list_t*)&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*)&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*)&hardcore)</Item>
|
||||
<Item Name="unofficial_enabled">*((__rc_bool_enum_t*)&unofficial_enabled)</Item>
|
||||
<Item Name="encore_mode">*((__rc_bool_enum_t*)&encore_mode)</Item>
|
||||
<Item Name="spectator_mode">*((__rc_client_spectator_mode_enum_t*)&spectator_mode)</Item>
|
||||
<Item Name="disconnect">*((__rc_client_disconnect_enum_t*)&disconnect)</Item>
|
||||
<Item Name="log_level">*((__rc_client_log_level_enum_t*)&log_level)</Item>
|
||||
<Item Name="user">*((__rc_client_user_state_enum_t*)&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*)&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*)&type)}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="type">*((__rc_client_raintegration_event_enum_t*)&type)</Item>
|
||||
<Item Condition="type==RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED" Name="menu_item">menu_item</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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*)&value)} prior={(float)*((float*)&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*)&size)</Item>
|
||||
<Item Name="changed">*((__rc_bool_enum_t*)&changed)</Item>
|
||||
<Item Name="type">*((__rc_value_type_enum_t*)&type)</Item>
|
||||
<Item Name="memref_type">*((__rc_memref_type_enum_t*)&memref_type)</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="rc_memref_t">
|
||||
<DisplayString Condition="value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">{*(rc_modified_memref_t*)&value}</DisplayString>
|
||||
<DisplayString Condition="value.memref_type==RC_MEMREF_TYPE_VALUE">{*(rc_value_t*)&value}</DisplayString>
|
||||
<DisplayString Condition="value.size==RC_MEMSIZE_VARIABLE">var</DisplayString>
|
||||
<DisplayString>{*((__rc_memsize_enum_func_t*)&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*)&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*)&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*)&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*)&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*)&value.memref}}}</DisplayString>
|
||||
<DisplayString Condition="type==RC_OPERAND_DELTA">{{delta {*(__rc_operand_memref_t*)&value.memref}}}</DisplayString>
|
||||
<DisplayString Condition="type==RC_OPERAND_PRIOR">{{prior {*(__rc_operand_memref_t*)&value.memref}}}</DisplayString>
|
||||
<DisplayString Condition="type==RC_OPERAND_BCD">{{bcd {*(__rc_operand_memref_t*)&value.memref}}}</DisplayString>
|
||||
<DisplayString Condition="type==RC_OPERAND_INVERTED">{{inverted {*(__rc_operand_memref_t*)&value.memref}}}</DisplayString>
|
||||
<DisplayString Condition="type==RC_OPERAND_CONST && value.num > 0xFFFF0000">{{value {(int)value.num}}}</DisplayString>
|
||||
<DisplayString Condition="type==RC_OPERAND_CONST && value.num > 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 && memref_access_type==RC_OPERAND_CONST">value.num</Item>
|
||||
<Item Name="value" Condition="type==RC_OPERAND_RECALL && memref_access_type==RC_OPERAND_FP">value.dbl</Item>
|
||||
<Item Name="value" Condition="type!=RC_OPERAND_CONST && type!=RC_OPERAND_FP && value.memref==0">value.memref</Item>
|
||||
<Item Name="value" Condition="type!=RC_OPERAND_CONST && type!=RC_OPERAND_FP && ((rc_memref_t*)value.memref)->value.memref_type!=RC_MEMREF_TYPE_MODIFIED_MEMREF">value.memref</Item>
|
||||
<Item Name="value" Condition="type!=RC_OPERAND_CONST && type!=RC_OPERAND_FP && ((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*)&type)</Item>
|
||||
<Item Name="size">*((__rc_memsize_enum_t*)&size)</Item>
|
||||
<Item Name="memref_access_type">*((__rc_operand_enum_t*)&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"><</DisplayString>
|
||||
<DisplayString Condition="value==RC_OPERATOR_LE"><=</DisplayString>
|
||||
<DisplayString Condition="value==RC_OPERATOR_GT">></DisplayString>
|
||||
<DisplayString Condition="value==RC_OPERATOR_GE">>=</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">&</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 && required_hits==0">{*((__rc_condition_enum_str_t*)&type)} {operand1}</DisplayString>
|
||||
<DisplayString Condition="oper==RC_OPERATOR_NONE">{*((__rc_condition_enum_str_t*)&type)} {operand1} ({required_hits})</DisplayString>
|
||||
<DisplayString Condition="required_hits==0">{*((__rc_condition_enum_str_t*)&type)} {operand1} {*((__rc_operator_enum_str_t*)&oper)} {operand2}</DisplayString>
|
||||
<DisplayString>{*((__rc_condition_enum_str_t*)&type)} {operand1} {*((__rc_operator_enum_str_t*)&oper)} {operand2} ({required_hits})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="type">*((__rc_condition_enum_t*)&type)</Item>
|
||||
<Item Name="operand1">operand1</Item>
|
||||
<Item Name="oper">*((__rc_operator_enum_t*)&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*)&modifier_type)} {modifier})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="value">memref.value</Item>
|
||||
<Item Name="parent">parent</Item>
|
||||
<Item Name="modifier_type">*((__rc_operator_enum_t*)&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*)&state)</Item>
|
||||
<Item Name="has_hits">*((__rc_bool_enum_t*)&has_hits)</Item>
|
||||
<Item Name="measured_as_percent">*((__rc_bool_enum_t*)&measured_as_percent)</Item>
|
||||
<Item Name="requirement">requirement</Item>
|
||||
<Item Name="alternative">*((__rc_condset_list_t*)&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*)&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&&display_type!=103">lookup</Item>
|
||||
<Item Name="value" Condition="display_type!=101&&display_type!=103">value</Item>
|
||||
<Item Name="display_type">*((__rc_format_enum_t*)&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*)&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*)&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*)&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*)&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*)&first_display)</Item>
|
||||
<Item Name="lookups">((__rc_richpresence_lookup_list_t*)&first_lookup)</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="__rc_value_list_t">
|
||||
<DisplayString Condition="first_value==0">{{NULL}}</DisplayString>
|
||||
<DisplayString>{(void*)&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*)&variables)</Item>
|
||||
<Item Name="addsource_parent">addsource_parent</Item>
|
||||
<Item Name="addsource_oper">*((__rc_operator_enum_t*)&addsource_oper)</Item>
|
||||
<Item Name="indirect_parent">indirect_parent</Item>
|
||||
<Item Name="remember">remember</Item>
|
||||
<Item Name="is_value">*((__rc_bool_enum_t*)&is_value)</Item>
|
||||
<Item Name="has_required_hits">*((__rc_bool_enum_t*)&has_required_hits)</Item>
|
||||
<Item Name="measured_as_percent">*((__rc_bool_enum_t*)&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>&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>
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue