update to rcheevos 10.1 (#12512)
This commit is contained in:
parent
0caeed6e1d
commit
5d4069cf8f
|
@ -73,7 +73,7 @@
|
||||||
#include "../network/net_http_special.h"
|
#include "../network/net_http_special.h"
|
||||||
#include "../tasks/tasks_internal.h"
|
#include "../tasks/tasks_internal.h"
|
||||||
|
|
||||||
#include "../deps/rcheevos/include/rc_runtime_types.h"
|
#include "../deps/rcheevos/include/rc_runtime.h"
|
||||||
#include "../deps/rcheevos/include/rc_url.h"
|
#include "../deps/rcheevos/include/rc_url.h"
|
||||||
#include "../deps/rcheevos/include/rc_hash.h"
|
#include "../deps/rcheevos/include/rc_hash.h"
|
||||||
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
|
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
|
||||||
|
@ -171,6 +171,7 @@ static void rcheevos_async_task_callback(
|
||||||
retro_task_t* task, void* task_data, void* user_data, const char* error);
|
retro_task_t* task, void* task_data, void* user_data, const char* error);
|
||||||
static void rcheevos_async_submit_lboard(rcheevos_locals_t *locals,
|
static void rcheevos_async_submit_lboard(rcheevos_locals_t *locals,
|
||||||
rcheevos_async_io_request* request);
|
rcheevos_async_io_request* request);
|
||||||
|
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals);
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
Supporting functions.
|
Supporting functions.
|
||||||
|
@ -351,48 +352,13 @@ static void rcheevos_log_post_url(
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool rcheevos_condset_contains_memref(const rc_condset_t* condset,
|
|
||||||
const rc_memref_t* memref)
|
|
||||||
{
|
|
||||||
if (condset)
|
|
||||||
{
|
|
||||||
rc_condition_t* cond = NULL;
|
|
||||||
for (cond = condset->conditions; cond; cond = cond->next)
|
|
||||||
{
|
|
||||||
if ( cond->operand1.value.memref == memref
|
|
||||||
|| cond->operand2.value.memref == memref)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool rcheevos_trigger_contains_memref(const rc_trigger_t* trigger,
|
|
||||||
const rc_memref_t* memref)
|
|
||||||
{
|
|
||||||
rc_condset_t* condset;
|
|
||||||
if (!trigger)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (rcheevos_condset_contains_memref(trigger->requirement, memref))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (condset = trigger->alternative; condset; condset = condset->next)
|
|
||||||
{
|
|
||||||
if (rcheevos_condset_contains_memref(condset, memref))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rcheevos_achievement_disabled(rcheevos_racheevo_t* cheevo, unsigned address)
|
static void rcheevos_achievement_disabled(rcheevos_racheevo_t* cheevo, unsigned address)
|
||||||
{
|
{
|
||||||
if (!cheevo)
|
if (!cheevo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement disabled (invalid address %06X): %s\n", address, cheevo->title);
|
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement %u disabled (invalid address %06X): %s\n",
|
||||||
|
cheevo->id, address, cheevo->title);
|
||||||
CHEEVOS_FREE(cheevo->memaddr);
|
CHEEVOS_FREE(cheevo->memaddr);
|
||||||
cheevo->memaddr = NULL;
|
cheevo->memaddr = NULL;
|
||||||
}
|
}
|
||||||
|
@ -402,97 +368,12 @@ static void rcheevos_lboard_disabled(rcheevos_ralboard_t* lboard, unsigned addre
|
||||||
if (!lboard)
|
if (!lboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard disabled (invalid address %06X): %s\n", address, lboard->title);
|
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard %u disabled (invalid address %06X): %s\n",
|
||||||
|
lboard->id, address, lboard->title);
|
||||||
CHEEVOS_FREE(lboard->mem);
|
CHEEVOS_FREE(lboard->mem);
|
||||||
lboard->mem = NULL;
|
lboard->mem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rcheevos_invalidate_address(unsigned address)
|
|
||||||
{
|
|
||||||
unsigned i, count;
|
|
||||||
rcheevos_racheevo_t* cheevo = NULL;
|
|
||||||
rcheevos_ralboard_t* lboard = NULL;
|
|
||||||
/* 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. */
|
|
||||||
rc_memref_t **last_memref = &rcheevos_locals.runtime.memrefs;
|
|
||||||
rc_memref_t *memref = *last_memref;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (memref->address == address && !memref->value.is_indirect)
|
|
||||||
{
|
|
||||||
*last_memref = memref->next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_memref = &memref->next;
|
|
||||||
memref = *last_memref;
|
|
||||||
} while(memref);
|
|
||||||
|
|
||||||
/* If the address is only used indirectly,
|
|
||||||
* don't disable anything dependent on it */
|
|
||||||
if (!memref)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Disable any achievements dependent on the address */
|
|
||||||
for (i = 0; i < 2; ++i)
|
|
||||||
{
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
cheevo = rcheevos_locals.patchdata.core;
|
|
||||||
count = rcheevos_locals.patchdata.core_count;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cheevo = rcheevos_locals.patchdata.unofficial;
|
|
||||||
count = rcheevos_locals.patchdata.unofficial_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (count--)
|
|
||||||
{
|
|
||||||
if (cheevo->memaddr)
|
|
||||||
{
|
|
||||||
const rc_trigger_t* trigger = rc_runtime_get_achievement(
|
|
||||||
&rcheevos_locals.runtime, cheevo->id);
|
|
||||||
|
|
||||||
if (trigger && rcheevos_trigger_contains_memref(trigger, memref))
|
|
||||||
{
|
|
||||||
rcheevos_achievement_disabled(cheevo, address);
|
|
||||||
rc_runtime_deactivate_achievement(&rcheevos_locals.runtime,
|
|
||||||
cheevo->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++cheevo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* disable any leaderboards dependent on the address */
|
|
||||||
lboard = rcheevos_locals.patchdata.lboards;
|
|
||||||
for (i = 0; i < rcheevos_locals.patchdata.lboard_count; ++i, ++lboard)
|
|
||||||
{
|
|
||||||
if (lboard->mem)
|
|
||||||
{
|
|
||||||
const rc_lboard_t* rc_lboard = rc_runtime_get_lboard(
|
|
||||||
&rcheevos_locals.runtime, lboard->id);
|
|
||||||
|
|
||||||
if ( rc_lboard &&
|
|
||||||
( rcheevos_trigger_contains_memref(&rc_lboard->start, memref) ||
|
|
||||||
rcheevos_trigger_contains_memref(&rc_lboard->cancel, memref) ||
|
|
||||||
rcheevos_trigger_contains_memref(&rc_lboard->submit, memref) ||
|
|
||||||
rcheevos_condset_contains_memref(rc_lboard->value.conditions,
|
|
||||||
memref))
|
|
||||||
)
|
|
||||||
{
|
|
||||||
rcheevos_lboard_disabled(lboard, address);
|
|
||||||
rc_runtime_deactivate_lboard(&rcheevos_locals.runtime, lboard->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rcheevos_handle_log_message(const char* message)
|
static void rcheevos_handle_log_message(const char* message)
|
||||||
{
|
{
|
||||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", message);
|
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", message);
|
||||||
|
@ -774,24 +655,6 @@ static void rcheevos_async_task_callback(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals)
|
|
||||||
{
|
|
||||||
rc_memref_t* memref = locals->runtime.memrefs;
|
|
||||||
|
|
||||||
while (memref)
|
|
||||||
{
|
|
||||||
if (!memref->value.is_indirect)
|
|
||||||
{
|
|
||||||
uint8_t* data = rc_libretro_memory_find(&rcheevos_locals.memory,
|
|
||||||
memref->address);
|
|
||||||
if (!data)
|
|
||||||
rcheevos_invalidate_address(memref->address);
|
|
||||||
}
|
|
||||||
|
|
||||||
memref = memref->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rcheevos_activate_achievements(rcheevos_locals_t *locals,
|
static void rcheevos_activate_achievements(rcheevos_locals_t *locals,
|
||||||
rcheevos_racheevo_t* cheevo, unsigned count, unsigned flags)
|
rcheevos_racheevo_t* cheevo, unsigned count, unsigned flags)
|
||||||
{
|
{
|
||||||
|
@ -859,7 +722,9 @@ static int rcheevos_parse(rcheevos_locals_t *locals, const char* json)
|
||||||
|
|
||||||
if ( locals->patchdata.core_count == 0
|
if ( locals->patchdata.core_count == 0
|
||||||
&& locals->patchdata.unofficial_count == 0
|
&& locals->patchdata.unofficial_count == 0
|
||||||
&& locals->patchdata.lboard_count == 0)
|
&& locals->patchdata.lboard_count == 0
|
||||||
|
&& (!locals->patchdata.richpresence_script ||
|
||||||
|
!*locals->patchdata.richpresence_script))
|
||||||
{
|
{
|
||||||
rcheevos_free_patchdata(&locals->patchdata);
|
rcheevos_free_patchdata(&locals->patchdata);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1121,7 +986,7 @@ static void rcheevos_lboard_submit(rcheevos_locals_t *locals,
|
||||||
char formatted_value[16];
|
char formatted_value[16];
|
||||||
|
|
||||||
/* Show the OSD message (regardless of notifications setting). */
|
/* Show the OSD message (regardless of notifications setting). */
|
||||||
rc_format_value(formatted_value, sizeof(formatted_value),
|
rc_runtime_format_lboard_value(formatted_value, sizeof(formatted_value),
|
||||||
value, lboard->format);
|
value, lboard->format);
|
||||||
|
|
||||||
CHEEVOS_LOG(RCHEEVOS_TAG "Submitting %s for leaderboard %u\n",
|
CHEEVOS_LOG(RCHEEVOS_TAG "Submitting %s for leaderboard %u\n",
|
||||||
|
@ -1191,7 +1056,7 @@ static void rcheevos_lboard_started(rcheevos_ralboard_t * lboard, int value,
|
||||||
#if defined(HAVE_GFX_WIDGETS)
|
#if defined(HAVE_GFX_WIDGETS)
|
||||||
if (widgets_ready && rcheevos_locals.leaderboard_trackers)
|
if (widgets_ready && rcheevos_locals.leaderboard_trackers)
|
||||||
{
|
{
|
||||||
rc_format_value(buffer, sizeof(buffer), value, lboard->format);
|
rc_runtime_format_lboard_value(buffer, sizeof(buffer), value, lboard->format);
|
||||||
gfx_widgets_set_leaderboard_display(lboard->id, buffer);
|
gfx_widgets_set_leaderboard_display(lboard->id, buffer);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1222,7 +1087,7 @@ static void rcheevos_lboard_updated(rcheevos_ralboard_t* lboard, int value,
|
||||||
if (widgets_ready && rcheevos_locals.leaderboard_trackers)
|
if (widgets_ready && rcheevos_locals.leaderboard_trackers)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
rc_format_value(buffer, sizeof(buffer), value, lboard->format);
|
rc_runtime_format_lboard_value(buffer, sizeof(buffer), value, lboard->format);
|
||||||
gfx_widgets_set_leaderboard_display(lboard->id, buffer);
|
gfx_widgets_set_leaderboard_display(lboard->id, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1670,6 +1535,17 @@ static void rcheevos_runtime_event_handler(const rc_runtime_event_t* runtime_eve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rcheevos_runtime_address_validator(unsigned address)
|
||||||
|
{
|
||||||
|
return (rc_libretro_memory_find(&rcheevos_locals.memory, address) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals)
|
||||||
|
{
|
||||||
|
rc_runtime_validate_addresses(&locals->runtime,
|
||||||
|
rcheevos_runtime_event_handler, rcheevos_runtime_address_validator);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
Test all the achievements (call once per frame).
|
Test all the achievements (call once per frame).
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@ -1949,11 +1825,21 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
|
||||||
"This game has no achievements.",
|
"This game has no achievements.",
|
||||||
0, 5 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
0, 5 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||||
|
|
||||||
rcheevos_pause_hardcore();
|
if (rcheevos_locals.patchdata.richpresence_script &&
|
||||||
|
*rcheevos_locals.patchdata.richpresence_script)
|
||||||
|
{
|
||||||
|
rcheevos_locals.loaded = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
rcheevos_pause_hardcore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
rcheevos_locals.loaded = true;
|
rcheevos_locals.loaded = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if HAVE_REWIND
|
#if HAVE_REWIND
|
||||||
if (!rcheevos_locals.hardcore_active)
|
if (!rcheevos_locals.hardcore_active)
|
||||||
|
|
|
@ -130,6 +130,9 @@ typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_eve
|
||||||
|
|
||||||
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
|
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
|
||||||
void rc_runtime_reset(rc_runtime_t* runtime);
|
void rc_runtime_reset(rc_runtime_t* runtime);
|
||||||
|
|
||||||
|
typedef int (*rc_runtime_validate_address_t)(unsigned address);
|
||||||
|
void rc_runtime_validate_addresses(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler);
|
||||||
void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned address);
|
void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned address);
|
||||||
|
|
||||||
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
|
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
|
||||||
|
|
|
@ -239,10 +239,18 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
|
||||||
|
|
||||||
switch (self->oper) {
|
switch (self->oper) {
|
||||||
case RC_OPERATOR_MULT:
|
case RC_OPERATOR_MULT:
|
||||||
if (self->operand2.type == RC_OPERAND_FP)
|
if (self->operand2.type == RC_OPERAND_FP) {
|
||||||
value = (int)((double)value * self->operand2.value.dbl);
|
value = (int)((double)value * self->operand2.value.dbl);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
|
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
|
||||||
|
* to the type's size. this allows negative multiplication through twos-complements. i.e.
|
||||||
|
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
|
||||||
|
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
|
||||||
|
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
|
||||||
|
*/
|
||||||
value *= rc_evaluate_operand(&self->operand2, eval_state);
|
value *= rc_evaluate_operand(&self->operand2, eval_state);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_OPERATOR_DIV:
|
case RC_OPERATOR_DIV:
|
||||||
|
|
|
@ -338,10 +338,14 @@ static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
|
||||||
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
|
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
|
||||||
|
|
||||||
/* ===== Magnavox Odyssey 2 ===== */
|
/* ===== Magnavox Odyssey 2 ===== */
|
||||||
|
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
|
||||||
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
|
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
|
||||||
{ 0x000000U, 0x00003FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
/* Internal and external RAMs are reachable using unique instructions.
|
||||||
|
* The real addresses provided are virtual and for mapping purposes only. */
|
||||||
|
{ 0x000000U, 0x00003FU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Internal RAM" },
|
||||||
|
{ 0x000040U, 0x00013FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "External RAM" }
|
||||||
};
|
};
|
||||||
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 1 };
|
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 2 };
|
||||||
|
|
||||||
/* ===== Master System ===== */
|
/* ===== Master System ===== */
|
||||||
/* http://www.smspower.org/Development/MemoryMap */
|
/* http://www.smspower.org/Development/MemoryMap */
|
||||||
|
@ -374,8 +378,9 @@ static const rc_memory_regions_t rc_memory_regions_msx = { _rc_memory_regions_ms
|
||||||
/* ===== Neo Geo Pocket ===== */
|
/* ===== Neo Geo Pocket ===== */
|
||||||
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
|
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
|
||||||
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
|
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
|
||||||
/* MednafenNGP exposes 16KB, but the doc suggests there's 24-32KB */
|
/* The docs suggest there's Work RAM exposed from $0000-$6FFF, Sound RAM from $7000-$7FFF, and Video
|
||||||
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
* RAM from $8000-$BFFF, but both MednafenNGP and FBNeo only expose system RAM from $4000-$7FFF */
|
||||||
|
{ 0x000000U, 0x003FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||||
};
|
};
|
||||||
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };
|
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,86 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||||
return RC_OK;
|
return RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||||
|
|
||||||
|
unsigned rc_transform_memref_value(unsigned value, char size)
|
||||||
|
{
|
||||||
|
switch (size)
|
||||||
|
{
|
||||||
|
case RC_MEMSIZE_BIT_0:
|
||||||
|
value = (value >> 0) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_1:
|
||||||
|
value = (value >> 1) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_2:
|
||||||
|
value = (value >> 2) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_3:
|
||||||
|
value = (value >> 3) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_4:
|
||||||
|
value = (value >> 4) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_5:
|
||||||
|
value = (value >> 5) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_6:
|
||||||
|
value = (value >> 6) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BIT_7:
|
||||||
|
value = (value >> 7) & 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_LOW:
|
||||||
|
value = value & 0x0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_HIGH:
|
||||||
|
value = (value >> 4) & 0x0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_BITCOUNT:
|
||||||
|
value = rc_bits_set[(value & 0x0F)]
|
||||||
|
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char rc_memref_shared_size(char size)
|
||||||
|
{
|
||||||
|
switch (size) {
|
||||||
|
case RC_MEMSIZE_BIT_0:
|
||||||
|
case RC_MEMSIZE_BIT_1:
|
||||||
|
case RC_MEMSIZE_BIT_2:
|
||||||
|
case RC_MEMSIZE_BIT_3:
|
||||||
|
case RC_MEMSIZE_BIT_4:
|
||||||
|
case RC_MEMSIZE_BIT_5:
|
||||||
|
case RC_MEMSIZE_BIT_6:
|
||||||
|
case RC_MEMSIZE_BIT_7:
|
||||||
|
case RC_MEMSIZE_LOW:
|
||||||
|
case RC_MEMSIZE_HIGH:
|
||||||
|
case RC_MEMSIZE_BITCOUNT:
|
||||||
|
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_transform_memref_value */
|
||||||
|
return RC_MEMSIZE_8_BITS;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
||||||
unsigned value;
|
unsigned value;
|
||||||
|
|
||||||
|
@ -103,46 +183,6 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
|
||||||
|
|
||||||
switch (size)
|
switch (size)
|
||||||
{
|
{
|
||||||
case RC_MEMSIZE_BIT_0:
|
|
||||||
value = (peek(address, 1, ud) >> 0) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_1:
|
|
||||||
value = (peek(address, 1, ud) >> 1) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_2:
|
|
||||||
value = (peek(address, 1, ud) >> 2) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_3:
|
|
||||||
value = (peek(address, 1, ud) >> 3) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_4:
|
|
||||||
value = (peek(address, 1, ud) >> 4) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_5:
|
|
||||||
value = (peek(address, 1, ud) >> 5) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_6:
|
|
||||||
value = (peek(address, 1, ud) >> 6) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_7:
|
|
||||||
value = (peek(address, 1, ud) >> 7) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_LOW:
|
|
||||||
value = peek(address, 1, ud) & 0x0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_HIGH:
|
|
||||||
value = (peek(address, 1, ud) >> 4) & 0x0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_8_BITS:
|
case RC_MEMSIZE_8_BITS:
|
||||||
value = peek(address, 1, ud);
|
value = peek(address, 1, ud);
|
||||||
break;
|
break;
|
||||||
|
@ -161,7 +201,15 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
if (rc_memref_shared_size(size) == RC_MEMSIZE_8_BITS)
|
||||||
|
{
|
||||||
|
value = peek(address, 1, ud);
|
||||||
|
value = rc_transform_memref_value(value, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,27 +102,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||||
if (ret != RC_OK)
|
if (ret != RC_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
switch (self->size) {
|
size = rc_memref_shared_size(self->size);
|
||||||
case RC_MEMSIZE_BIT_0:
|
|
||||||
case RC_MEMSIZE_BIT_1:
|
|
||||||
case RC_MEMSIZE_BIT_2:
|
|
||||||
case RC_MEMSIZE_BIT_3:
|
|
||||||
case RC_MEMSIZE_BIT_4:
|
|
||||||
case RC_MEMSIZE_BIT_5:
|
|
||||||
case RC_MEMSIZE_BIT_6:
|
|
||||||
case RC_MEMSIZE_BIT_7:
|
|
||||||
case RC_MEMSIZE_LOW:
|
|
||||||
case RC_MEMSIZE_HIGH:
|
|
||||||
case RC_MEMSIZE_BITCOUNT:
|
|
||||||
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_evaluate_operand */
|
|
||||||
size = RC_MEMSIZE_8_BITS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
size = self->size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
|
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
|
||||||
if (parse->offset < 0)
|
if (parse->offset < 0)
|
||||||
return parse->offset;
|
return parse->offset;
|
||||||
|
@ -295,8 +275,6 @@ int rc_operand_is_memref(rc_operand_t* self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
|
||||||
|
|
||||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||||
#ifndef RC_DISABLE_LUA
|
#ifndef RC_DISABLE_LUA
|
||||||
rc_luapeek_t luapeek;
|
rc_luapeek_t luapeek;
|
||||||
|
@ -348,56 +326,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* step 2: mask off appropriate bits */
|
/* step 2: mask off appropriate bits */
|
||||||
switch (self->size)
|
value = rc_transform_memref_value(value, self->size);
|
||||||
{
|
|
||||||
case RC_MEMSIZE_BIT_0:
|
|
||||||
value = (value >> 0) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_1:
|
|
||||||
value = (value >> 1) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_2:
|
|
||||||
value = (value >> 2) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_3:
|
|
||||||
value = (value >> 3) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_4:
|
|
||||||
value = (value >> 4) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_5:
|
|
||||||
value = (value >> 5) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_6:
|
|
||||||
value = (value >> 6) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_7:
|
|
||||||
value = (value >> 7) & 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_LOW:
|
|
||||||
value = value & 0x0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_HIGH:
|
|
||||||
value = (value >> 4) & 0x0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_BITCOUNT:
|
|
||||||
value = rc_bits_set[(value & 0x0F)]
|
|
||||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* step 3: apply logic */
|
/* step 3: apply logic */
|
||||||
switch (self->type)
|
switch (self->type)
|
||||||
|
|
|
@ -117,6 +117,8 @@ void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
|
||||||
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
|
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
|
||||||
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
|
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
|
||||||
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
|
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
|
||||||
|
char rc_memref_shared_size(char size);
|
||||||
|
unsigned rc_transform_memref_value(unsigned value, char size);
|
||||||
|
|
||||||
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
|
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||||
int rc_trigger_state_active(int state);
|
int rc_trigger_state_active(int state);
|
||||||
|
|
|
@ -131,7 +131,7 @@ static int rc_libretro_match_value(const char* val, const char* match) {
|
||||||
if (*match == ',') {
|
if (*match == ',') {
|
||||||
do {
|
do {
|
||||||
const char* ptr = ++match;
|
const char* ptr = ++match;
|
||||||
int size;
|
size_t size;
|
||||||
|
|
||||||
while (*match && *match != ',')
|
while (*match && *match != ',')
|
||||||
++match;
|
++match;
|
||||||
|
@ -218,7 +218,7 @@ unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regio
|
||||||
return ®ions->data[i][address];
|
return ®ions->data[i][address];
|
||||||
}
|
}
|
||||||
|
|
||||||
address -= size;
|
address -= (unsigned)size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -360,8 +360,8 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X",
|
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X%s",
|
||||||
(unsigned)(desc - mmap->descriptors) + 1, (int)offset);
|
(unsigned)(desc - mmap->descriptors) + 1, (int)offset, desc->ptr ? "" : " [no pointer]");
|
||||||
|
|
||||||
if (desc->ptr) {
|
if (desc->ptr) {
|
||||||
desc_start = (uint8_t*)desc->ptr + desc->offset;
|
desc_start = (uint8_t*)desc->ptr + desc->offset;
|
||||||
|
@ -387,7 +387,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||||
else {
|
else {
|
||||||
rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
|
rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
|
||||||
console_region_size -= desc_size;
|
console_region_size -= desc_size;
|
||||||
real_address += desc_size;
|
real_address += (unsigned)desc_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -22,7 +22,8 @@ static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* mema
|
||||||
if (rc_parse_memref(&end, &size, &address) == RC_OK) {
|
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 */
|
/* 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 (end == &memaddr[memaddr_len]) {
|
||||||
/* just a memory reference, allocate it */
|
/* 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;
|
return &rc_alloc_memref(parse, address, size, 0)->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -491,23 +491,32 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||||
|
|
||||||
old_state = trigger->state;
|
old_state = trigger->state;
|
||||||
new_state = rc_evaluate_trigger(trigger, peek, ud, L);
|
new_state = rc_evaluate_trigger(trigger, peek, ud, L);
|
||||||
|
|
||||||
|
/* the trigger state doesn't actually change to RESET, RESET just serves as a notification.
|
||||||
|
* handle the notification, then look at the actual state */
|
||||||
|
if (new_state == RC_TRIGGER_STATE_RESET)
|
||||||
|
{
|
||||||
|
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
|
||||||
|
runtime_event.id = self->triggers[i].id;
|
||||||
|
event_handler(&runtime_event);
|
||||||
|
|
||||||
|
new_state = trigger->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if the state hasn't changed, there won't be any events raised */
|
||||||
if (new_state == old_state)
|
if (new_state == old_state)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* raise an UNPRIMED event when changing from UNPRIMED to anything else */
|
||||||
if (old_state == RC_TRIGGER_STATE_PRIMED) {
|
if (old_state == RC_TRIGGER_STATE_PRIMED) {
|
||||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
|
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
|
||||||
runtime_event.id = self->triggers[i].id;
|
runtime_event.id = self->triggers[i].id;
|
||||||
event_handler(&runtime_event);
|
event_handler(&runtime_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* raise events for each of the possible new states */
|
||||||
switch (new_state)
|
switch (new_state)
|
||||||
{
|
{
|
||||||
case RC_TRIGGER_STATE_RESET:
|
|
||||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
|
|
||||||
runtime_event.id = self->triggers[i].id;
|
|
||||||
event_handler(&runtime_event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_TRIGGER_STATE_TRIGGERED:
|
case RC_TRIGGER_STATE_TRIGGERED:
|
||||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
|
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
|
||||||
runtime_event.id = self->triggers[i].id;
|
runtime_event.id = self->triggers[i].id;
|
||||||
|
@ -527,6 +536,8 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_TRIGGER_STATE_ACTIVE:
|
case RC_TRIGGER_STATE_ACTIVE:
|
||||||
|
/* only raise ACTIVATED event when transitioning from an inactive state.
|
||||||
|
* note that inactive in this case means active but cannot trigger. */
|
||||||
if (old_state == RC_TRIGGER_STATE_WAITING || old_state == RC_TRIGGER_STATE_PAUSED) {
|
if (old_state == RC_TRIGGER_STATE_WAITING || old_state == RC_TRIGGER_STATE_PAUSED) {
|
||||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
|
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
|
||||||
runtime_event.id = self->triggers[i].id;
|
runtime_event.id = self->triggers[i].id;
|
||||||
|
@ -667,32 +678,8 @@ static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memr
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
|
static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref) {
|
||||||
unsigned i;
|
unsigned i;
|
||||||
rc_memref_t* memref;
|
|
||||||
rc_memref_t** last_memref;
|
|
||||||
|
|
||||||
if (!self->memrefs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* 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 = &self->memrefs;
|
|
||||||
memref = *last_memref;
|
|
||||||
do {
|
|
||||||
if (memref->address == address && !memref->value.is_indirect) {
|
|
||||||
*last_memref = memref->next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_memref = &memref->next;
|
|
||||||
memref = *last_memref;
|
|
||||||
} while (memref);
|
|
||||||
|
|
||||||
/* if the address is only used indirectly, don't disable anything dependent on it */
|
|
||||||
if (!memref)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* disable any achievements dependent on the address */
|
/* disable any achievements dependent on the address */
|
||||||
for (i = 0; i < self->trigger_count; ++i) {
|
for (i = 0; i < self->trigger_count; ++i) {
|
||||||
|
@ -726,3 +713,80 @@ void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
|
||||||
|
rc_memref_t** last_memref = &self->memrefs;
|
||||||
|
rc_memref_t* memref = self->memrefs;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_memref = &memref->next;
|
||||||
|
memref = *last_memref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
rc_runtime_invalidate_memref(self, memref);
|
||||||
|
++num_invalid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
last_memref = &memref->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
memref = *last_memref;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_invalid) {
|
||||||
|
rc_runtime_event_t runtime_event;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = self->trigger_count - 1; i >= 0; --i) {
|
||||||
|
rc_trigger_t* trigger = self->triggers[i].trigger;
|
||||||
|
if (trigger && self->triggers[i].invalid_memref) {
|
||||||
|
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
|
||||||
|
runtime_event.id = self->triggers[i].id;
|
||||||
|
runtime_event.value = self->triggers[i].invalid_memref->address;
|
||||||
|
|
||||||
|
trigger->state = RC_TRIGGER_STATE_DISABLED;
|
||||||
|
self->triggers[i].invalid_memref = NULL;
|
||||||
|
|
||||||
|
event_handler(&runtime_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = self->lboard_count - 1; i >= 0; --i) {
|
||||||
|
rc_lboard_t* lboard = self->lboards[i].lboard;
|
||||||
|
if (lboard && self->lboards[i].invalid_memref) {
|
||||||
|
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
|
||||||
|
runtime_event.id = self->lboards[i].id;
|
||||||
|
runtime_event.value = self->lboards[i].invalid_memref->address;
|
||||||
|
|
||||||
|
lboard->state = RC_LBOARD_STATE_DISABLED;
|
||||||
|
self->lboards[i].invalid_memref = NULL;
|
||||||
|
|
||||||
|
event_handler(&runtime_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue