mirror of
https://github.com/libretro/RetroArch
synced 2025-03-28 19:20:35 +00:00
Merge pull request #10612 from Jamiras/rcheevos_90
(cheevos) upgrade to rcheevos 9.0
This commit is contained in:
commit
a8eaf82e06
@ -1851,18 +1851,21 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
cheevos-new/parser.o \
|
||||
cheevos-new/hash.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
|
||||
deps/rcheevos/src/rcheevos/trigger.o \
|
||||
deps/rcheevos/src/rcheevos/condset.o \
|
||||
deps/rcheevos/src/rcheevos/condition.o \
|
||||
deps/rcheevos/src/rcheevos/operand.o \
|
||||
deps/rcheevos/src/rcheevos/term.o \
|
||||
deps/rcheevos/src/rcheevos/expression.o \
|
||||
deps/rcheevos/src/rcheevos/value.o \
|
||||
deps/rcheevos/src/rcheevos/lboard.o \
|
||||
deps/rcheevos/src/rcheevos/alloc.o \
|
||||
deps/rcheevos/src/rcheevos/compat.o \
|
||||
deps/rcheevos/src/rcheevos/condition.o \
|
||||
deps/rcheevos/src/rcheevos/condset.o \
|
||||
deps/rcheevos/src/rcheevos/consoleinfo.o \
|
||||
deps/rcheevos/src/rcheevos/format.o \
|
||||
deps/rcheevos/src/rcheevos/lboard.o \
|
||||
deps/rcheevos/src/rcheevos/memref.o \
|
||||
deps/rcheevos/src/rcheevos/operand.o \
|
||||
deps/rcheevos/src/rcheevos/richpresence.o \
|
||||
deps/rcheevos/src/rcheevos/runtime.o \
|
||||
deps/rcheevos/src/rcheevos/runtime_progress.o \
|
||||
deps/rcheevos/src/rcheevos/trigger.o \
|
||||
deps/rcheevos/src/rcheevos/value.o \
|
||||
deps/rcheevos/src/rhash/hash.o \
|
||||
deps/rcheevos/src/rurl/url.o
|
||||
|
||||
ifeq ($(HAVE_LUA), 1)
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <features/features_cpu.h>
|
||||
#include <formats/cdfs.h>
|
||||
#include <compat/strl.h>
|
||||
#include <rhash.h>
|
||||
#include <../libretro-common/include/rhash.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_math.h>
|
||||
#include <net/net_http.h>
|
||||
@ -81,9 +81,6 @@
|
||||
/* Define this macro to dump all cheevos' addresses. */
|
||||
#undef CHEEVOS_DUMP_ADDRS
|
||||
|
||||
/* Define this macro to remove HTTP timeouts. */
|
||||
#undef CHEEVOS_NO_TIMEOUT
|
||||
|
||||
/* Define this macro to load a JSON file from disk instead of downloading
|
||||
* from retroachievements.org. */
|
||||
#undef CHEEVOS_JSON_OVERRIDE
|
||||
@ -92,7 +89,7 @@
|
||||
* that name. */
|
||||
#undef CHEEVOS_SAVE_JSON
|
||||
|
||||
/* Define this macro to log URLs. */
|
||||
/* Define this macro to log URLs. */
|
||||
#undef CHEEVOS_LOG_URLS
|
||||
|
||||
/* Define this macro to have the password and token logged. THIS WILL DISCLOSE
|
||||
@ -311,69 +308,94 @@ static void rcheevos_get_user_agent(char* buffer)
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
static void rcheevos_log_url(const char* format, const char* url)
|
||||
#ifdef CHEEVOS_LOG_URLS
|
||||
static void rcheevos_filter_url_param(char* url, char* param)
|
||||
{
|
||||
char* start;
|
||||
char* next;
|
||||
size_t param_len = strlen(param);
|
||||
|
||||
start = strchr(url, '?');
|
||||
if (!start)
|
||||
start = url;
|
||||
else
|
||||
++start;
|
||||
|
||||
do
|
||||
{
|
||||
next = strchr(start, '&');
|
||||
|
||||
if (start[param_len] == '=' && memcmp(start, param, param_len) == 0)
|
||||
{
|
||||
if (next)
|
||||
strcpy(start, next + 1);
|
||||
else if (start > url)
|
||||
start[-1] = '\0';
|
||||
else
|
||||
*start = '\0';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return;
|
||||
|
||||
start = next + 1;
|
||||
} while (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void rcheevos_log_url(const char* api, const char* url)
|
||||
{
|
||||
#ifdef CHEEVOS_LOG_URLS
|
||||
#ifdef CHEEVOS_LOG_PASSWORD
|
||||
CHEEVOS_LOG(format, url);
|
||||
#else
|
||||
#ifdef CHEEVOS_LOG_PASSWORD
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s\n", api, url);
|
||||
#else
|
||||
char copy[256];
|
||||
char* aux = NULL;
|
||||
char* next = NULL;
|
||||
|
||||
if (!string_is_empty(url))
|
||||
strlcpy(copy, url, sizeof(copy));
|
||||
|
||||
aux = strstr(copy, "?p=");
|
||||
|
||||
if (!aux)
|
||||
aux = strstr(copy, "&p=");
|
||||
|
||||
if (aux)
|
||||
{
|
||||
aux += 3;
|
||||
next = strchr(aux, '&');
|
||||
|
||||
if (next)
|
||||
{
|
||||
do
|
||||
{
|
||||
*aux++ = *next++;
|
||||
} while (next[-1] != 0);
|
||||
}
|
||||
else
|
||||
*aux = 0;
|
||||
}
|
||||
|
||||
aux = strstr(copy, "?t=");
|
||||
|
||||
if (!aux)
|
||||
aux = strstr(copy, "&t=");
|
||||
|
||||
if (aux)
|
||||
{
|
||||
aux += 3;
|
||||
next = strchr(aux, '&');
|
||||
|
||||
if (next)
|
||||
{
|
||||
do
|
||||
{
|
||||
*aux++ = *next++;
|
||||
} while (next[-1] != 0);
|
||||
}
|
||||
else
|
||||
*aux = 0;
|
||||
}
|
||||
|
||||
CHEEVOS_LOG(format, copy);
|
||||
#endif
|
||||
strlcpy(copy, url, sizeof(copy));
|
||||
rcheevos_filter_url_param(copy, "p");
|
||||
rcheevos_filter_url_param(copy, "t");
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s\n", api, copy);
|
||||
#endif
|
||||
#else
|
||||
(void)format;
|
||||
(void)api;
|
||||
(void)url;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rcheevos_log_post_url(const char* api, const char* url, const char* post)
|
||||
{
|
||||
#ifdef CHEEVOS_LOG_URLS
|
||||
#ifdef CHEEVOS_LOG_PASSWORD
|
||||
if (post && post[0])
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s&%s\n", api, url, post);
|
||||
else
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s\n", api, url);
|
||||
#else
|
||||
if (post && post[0])
|
||||
{
|
||||
char post_copy[2048];
|
||||
strlcpy(post_copy, post, sizeof(post_copy));
|
||||
rcheevos_filter_url_param(post_copy, "p");
|
||||
rcheevos_filter_url_param(post_copy, "t");
|
||||
|
||||
if (post_copy[0])
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s&%s\n", api, url, post_copy);
|
||||
else
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s\n", api, url);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "%s: %s\n", api, url);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
(void)api;
|
||||
(void)url;
|
||||
(void)post;
|
||||
#endif
|
||||
}
|
||||
|
||||
static retro_time_t rcheevos_async_send_rich_presence(rcheevos_async_io_request* request);
|
||||
static void rcheevos_async_award_achievement(rcheevos_async_io_request* request);
|
||||
static void rcheevos_async_submit_lboard(rcheevos_async_io_request* request);
|
||||
@ -442,39 +464,6 @@ static void rcheevos_async_task_callback(retro_task_t* task, void* task_data, vo
|
||||
}
|
||||
}
|
||||
|
||||
static const char* rcheevos_rc_error(int ret)
|
||||
{
|
||||
switch (ret)
|
||||
{
|
||||
case RC_OK: return "Ok";
|
||||
case RC_INVALID_LUA_OPERAND: return "Invalid Lua operand";
|
||||
case RC_INVALID_MEMORY_OPERAND: return "Invalid memory operand";
|
||||
case RC_INVALID_CONST_OPERAND: return "Invalid constant operand";
|
||||
case RC_INVALID_FP_OPERAND: return "Invalid floating-point operand";
|
||||
case RC_INVALID_CONDITION_TYPE: return "Invalid condition type";
|
||||
case RC_INVALID_OPERATOR: return "Invalid operator";
|
||||
case RC_INVALID_REQUIRED_HITS: return "Invalid required hits";
|
||||
case RC_DUPLICATED_START: return "Duplicated start condition";
|
||||
case RC_DUPLICATED_CANCEL: return "Duplicated cancel condition";
|
||||
case RC_DUPLICATED_SUBMIT: return "Duplicated submit condition";
|
||||
case RC_DUPLICATED_VALUE: return "Duplicated value expression";
|
||||
case RC_DUPLICATED_PROGRESS: return "Duplicated progress expression";
|
||||
case RC_MISSING_START: return "Missing start condition";
|
||||
case RC_MISSING_CANCEL: return "Missing cancel condition";
|
||||
case RC_MISSING_SUBMIT: return "Missing submit condition";
|
||||
case RC_MISSING_VALUE: return "Missing value expression";
|
||||
case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
|
||||
case RC_MISSING_DISPLAY_STRING: return "Missing display string";
|
||||
case RC_OUT_OF_MEMORY: return "Out of memory";
|
||||
case RC_INVALID_VALUE_FLAG: return "Invalid flag in value expression";
|
||||
case RC_MISSING_VALUE_MEASURED: return "Missing measured flag in value expression";
|
||||
case RC_MULTIPLE_MEASURED: return "Multiple measured targets";
|
||||
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
|
||||
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
static int rcheevos_parse(const char* json)
|
||||
{
|
||||
char buffer[256];
|
||||
@ -604,7 +593,7 @@ static int rcheevos_parse(const char* json)
|
||||
if (res < 0)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "Error in achievement %d \"%s\": %s",
|
||||
cheevo->info->id, cheevo->info->title, rcheevos_rc_error(res));
|
||||
cheevo->info->id, cheevo->info->title, rc_error_str(res));
|
||||
|
||||
if (settings->bools.cheevos_verbose_enable)
|
||||
runloop_msg_queue_push(buffer, 0, 4 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
@ -641,7 +630,7 @@ static int rcheevos_parse(const char* json)
|
||||
if (res < 0)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "Error in leaderboard %d \"%s\": %s",
|
||||
lboard->info->id, lboard->info->title, rcheevos_rc_error(res));
|
||||
lboard->info->id, lboard->info->title, rc_error_str(res));
|
||||
|
||||
if (settings->bools.cheevos_verbose_enable)
|
||||
runloop_msg_queue_push(buffer, 0, 4 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
@ -671,7 +660,7 @@ static int rcheevos_parse(const char* json)
|
||||
int buffer_size = rc_richpresence_size(rcheevos_locals.patchdata.richpresence_script);
|
||||
if (buffer_size <= 0)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "Error in rich presence: %s", rcheevos_rc_error(buffer_size));
|
||||
snprintf(buffer, sizeof(buffer), "Error in rich presence: %s", rc_error_str(buffer_size));
|
||||
|
||||
if (settings->bools.cheevos_verbose_enable)
|
||||
runloop_msg_queue_push(buffer, 0, 4 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
@ -721,7 +710,7 @@ static void rcheevos_async_award_achievement(rcheevos_async_io_request* request)
|
||||
{
|
||||
char buffer[256];
|
||||
settings_t *settings = config_get_ptr();
|
||||
int ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, rcheevos_locals.token, request->id, request->hardcore);
|
||||
int ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, rcheevos_locals.token, request->id, request->hardcore, rcheevos_locals.hash);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
@ -730,7 +719,7 @@ static void rcheevos_async_award_achievement(rcheevos_async_io_request* request)
|
||||
return;
|
||||
}
|
||||
|
||||
rcheevos_log_url(RCHEEVOS_TAG "rc_url_award_cheevo: %s\n", buffer);
|
||||
rcheevos_log_url("rc_url_award_cheevo", buffer);
|
||||
task_push_http_transfer_with_user_agent(buffer, true, NULL, request->user_agent, rcheevos_async_task_callback, request);
|
||||
}
|
||||
|
||||
@ -907,7 +896,7 @@ static void rcheevos_async_submit_lboard(rcheevos_async_io_request* request)
|
||||
char buffer[256];
|
||||
settings_t *settings = config_get_ptr();
|
||||
int ret = rc_url_submit_lboard(buffer, sizeof(buffer), settings->arrays.cheevos_username,
|
||||
rcheevos_locals.token, request->id, request->value, rcheevos_locals.hash);
|
||||
rcheevos_locals.token, request->id, request->value);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
@ -916,7 +905,7 @@ static void rcheevos_async_submit_lboard(rcheevos_async_io_request* request)
|
||||
return;
|
||||
}
|
||||
|
||||
rcheevos_log_url(RCHEEVOS_TAG "rc_url_submit_lboard: %s\n", buffer);
|
||||
rcheevos_log_url("rc_url_submit_lboard", buffer);
|
||||
task_push_http_transfer_with_user_agent(buffer, true, NULL, request->user_agent, rcheevos_async_task_callback, request);
|
||||
}
|
||||
|
||||
@ -971,39 +960,33 @@ static void rcheevos_test_leaderboards(void)
|
||||
switch (rc_evaluate_lboard(lboard->lboard, &lboard->last_value, rcheevos_peek, NULL, NULL))
|
||||
{
|
||||
default:
|
||||
case RC_LBOARD_INACTIVE:
|
||||
break;
|
||||
|
||||
case RC_LBOARD_ACTIVE:
|
||||
/* this is where we would update the onscreen tracker */
|
||||
break;
|
||||
|
||||
case RC_LBOARD_TRIGGERED:
|
||||
case RC_LBOARD_STATE_TRIGGERED:
|
||||
rcheevos_lboard_submit(lboard);
|
||||
break;
|
||||
|
||||
case RC_LBOARD_CANCELED:
|
||||
{
|
||||
case RC_LBOARD_STATE_CANCELED:
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Cancel leaderboard %s\n", lboard->info->title);
|
||||
lboard->active = 0;
|
||||
runloop_msg_queue_push("Leaderboard attempt cancelled!",
|
||||
0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
break;
|
||||
}
|
||||
|
||||
case RC_LBOARD_STARTED:
|
||||
{
|
||||
char buffer[256];
|
||||
case RC_LBOARD_STATE_STARTED:
|
||||
if (!lboard->active)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Leaderboard started: %s\n", lboard->info->title);
|
||||
lboard->active = 1;
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Leaderboard started: %s\n", lboard->info->title);
|
||||
lboard->active = 1;
|
||||
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Leaderboard Active: %s", lboard->info->title);
|
||||
runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
runloop_msg_queue_push(lboard->info->description, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Leaderboard Active: %s", lboard->info->title);
|
||||
runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
runloop_msg_queue_push(lboard->info->description, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rcheevos_locals.invalid_peek_address)
|
||||
@ -1046,30 +1029,31 @@ static retro_time_t rcheevos_async_send_rich_presence(rcheevos_async_io_request*
|
||||
|
||||
{
|
||||
char url[256], post_data[1024];
|
||||
int ret = rc_url_ping(url, sizeof(url), post_data, sizeof(post_data),
|
||||
cheevos_username, rcheevos_locals.token, rcheevos_locals.patchdata.game_id,
|
||||
rcheevos_locals.richpresence.evaluation);
|
||||
|
||||
snprintf(url, sizeof(url),
|
||||
"http://retroachievements.org/dorequest.php?r=ping&u=%s&t=%s",
|
||||
cheevos_username, rcheevos_locals.token);
|
||||
|
||||
if (rcheevos_locals.richpresence.evaluation[0])
|
||||
if (ret < 0)
|
||||
{
|
||||
char* tmp = NULL;
|
||||
net_http_urlencode(&tmp, rcheevos_locals.richpresence.evaluation);
|
||||
snprintf(post_data, sizeof(post_data), "g=%u&m=%s", rcheevos_locals.patchdata.game_id, tmp);
|
||||
CHEEVOS_FREE(tmp);
|
||||
|
||||
#ifdef HAVE_DISCORD
|
||||
if (settings->bools.discord_enable)
|
||||
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS, false);
|
||||
#endif
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "buffer too small to create URL\n");
|
||||
}
|
||||
else
|
||||
snprintf(post_data, sizeof(post_data), "g=%u", rcheevos_locals.patchdata.game_id);
|
||||
{
|
||||
rcheevos_log_post_url("rc_url_ping", url, post_data);
|
||||
|
||||
rcheevos_get_user_agent(request->user_agent);
|
||||
task_push_http_post_transfer_with_user_agent(url, post_data, true, "POST", request->user_agent, NULL, NULL);
|
||||
rcheevos_get_user_agent(request->user_agent);
|
||||
task_push_http_post_transfer_with_user_agent(url, post_data, true, "POST", request->user_agent, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_DISCORD
|
||||
if (rcheevos_locals.richpresence.evaluation[0])
|
||||
{
|
||||
if (settings->bools.discord_enable)
|
||||
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update rich presence every two minutes */
|
||||
if (settings->bools.cheevos_richpresence_enable)
|
||||
return cpu_features_get_time_usec() + CHEEVOS_PING_FREQUENCY;
|
||||
@ -1109,15 +1093,7 @@ void rcheevos_reset_game(void)
|
||||
rc_reset_lboard(lboard->lboard);
|
||||
|
||||
if (lboard->active)
|
||||
{
|
||||
lboard->active = 0;
|
||||
|
||||
/* This ensures the leaderboard won't restart
|
||||
* until the start trigger is false for at
|
||||
* least one frame */
|
||||
if (lboard->lboard)
|
||||
lboard->lboard->submitted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
rcheevos_locals.richpresence.last_update = cpu_features_get_time_usec();
|
||||
@ -2463,14 +2439,6 @@ found:
|
||||
}
|
||||
memcpy(coro->last_hash, coro->hash, sizeof(coro->hash));
|
||||
|
||||
size = rc_url_get_gameid(coro->url, sizeof(coro->url), coro->hash);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "buffer too small to create URL\n");
|
||||
CORO_RET();
|
||||
}
|
||||
|
||||
sprintf(rcheevos_locals.hash, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
coro->hash[0], coro->hash[1], coro->hash[2], coro->hash[3],
|
||||
coro->hash[4], coro->hash[5], coro->hash[6], coro->hash[7],
|
||||
@ -2478,7 +2446,16 @@ found:
|
||||
coro->hash[12], coro->hash[13], coro->hash[14], coro->hash[15]);
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "checking %s\n", rcheevos_locals.hash);
|
||||
rcheevos_log_url(RCHEEVOS_TAG "rc_url_get_gameid: %s\n", coro->url);
|
||||
|
||||
size = rc_url_get_gameid(coro->url, sizeof(coro->url), rcheevos_locals.hash);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "buffer too small to create URL\n");
|
||||
CORO_RET();
|
||||
}
|
||||
|
||||
rcheevos_log_url("rc_url_get_gameid", coro->url);
|
||||
CORO_GOSUB(RCHEEVOS_HTTP_GET);
|
||||
|
||||
if (!coro->json)
|
||||
@ -2511,7 +2488,7 @@ found:
|
||||
CORO_STOP();
|
||||
}
|
||||
|
||||
rcheevos_log_url(RCHEEVOS_TAG "rc_url_get_patch: %s\n", coro->url);
|
||||
rcheevos_log_url("rc_url_get_patch", coro->url);
|
||||
CORO_GOSUB(RCHEEVOS_HTTP_GET);
|
||||
|
||||
if (!coro->json)
|
||||
@ -2651,21 +2628,36 @@ found:
|
||||
}
|
||||
|
||||
if (string_is_empty(coro->settings->arrays.cheevos_token))
|
||||
{
|
||||
ret = rc_url_login_with_password(coro->url, sizeof(coro->url),
|
||||
coro->settings->arrays.cheevos_username,
|
||||
coro->settings->arrays.cheevos_password);
|
||||
|
||||
if (ret == RC_OK)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "attempting to login %s (with password)\n", coro->settings->arrays.cheevos_username);
|
||||
rcheevos_log_url("rc_url_login_with_password", coro->url);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = rc_url_login_with_token(coro->url, sizeof(coro->url),
|
||||
coro->settings->arrays.cheevos_username,
|
||||
coro->settings->arrays.cheevos_token);
|
||||
|
||||
if (ret == RC_OK)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "attempting to login %s (with token)\n", coro->settings->arrays.cheevos_username);
|
||||
rcheevos_log_url("rc_url_login_with_token", coro->url);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "buffer too small to create URL\n");
|
||||
CORO_STOP();
|
||||
}
|
||||
|
||||
rcheevos_log_url(RCHEEVOS_TAG "rc_url_login_with_password: %s\n", coro->url);
|
||||
CORO_GOSUB(RCHEEVOS_HTTP_GET);
|
||||
|
||||
if (!coro->json)
|
||||
@ -2686,6 +2678,7 @@ found:
|
||||
tok);
|
||||
runloop_msg_queue_push(msg, 0, 5 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
*coro->settings->arrays.cheevos_token = 0;
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "login error: %s\n", tok);
|
||||
|
||||
CHEEVOS_FREE(coro->json);
|
||||
CORO_STOP();
|
||||
@ -2703,6 +2696,7 @@ found:
|
||||
runloop_msg_queue_push(msg, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "logged in successfully\n");
|
||||
strlcpy(rcheevos_locals.token, tok,
|
||||
sizeof(rcheevos_locals.token));
|
||||
|
||||
@ -2838,7 +2832,7 @@ found:
|
||||
CORO_STOP();
|
||||
}
|
||||
|
||||
rcheevos_log_url(RCHEEVOS_TAG "rc_url_get_unlock_list: %s\n", coro->url);
|
||||
rcheevos_log_url("rc_url_get_unlock_list", coro->url);
|
||||
CORO_GOSUB(RCHEEVOS_HTTP_GET);
|
||||
|
||||
if (coro->json)
|
||||
@ -2862,15 +2856,19 @@ found:
|
||||
*************************************************************************/
|
||||
CORO_SUB(RCHEEVOS_PLAYING)
|
||||
|
||||
snprintf(
|
||||
coro->url, sizeof(coro->url),
|
||||
"http://retroachievements.org/dorequest.php?r=postactivity&u=%s&t=%s&a=3&m=%u",
|
||||
{
|
||||
int ret = rc_url_post_playing(coro->url, sizeof(coro->url),
|
||||
coro->settings->arrays.cheevos_username,
|
||||
rcheevos_locals.token, coro->gameid
|
||||
);
|
||||
rcheevos_locals.token, coro->gameid);
|
||||
|
||||
coro->url[sizeof(coro->url) - 1] = 0;
|
||||
rcheevos_log_url(RCHEEVOS_TAG "url to post the 'playing' activity: %s\n", coro->url);
|
||||
if (ret < 0)
|
||||
{
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG "buffer too small to create URL\n");
|
||||
CORO_STOP();
|
||||
}
|
||||
}
|
||||
|
||||
rcheevos_log_url("rc_url_post_playing", coro->url);
|
||||
|
||||
CORO_GOSUB(RCHEEVOS_HTTP_GET);
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../core.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rcheevos.h"
|
||||
#include "../deps/rcheevos/include/rconsoles.h"
|
||||
|
||||
static int rcheevos_cmpaddr(const void* e1, const void* e2)
|
||||
{
|
||||
|
49
deps/rcheevos/CHANGELOG.md
vendored
49
deps/rcheevos/CHANGELOG.md
vendored
@ -1,3 +1,52 @@
|
||||
# v9.0.0
|
||||
|
||||
* new size: RC_MEMSIZE_BITCOUNT
|
||||
* new flag: RC_CONDITION_OR_NEXT
|
||||
* new flag: RC_CONDITION_TRIGGER
|
||||
* new flag: RC_CONDITION_MEASURED_IF
|
||||
* new operators: RC_OPERATOR_MULT / RC_OPERATOR_DIV
|
||||
* is_bcd removed from memref - now part of RC_MEMSIZE
|
||||
* add rc_runtime_t and associated functions
|
||||
* add rc_hash_ functions
|
||||
* add rc_error_str function
|
||||
* add game_hash parameter to rc_url_award_cheevo
|
||||
* remove hash parameter from rc_url_submit_lboard
|
||||
* add rc_url_ping function
|
||||
* add rc_console_ functions
|
||||
|
||||
# v8.1.0
|
||||
|
||||
* new flag: RC_CONDITION_MEASURED
|
||||
* new flag: RC_CONDITION_ADD_ADDRESS
|
||||
* add rc_evaluate_trigger - extended version of rc_test_trigger with more granular return codes
|
||||
* make rc_evaluate_value return a signed int (was unsigned int)
|
||||
* new formats: RC_FORMAT_MINUTES and RC_FORMAT_SECONDS_AS_MINUTES
|
||||
* removed " Points" text from RC_FORMAT_SCORE format
|
||||
* removed RC_FORMAT_OTHER format. "OTHER" format now parses to RC_FORMAT_SCORE
|
||||
* bugfix: AddHits will now honor AndNext on previous condition
|
||||
|
||||
# v8.0.1
|
||||
|
||||
* bugfix: prevent null reference exception if rich presence contains condition without display string
|
||||
* bugfix: 24-bit read from memory should only read 24-bits
|
||||
|
||||
# v8.0.0
|
||||
|
||||
* support for prior operand type
|
||||
* support for AndNext condition flag
|
||||
* support for rich presence
|
||||
* bugfix: update delta/prior memory values while group is paused
|
||||
* bugfix: allow floating point number without leading 0
|
||||
* bugfix: support empty alt groups
|
||||
|
||||
# v7.1.1
|
||||
|
||||
* Address signed/unsigned mismatch warnings
|
||||
|
||||
# v7.1.0
|
||||
|
||||
* Added the RC_DISABLE_LUA macro to compile rcheevos without Lua support
|
||||
|
||||
# v7.0.2
|
||||
|
||||
* Make sure the code is C89-compliant
|
||||
|
287
deps/rcheevos/README.md
vendored
287
deps/rcheevos/README.md
vendored
@ -59,10 +59,16 @@ enum {
|
||||
RC_INVALID_VALUE_FLAG = -20,
|
||||
RC_MISSING_VALUE_MEASURED = -21,
|
||||
RC_MULTIPLE_MEASURED = -22,
|
||||
RC_INVALID_MEASURED_TARGET = -23
|
||||
RC_INVALID_MEASURED_TARGET = -23,
|
||||
RC_INVALID_COMPARISON = -24
|
||||
};
|
||||
```
|
||||
|
||||
To convert the return code into something human-readable, pass it to:
|
||||
```c
|
||||
const char* rc_error_str(int ret);
|
||||
```
|
||||
|
||||
### Console identifiers
|
||||
|
||||
This enumeration uniquely identifies each of the supported platforms in RetroAchievements.
|
||||
@ -92,14 +98,38 @@ enum {
|
||||
RC_CONSOLE_PLAYSTATION_2 = 21,
|
||||
RC_CONSOLE_XBOX = 22,
|
||||
RC_CONSOLE_SKYNET = 23,
|
||||
RC_CONSOLE_XBOX_ONE = 24,
|
||||
RC_CONSOLE_POKEMON_MINI = 24,
|
||||
RC_CONSOLE_ATARI_2600 = 25,
|
||||
RC_CONSOLE_MS_DOS = 26,
|
||||
RC_CONSOLE_ARCADE = 27,
|
||||
RC_CONSOLE_VIRTUAL_BOY = 28,
|
||||
RC_CONSOLE_MSX = 29,
|
||||
RC_CONSOLE_COMMODORE_64 = 30,
|
||||
RC_CONSOLE_ZX81 = 31
|
||||
RC_CONSOLE_ZX81 = 31,
|
||||
RC_CONSOLE_ORIC = 32,
|
||||
RC_CONSOLE_SG1000 = 33,
|
||||
RC_CONSOLE_VIC20 = 34,
|
||||
RC_CONSOLE_AMIGA = 35,
|
||||
RC_CONSOLE_AMIGA_ST = 36,
|
||||
RC_CONSOLE_AMSTRAD_PC = 37,
|
||||
RC_CONSOLE_APPLE_II = 38,
|
||||
RC_CONSOLE_SATURN = 39,
|
||||
RC_CONSOLE_DREAMCAST = 40,
|
||||
RC_CONSOLE_PSP = 41,
|
||||
RC_CONSOLE_CDI = 42,
|
||||
RC_CONSOLE_3DO = 43,
|
||||
RC_CONSOLE_COLECOVISION = 44,
|
||||
RC_CONSOLE_INTELLIVISION = 45,
|
||||
RC_CONSOLE_VECTREX = 46,
|
||||
RC_CONSOLE_PC8800 = 47,
|
||||
RC_CONSOLE_PC9800 = 48,
|
||||
RC_CONSOLE_PCFX = 49,
|
||||
RC_CONSOLE_ATARI_5200 = 50,
|
||||
RC_CONSOLE_ATARI_7800 = 51,
|
||||
RC_CONSOLE_X68K = 52,
|
||||
RC_CONSOLE_WONDERSWAN = 53,
|
||||
RC_CONSOLE_CASSETTEVISION = 54,
|
||||
RC_CONSOLE_SUPER_CASSETTEVISION = 55
|
||||
};
|
||||
```
|
||||
|
||||
@ -115,27 +145,23 @@ An operand is the leaf node of RetroAchievements expressions, and can hold one o
|
||||
typedef struct {
|
||||
union {
|
||||
/* A value read from memory. */
|
||||
struct {
|
||||
/* The memory address or constant value of this variable. */
|
||||
unsigned value;
|
||||
/* The previous memory contents if RC_OPERAND_DELTA. */
|
||||
unsigned previous;
|
||||
rc_memref_value_t* memref;
|
||||
|
||||
/* The size of the variable. */
|
||||
char size;
|
||||
/* True if the value is in BCD. */
|
||||
char is_bcd;
|
||||
/* The type of the variable. */
|
||||
};
|
||||
/* An integer value. */
|
||||
unsigned num;
|
||||
|
||||
/* A floating point value. */
|
||||
double fp_value;
|
||||
double dbl;
|
||||
|
||||
/* A reference to the Lua function that provides the value. */
|
||||
int function_ref;
|
||||
int luafunc;
|
||||
};
|
||||
|
||||
/* specifies which member of the value union is being used */
|
||||
char type;
|
||||
|
||||
/* the actual RC_MEMSIZE of the operand - memref.size may differ */
|
||||
char size;
|
||||
}
|
||||
rc_operand_t;
|
||||
```
|
||||
@ -144,20 +170,21 @@ The `size` field, when applicable, holds one of these values:
|
||||
|
||||
```c
|
||||
enum {
|
||||
RC_OPERAND_BIT_0,
|
||||
RC_OPERAND_BIT_1,
|
||||
RC_OPERAND_BIT_2,
|
||||
RC_OPERAND_BIT_3,
|
||||
RC_OPERAND_BIT_4,
|
||||
RC_OPERAND_BIT_5,
|
||||
RC_OPERAND_BIT_6,
|
||||
RC_OPERAND_BIT_7,
|
||||
RC_OPERAND_LOW,
|
||||
RC_OPERAND_HIGH,
|
||||
RC_OPERAND_8_BITS,
|
||||
RC_OPERAND_16_BITS,
|
||||
RC_OPERAND_24_BITS,
|
||||
RC_OPERAND_32_BITS,
|
||||
RC_MEMSIZE_8_BITS,
|
||||
RC_MEMSIZE_16_BITS,
|
||||
RC_MEMSIZE_24_BITS,
|
||||
RC_MEMSIZE_32_BITS,
|
||||
RC_MEMSIZE_LOW,
|
||||
RC_MEMSIZE_HIGH,
|
||||
RC_MEMSIZE_BIT_0,
|
||||
RC_MEMSIZE_BIT_1,
|
||||
RC_MEMSIZE_BIT_2,
|
||||
RC_MEMSIZE_BIT_3,
|
||||
RC_MEMSIZE_BIT_4,
|
||||
RC_MEMSIZE_BIT_5,
|
||||
RC_MEMSIZE_BIT_6,
|
||||
RC_MEMSIZE_BIT_7,
|
||||
RC_MEMSIZE_BITCOUNT
|
||||
};
|
||||
```
|
||||
|
||||
@ -165,15 +192,18 @@ The `type` field is always valid, and holds one of these values:
|
||||
|
||||
```c
|
||||
enum {
|
||||
RC_OPERAND_ADDRESS, /* Compare to the value of a live address in RAM. */
|
||||
RC_OPERAND_DELTA, /* The value last known at this address. */
|
||||
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
|
||||
RC_OPERAND_FP, /* A floating point value. */
|
||||
RC_OPERAND_LUA /* A Lua function that provides the value. */
|
||||
RC_OPERAND_ADDRESS, /* The value of a live address in RAM. */
|
||||
RC_OPERAND_DELTA, /* The value last known at this address. */
|
||||
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
|
||||
RC_OPERAND_FP, /* A floating point value. */
|
||||
RC_OPERAND_LUA, /* A Lua function that provides the value. */
|
||||
RC_OPERAND_PRIOR, /* The last differing value at this address. */
|
||||
RC_OPERAND_BCD, /* The BCD-decoded value of a live address in RAM */
|
||||
RC_OPERAND_INVERTED /* The twos-complement value of a live address in RAM */
|
||||
};
|
||||
```
|
||||
|
||||
`RC_OPERAND_ADDRESS`, `RC_OPERAND_DELTA` and `RC_OPERAND_CONST` mean that the anonymous structure in the union is active. `RC_OPERAND_FP` means that `fp_value` is active. `RC_OPERAND_LUA` means `function_ref` is active.
|
||||
`RC_OPERAND_ADDRESS`, `RC_OPERAND_DELTA`, `RC_OPERAND_PRIOR`, `RC_OPERAND_BCD`, and `RC_OPERAND_INVERTED` mean that `memref` is active. `RC_OPERAND_CONST` means that `num` is active. `RC_OPERAND_FP` means that `dbl` is active. `RC_OPERAND_LUA` means `luafunc` is active.
|
||||
|
||||
|
||||
### `rc_condition_t`
|
||||
@ -184,9 +214,6 @@ A condition compares its two operands according to the defined operator. It also
|
||||
typedef struct rc_condition_t rc_condition_t;
|
||||
|
||||
struct rc_condition_t {
|
||||
/* The next condition in the chain. */
|
||||
rc_condition_t* next;
|
||||
|
||||
/* The condition's operands. */
|
||||
rc_operand_t operand1;
|
||||
rc_operand_t operand2;
|
||||
@ -196,6 +223,9 @@ struct rc_condition_t {
|
||||
/* Number of hits so far. */
|
||||
unsigned current_hits;
|
||||
|
||||
/* The next condition in the chain. */
|
||||
rc_condition_t* next;
|
||||
|
||||
/* The type of the condition. */
|
||||
char type;
|
||||
/* The comparison operator to use. */
|
||||
@ -219,7 +249,9 @@ enum {
|
||||
RC_CONDITION_ADD_HITS,
|
||||
RC_CONDITION_AND_NEXT,
|
||||
RC_CONDITION_MEASURED,
|
||||
RC_CONDITION_ADD_ADDRESS
|
||||
RC_CONDITION_ADD_ADDRESS,
|
||||
RC_CONDITION_TRIGGER,
|
||||
RC_CONDITION_MEASURED_IF
|
||||
};
|
||||
```
|
||||
|
||||
@ -227,13 +259,16 @@ enum {
|
||||
|
||||
```c
|
||||
enum {
|
||||
RC_CONDITION_EQ,
|
||||
RC_CONDITION_LT,
|
||||
RC_CONDITION_LE,
|
||||
RC_CONDITION_GT,
|
||||
RC_CONDITION_GE,
|
||||
RC_CONDITION_NE,
|
||||
RC_CONDITION_NONE
|
||||
RC_OPERATOR_EQ,
|
||||
RC_OPERATOR_LT,
|
||||
RC_OPERATOR_LE,
|
||||
RC_OPERATOR_GT,
|
||||
RC_OPERATOR_GE,
|
||||
RC_OPERATOR_NE,
|
||||
RC_OPERATOR_NONE,
|
||||
RC_OPERATOR_MULT,
|
||||
RC_OPERATOR_DIV,
|
||||
RC_OPERATOR_AND
|
||||
};
|
||||
```
|
||||
|
||||
@ -267,6 +302,9 @@ typedef struct {
|
||||
|
||||
/* The list of sub condition sets in this test. */
|
||||
rc_condset_t* alternative;
|
||||
|
||||
/* The memory references required by the trigger. */
|
||||
rc_memref_value_t* memrefs;
|
||||
}
|
||||
rc_trigger_t;
|
||||
```
|
||||
@ -313,7 +351,8 @@ enum {
|
||||
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
|
||||
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
|
||||
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
|
||||
RC_TRIGGER_STATE_TRIGGERED /* achievement has triggered */
|
||||
RC_TRIGGER_STATE_TRIGGERED, /* achievement has triggered */
|
||||
RC_TRIGGER_STATE_PRIMED /* all non-Trigger conditions are true */
|
||||
};
|
||||
```
|
||||
|
||||
@ -323,52 +362,12 @@ Finally, `rc_reset_trigger` can be used to reset the internal state of a trigger
|
||||
void rc_reset_trigger(rc_trigger_t* self);
|
||||
```
|
||||
|
||||
### `rc_term_t`
|
||||
|
||||
A term is the leaf node of expressions used to compute values from operands. A term is evaluated by multiplying its two operands. `invert` is used to invert the bits of the second operand of the term, when the unary operator `~` is used.
|
||||
|
||||
```c
|
||||
typedef struct rc_term_t rc_term_t;
|
||||
|
||||
struct rc_term_t {
|
||||
/* The next term in this chain. */
|
||||
rc_term_t* next;
|
||||
|
||||
/* The first operand. */
|
||||
rc_operand_t operand1;
|
||||
/* The second operand. */
|
||||
rc_operand_t operand2;
|
||||
|
||||
/* A value that is applied to the second variable to invert its bits. */
|
||||
unsigned invert;
|
||||
};
|
||||
```
|
||||
|
||||
### `rc_expression_t`
|
||||
|
||||
An expression is a collection of terms. All terms in the collection are added together to give the value of the expression.
|
||||
|
||||
```c
|
||||
typedef struct rc_expression_t rc_expression_t;
|
||||
|
||||
struct rc_expression_t {
|
||||
/* The next expression in this chain. */
|
||||
rc_expression_t* next;
|
||||
|
||||
/* The list of terms in this expression. */
|
||||
rc_term_t* terms;
|
||||
};
|
||||
```
|
||||
|
||||
### `rc_value_t`
|
||||
|
||||
A value is a collection of expressions. It's used to give the value for a leaderboard, and it evaluates to value of the expression with the greatest value in the collection.
|
||||
A value is a collection of conditions that result in a single RC_CONDITION_MEASURED expression. It's used to calculate the value for a leaderboard and for lookups in rich presence.
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
/* The list of expression to evaluate. */
|
||||
rc_expression_t* expressions;
|
||||
|
||||
/* The list of conditions to evaluate. */
|
||||
rc_condset_t* conditions;
|
||||
|
||||
@ -413,9 +412,9 @@ typedef struct {
|
||||
rc_trigger_t cancel;
|
||||
rc_value_t value;
|
||||
rc_value_t* progress;
|
||||
rc_memref_value_t* memrefs;
|
||||
|
||||
char started;
|
||||
char submitted;
|
||||
char state;
|
||||
}
|
||||
rc_lboard_t;
|
||||
```
|
||||
@ -437,11 +436,12 @@ The function returns an action that must be performed by the caller, and `value`
|
||||
|
||||
```c
|
||||
enum {
|
||||
RC_LBOARD_INACTIVE,
|
||||
RC_LBOARD_ACTIVE,
|
||||
RC_LBOARD_STARTED,
|
||||
RC_LBOARD_CANCELED,
|
||||
RC_LBOARD_TRIGGERED
|
||||
RC_LBOARD_STATE_INACTIVE, /* leaderboard is not being processed */
|
||||
RC_LBOARD_STATE_WAITING, /* leaderboard cannot activate until the start condition has been false for at least one frame */
|
||||
RC_LBOARD_STATE_ACTIVE, /* leaderboard is active and may start */
|
||||
RC_LBOARD_STATE_STARTED, /* leaderboard attempt in progress */
|
||||
RC_LBOARD_STATE_CANCELED, /* leaderboard attempt canceled */
|
||||
RC_LBOARD_STATE_TRIGGERED /* leaderboard attempt complete, value should be submitted */
|
||||
};
|
||||
```
|
||||
|
||||
@ -458,6 +458,101 @@ The caller must keep track of these values and do the necessary actions:
|
||||
void rc_reset_lboard(rc_lboard_t* lboard);
|
||||
```
|
||||
|
||||
### `rc_runtime_t`
|
||||
|
||||
The runtime encapsulates a set of achievements and leaderboards and manages processing them for each frame. When important things occur, events are raised for the caller via a callback.
|
||||
|
||||
```c
|
||||
typedef struct rc_runtime_t {
|
||||
rc_runtime_trigger_t* triggers;
|
||||
unsigned trigger_count;
|
||||
unsigned trigger_capacity;
|
||||
|
||||
rc_runtime_lboard_t* lboards;
|
||||
unsigned lboard_count;
|
||||
unsigned lboard_capacity;
|
||||
|
||||
rc_runtime_richpresence_t* richpresence;
|
||||
char* richpresence_display_buffer;
|
||||
char richpresence_update_timer;
|
||||
|
||||
rc_memref_value_t* memrefs;
|
||||
rc_memref_value_t** next_memref;
|
||||
}
|
||||
rc_runtime_t;
|
||||
```
|
||||
|
||||
The runtime must first be initialized.
|
||||
```c
|
||||
void rc_runtime_init(rc_runtime_t* runtime);
|
||||
```
|
||||
|
||||
Then individual achievements, leaderboards, and even rich presence can be loaded into the runtime. These functions return RC_OK, or one of the negative value error codes listed above.
|
||||
```c
|
||||
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
|
||||
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
|
||||
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
|
||||
```
|
||||
|
||||
The runtime should be called once per frame to evaluate the state of the active achievements/leaderboards:
|
||||
```c
|
||||
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L);
|
||||
```
|
||||
|
||||
The `event_handler` is a callback function that is called for each event that occurs when processing the frame.
|
||||
```c
|
||||
typedef struct rc_runtime_event_t {
|
||||
unsigned id;
|
||||
int value;
|
||||
char type;
|
||||
}
|
||||
rc_runtime_event_t;
|
||||
|
||||
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
|
||||
```
|
||||
|
||||
The `event.type` field will be one of the following:
|
||||
* RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED (id=achievement id)
|
||||
An achievement starts in the RC_TRIGGER_STATE_WAITING state and cannot trigger until it has been false for at least one frame. This event indicates the achievement is no longer waiting and may trigger on a future frame.
|
||||
* RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED (id=achievement id)
|
||||
One or more conditions in the achievement have disabled the achievement.
|
||||
* RC_RUNTIME_EVENT_ACHIEVEMENT_RESET (id=achievement id)
|
||||
One or more conditions in the achievement have reset any progress captured in the achievement.
|
||||
* RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED (id=achievement id)
|
||||
All conditions for the achievement have been met and the user should be informed.
|
||||
NOTE: If `rc_runtime_reset` is called without deactivating the achievement, it may trigger again.
|
||||
* RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED (id=achievement id)
|
||||
All non-trigger conditions for the achievement have been met. This typically indicates the achievement is a challenge achievement and the challenge is active.
|
||||
* RC_RUNTIME_EVENT_LBOARD_STARTED (id=leaderboard id, value=leaderboard value)
|
||||
The leaderboard's start condition has been met and the user should be informed that a leaderboard attempt has started.
|
||||
* RC_RUNTIME_EVENT_LBOARD_CANCELED (id=leaderboard id, value=leaderboard value)
|
||||
The leaderboard's cancel condition has been met and the user should be informed that a leaderboard attempt has failed.
|
||||
* RC_RUNTIME_EVENT_LBOARD_UPDATED (id=leaderboard id, value=leaderboard value)
|
||||
The leaderboard value has changed.
|
||||
* RC_RUNTIME_EVENT_LBOARD_TRIGGERED (id=leaderboard id, value=leaderboard value)
|
||||
The leaderboard's submit condition has been met and the user should be informed that a leaderboard attempt was successful. The value should be submitted.
|
||||
|
||||
When an achievement triggers, it should be deactivated so it won't trigger again:
|
||||
```c
|
||||
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
|
||||
```
|
||||
Additionally, the unlock should be submitted to the server.
|
||||
|
||||
When a leaderboard triggers, it should not be deactivated in case the player wants to try again for a better score. The value should be submitted to the server.
|
||||
|
||||
`rc_runtime_do_frame` also periodically updates the rich presense string (every 60 frames). To get the current value, call
|
||||
```c
|
||||
const char* rc_runtime_get_richpresence(const rc_runtime_t* runtime);
|
||||
```
|
||||
|
||||
When the game is reset, the runtime should also be reset:
|
||||
```c
|
||||
void rc_runtime_reset(rc_runtime_t* runtime);
|
||||
```
|
||||
|
||||
This ensures any active achievements/leaderboards are set back to their initial states and prevents unexpected triggers when the memory changes in atypical way.
|
||||
|
||||
|
||||
### Value Formatting
|
||||
|
||||
**rcheevos** includes helper functions to parse formatting strings from RetroAchievements, and format values according to them.
|
||||
|
266
deps/rcheevos/include/rcheevos.h
vendored
266
deps/rcheevos/include/rcheevos.h
vendored
@ -35,46 +35,12 @@ enum {
|
||||
RC_INVALID_VALUE_FLAG = -20,
|
||||
RC_MISSING_VALUE_MEASURED = -21,
|
||||
RC_MULTIPLE_MEASURED = -22,
|
||||
RC_INVALID_MEASURED_TARGET = -23
|
||||
RC_INVALID_MEASURED_TARGET = -23,
|
||||
RC_INVALID_COMPARISON = -24,
|
||||
RC_INVALID_STATE = -25
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
| Console identifiers |
|
||||
\*****************************************************************************/
|
||||
|
||||
enum {
|
||||
RC_CONSOLE_MEGA_DRIVE = 1,
|
||||
RC_CONSOLE_NINTENDO_64 = 2,
|
||||
RC_CONSOLE_SUPER_NINTENDO = 3,
|
||||
RC_CONSOLE_GAMEBOY = 4,
|
||||
RC_CONSOLE_GAMEBOY_ADVANCE = 5,
|
||||
RC_CONSOLE_GAMEBOY_COLOR = 6,
|
||||
RC_CONSOLE_NINTENDO = 7,
|
||||
RC_CONSOLE_PC_ENGINE = 8,
|
||||
RC_CONSOLE_SEGA_CD = 9,
|
||||
RC_CONSOLE_SEGA_32X = 10,
|
||||
RC_CONSOLE_MASTER_SYSTEM = 11,
|
||||
RC_CONSOLE_PLAYSTATION = 12,
|
||||
RC_CONSOLE_ATARI_LYNX = 13,
|
||||
RC_CONSOLE_NEOGEO_POCKET = 14,
|
||||
RC_CONSOLE_GAME_GEAR = 15,
|
||||
RC_CONSOLE_GAMECUBE = 16,
|
||||
RC_CONSOLE_ATARI_JAGUAR = 17,
|
||||
RC_CONSOLE_NINTENDO_DS = 18,
|
||||
RC_CONSOLE_WII = 19,
|
||||
RC_CONSOLE_WII_U = 20,
|
||||
RC_CONSOLE_PLAYSTATION_2 = 21,
|
||||
RC_CONSOLE_XBOX = 22,
|
||||
RC_CONSOLE_SKYNET = 23,
|
||||
RC_CONSOLE_XBOX_ONE = 24,
|
||||
RC_CONSOLE_ATARI_2600 = 25,
|
||||
RC_CONSOLE_MS_DOS = 26,
|
||||
RC_CONSOLE_ARCADE = 27,
|
||||
RC_CONSOLE_VIRTUAL_BOY = 28,
|
||||
RC_CONSOLE_MSX = 29,
|
||||
RC_CONSOLE_COMMODORE_64 = 30,
|
||||
RC_CONSOLE_ZX81 = 31
|
||||
};
|
||||
const char* rc_error_str(int ret);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Callbacks |
|
||||
@ -93,6 +59,12 @@ typedef unsigned (*rc_peek_t)(unsigned address, unsigned num_bytes, void* ud);
|
||||
|
||||
/* Sizes. */
|
||||
enum {
|
||||
RC_MEMSIZE_8_BITS,
|
||||
RC_MEMSIZE_16_BITS,
|
||||
RC_MEMSIZE_24_BITS,
|
||||
RC_MEMSIZE_32_BITS,
|
||||
RC_MEMSIZE_LOW,
|
||||
RC_MEMSIZE_HIGH,
|
||||
RC_MEMSIZE_BIT_0,
|
||||
RC_MEMSIZE_BIT_1,
|
||||
RC_MEMSIZE_BIT_2,
|
||||
@ -101,12 +73,7 @@ enum {
|
||||
RC_MEMSIZE_BIT_5,
|
||||
RC_MEMSIZE_BIT_6,
|
||||
RC_MEMSIZE_BIT_7,
|
||||
RC_MEMSIZE_LOW,
|
||||
RC_MEMSIZE_HIGH,
|
||||
RC_MEMSIZE_8_BITS,
|
||||
RC_MEMSIZE_16_BITS,
|
||||
RC_MEMSIZE_24_BITS,
|
||||
RC_MEMSIZE_32_BITS
|
||||
RC_MEMSIZE_BITCOUNT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -114,8 +81,6 @@ typedef struct {
|
||||
unsigned address;
|
||||
/* The size of the variable. */
|
||||
char size;
|
||||
/* True if the value is in BCD. */
|
||||
char is_bcd;
|
||||
/* True if the reference will be used in indirection */
|
||||
char is_indirect;
|
||||
} rc_memref_t;
|
||||
@ -143,12 +108,14 @@ struct rc_memref_value_t {
|
||||
|
||||
/* types */
|
||||
enum {
|
||||
RC_OPERAND_ADDRESS, /* Compare to the value of a live address in RAM. */
|
||||
RC_OPERAND_DELTA, /* The value last known at this address. */
|
||||
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
|
||||
RC_OPERAND_FP, /* A floating point value. */
|
||||
RC_OPERAND_LUA, /* A Lua function that provides the value. */
|
||||
RC_OPERAND_PRIOR /* The last differing value at this address. */
|
||||
RC_OPERAND_ADDRESS, /* The value of a live address in RAM. */
|
||||
RC_OPERAND_DELTA, /* The value last known at this address. */
|
||||
RC_OPERAND_CONST, /* A 32-bit unsigned integer. */
|
||||
RC_OPERAND_FP, /* A floating point value. */
|
||||
RC_OPERAND_LUA, /* A Lua function that provides the value. */
|
||||
RC_OPERAND_PRIOR, /* The last differing value at this address. */
|
||||
RC_OPERAND_BCD, /* The BCD-decoded value of a live address in RAM */
|
||||
RC_OPERAND_INVERTED /* The twos-complement value of a live address in RAM */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -166,7 +133,11 @@ typedef struct {
|
||||
int luafunc;
|
||||
} value;
|
||||
|
||||
/* specifies which member of the value union is being used */
|
||||
char type;
|
||||
|
||||
/* the actual RC_MEMSIZE of the operand - memref.size may differ */
|
||||
char size;
|
||||
}
|
||||
rc_operand_t;
|
||||
|
||||
@ -184,18 +155,24 @@ enum {
|
||||
RC_CONDITION_ADD_HITS,
|
||||
RC_CONDITION_AND_NEXT,
|
||||
RC_CONDITION_MEASURED,
|
||||
RC_CONDITION_ADD_ADDRESS
|
||||
RC_CONDITION_ADD_ADDRESS,
|
||||
RC_CONDITION_OR_NEXT,
|
||||
RC_CONDITION_TRIGGER,
|
||||
RC_CONDITION_MEASURED_IF
|
||||
};
|
||||
|
||||
/* operators */
|
||||
enum {
|
||||
RC_CONDITION_EQ,
|
||||
RC_CONDITION_LT,
|
||||
RC_CONDITION_LE,
|
||||
RC_CONDITION_GT,
|
||||
RC_CONDITION_GE,
|
||||
RC_CONDITION_NE,
|
||||
RC_CONDITION_NONE
|
||||
RC_OPERATOR_EQ,
|
||||
RC_OPERATOR_LT,
|
||||
RC_OPERATOR_LE,
|
||||
RC_OPERATOR_GT,
|
||||
RC_OPERATOR_GE,
|
||||
RC_OPERATOR_NE,
|
||||
RC_OPERATOR_NONE,
|
||||
RC_OPERATOR_MULT,
|
||||
RC_OPERATOR_DIV,
|
||||
RC_OPERATOR_AND
|
||||
};
|
||||
|
||||
typedef struct rc_condition_t rc_condition_t;
|
||||
@ -256,7 +233,8 @@ enum {
|
||||
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
|
||||
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
|
||||
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
|
||||
RC_TRIGGER_STATE_TRIGGERED /* achievement has triggered */
|
||||
RC_TRIGGER_STATE_TRIGGERED, /* achievement has triggered */
|
||||
RC_TRIGGER_STATE_PRIMED /* all non-Trigger conditions are true */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -290,38 +268,10 @@ int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State*
|
||||
void rc_reset_trigger(rc_trigger_t* self);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Expressions and values |
|
||||
| Values |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_term_t rc_term_t;
|
||||
|
||||
struct rc_term_t {
|
||||
/* The next term in this chain. */
|
||||
rc_term_t* next;
|
||||
|
||||
/* The first operand. */
|
||||
rc_operand_t operand1;
|
||||
/* The second operand. */
|
||||
rc_operand_t operand2;
|
||||
|
||||
/* A value that is applied to the second variable to invert its bits. */
|
||||
unsigned invert;
|
||||
};
|
||||
|
||||
typedef struct rc_expression_t rc_expression_t;
|
||||
|
||||
struct rc_expression_t {
|
||||
/* The next expression in this chain. */
|
||||
rc_expression_t* next;
|
||||
|
||||
/* The list of terms in this expression. */
|
||||
rc_term_t* terms;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/* The list of expression to evaluate. */
|
||||
rc_expression_t* expressions;
|
||||
|
||||
/* The list of conditions to evaluate. */
|
||||
rc_condset_t* conditions;
|
||||
|
||||
@ -340,11 +290,12 @@ int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L)
|
||||
|
||||
/* Return values for rc_evaluate_lboard. */
|
||||
enum {
|
||||
RC_LBOARD_INACTIVE,
|
||||
RC_LBOARD_ACTIVE,
|
||||
RC_LBOARD_STARTED,
|
||||
RC_LBOARD_CANCELED,
|
||||
RC_LBOARD_TRIGGERED
|
||||
RC_LBOARD_STATE_INACTIVE, /* leaderboard is not being processed */
|
||||
RC_LBOARD_STATE_WAITING, /* leaderboard cannot activate until the start condition has been false for at least one frame */
|
||||
RC_LBOARD_STATE_ACTIVE, /* leaderboard is active and may start */
|
||||
RC_LBOARD_STATE_STARTED, /* leaderboard attempt in progress */
|
||||
RC_LBOARD_STATE_CANCELED, /* leaderboard attempt canceled */
|
||||
RC_LBOARD_STATE_TRIGGERED /* leaderboard attempt complete, value should be submitted */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -355,8 +306,7 @@ typedef struct {
|
||||
rc_value_t* progress;
|
||||
rc_memref_value_t* memrefs;
|
||||
|
||||
char started;
|
||||
char submitted;
|
||||
char state;
|
||||
}
|
||||
rc_lboard_t;
|
||||
|
||||
@ -433,6 +383,128 @@ int rc_richpresence_size(const char* script);
|
||||
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx);
|
||||
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Runtime |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_runtime_trigger_t {
|
||||
unsigned id;
|
||||
rc_trigger_t* trigger;
|
||||
void* buffer;
|
||||
unsigned char md5[16];
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_trigger_t;
|
||||
|
||||
typedef struct rc_runtime_lboard_t {
|
||||
unsigned id;
|
||||
int value;
|
||||
rc_lboard_t* lboard;
|
||||
void* buffer;
|
||||
unsigned char md5[16];
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_lboard_t;
|
||||
|
||||
typedef struct rc_runtime_richpresence_t {
|
||||
rc_richpresence_t* richpresence;
|
||||
void* buffer;
|
||||
struct rc_runtime_richpresence_t* previous;
|
||||
char owns_memrefs;
|
||||
}
|
||||
rc_runtime_richpresence_t;
|
||||
|
||||
typedef struct rc_runtime_t {
|
||||
rc_runtime_trigger_t* triggers;
|
||||
unsigned trigger_count;
|
||||
unsigned trigger_capacity;
|
||||
|
||||
rc_runtime_lboard_t* lboards;
|
||||
unsigned lboard_count;
|
||||
unsigned lboard_capacity;
|
||||
|
||||
rc_runtime_richpresence_t* richpresence;
|
||||
char* richpresence_display_buffer;
|
||||
char richpresence_update_timer;
|
||||
|
||||
rc_memref_value_t* memrefs;
|
||||
rc_memref_value_t** next_memref;
|
||||
}
|
||||
rc_runtime_t;
|
||||
|
||||
void rc_runtime_init(rc_runtime_t* runtime);
|
||||
void rc_runtime_destroy(rc_runtime_t* runtime);
|
||||
|
||||
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
|
||||
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
|
||||
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* runtime, unsigned id);
|
||||
|
||||
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
|
||||
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
|
||||
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* runtime, unsigned id);
|
||||
|
||||
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
|
||||
const char* rc_runtime_get_richpresence(const rc_runtime_t* runtime);
|
||||
|
||||
enum {
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_RESET,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED,
|
||||
RC_RUNTIME_EVENT_LBOARD_STARTED,
|
||||
RC_RUNTIME_EVENT_LBOARD_CANCELED,
|
||||
RC_RUNTIME_EVENT_LBOARD_UPDATED,
|
||||
RC_RUNTIME_EVENT_LBOARD_TRIGGERED
|
||||
};
|
||||
|
||||
typedef struct rc_runtime_event_t {
|
||||
unsigned id;
|
||||
int value;
|
||||
char type;
|
||||
}
|
||||
rc_runtime_event_t;
|
||||
|
||||
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
|
||||
|
||||
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L);
|
||||
void rc_runtime_reset(rc_runtime_t* runtime);
|
||||
|
||||
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
|
||||
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L);
|
||||
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char* serialized, lua_State* L);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Memory mapping |
|
||||
\*****************************************************************************/
|
||||
|
||||
enum {
|
||||
RC_MEMORY_TYPE_SYSTEM_RAM, /* normal system memory */
|
||||
RC_MEMORY_TYPE_SAVE_RAM, /* memory that persists between sessions */
|
||||
RC_MEMORY_TYPE_VIDEO_RAM, /* memory reserved for graphical processing */
|
||||
RC_MEMORY_TYPE_READONLY, /* memory that maps to read only data */
|
||||
RC_MEMORY_TYPE_HARDWARE_CONTROLLER, /* memory for interacting with system components */
|
||||
RC_MEMORY_TYPE_VIRTUAL_RAM, /* secondary address space that maps to real memory in system RAM */
|
||||
RC_MEMORY_TYPE_UNUSED /* these addresses don't really exist */
|
||||
};
|
||||
|
||||
typedef struct rc_memory_region_t {
|
||||
unsigned start_address; /* first address of block as queried by RetroAchievements */
|
||||
unsigned end_address; /* last address of block as queried by RetroAchievements */
|
||||
unsigned real_address; /* real address for first address of block */
|
||||
char type; /* RC_MEMORY_TYPE_ for block */
|
||||
const char* description; /* short description of block */
|
||||
}
|
||||
rc_memory_region_t;
|
||||
|
||||
typedef struct rc_memory_regions_t {
|
||||
const rc_memory_region_t* region;
|
||||
unsigned num_regions;
|
||||
}
|
||||
rc_memory_regions_t;
|
||||
|
||||
const rc_memory_regions_t* rc_console_memory_regions(int console_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
79
deps/rcheevos/include/rconsoles.h
vendored
Normal file
79
deps/rcheevos/include/rconsoles.h
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef RCONSOLES_H
|
||||
#define RCONSOLES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*****************************************************************************\
|
||||
| Console identifiers |
|
||||
\*****************************************************************************/
|
||||
|
||||
enum {
|
||||
RC_CONSOLE_MEGA_DRIVE = 1,
|
||||
RC_CONSOLE_NINTENDO_64 = 2,
|
||||
RC_CONSOLE_SUPER_NINTENDO = 3,
|
||||
RC_CONSOLE_GAMEBOY = 4,
|
||||
RC_CONSOLE_GAMEBOY_ADVANCE = 5,
|
||||
RC_CONSOLE_GAMEBOY_COLOR = 6,
|
||||
RC_CONSOLE_NINTENDO = 7,
|
||||
RC_CONSOLE_PC_ENGINE = 8,
|
||||
RC_CONSOLE_SEGA_CD = 9,
|
||||
RC_CONSOLE_SEGA_32X = 10,
|
||||
RC_CONSOLE_MASTER_SYSTEM = 11,
|
||||
RC_CONSOLE_PLAYSTATION = 12,
|
||||
RC_CONSOLE_ATARI_LYNX = 13,
|
||||
RC_CONSOLE_NEOGEO_POCKET = 14,
|
||||
RC_CONSOLE_GAME_GEAR = 15,
|
||||
RC_CONSOLE_GAMECUBE = 16,
|
||||
RC_CONSOLE_ATARI_JAGUAR = 17,
|
||||
RC_CONSOLE_NINTENDO_DS = 18,
|
||||
RC_CONSOLE_WII = 19,
|
||||
RC_CONSOLE_WII_U = 20,
|
||||
RC_CONSOLE_PLAYSTATION_2 = 21,
|
||||
RC_CONSOLE_XBOX = 22,
|
||||
/* 23 used to be EVENTS */
|
||||
RC_CONSOLE_POKEMON_MINI = 24,
|
||||
RC_CONSOLE_ATARI_2600 = 25,
|
||||
RC_CONSOLE_MS_DOS = 26,
|
||||
RC_CONSOLE_ARCADE = 27,
|
||||
RC_CONSOLE_VIRTUAL_BOY = 28,
|
||||
RC_CONSOLE_MSX = 29,
|
||||
RC_CONSOLE_COMMODORE_64 = 30,
|
||||
RC_CONSOLE_ZX81 = 31,
|
||||
RC_CONSOLE_ORIC = 32,
|
||||
RC_CONSOLE_SG1000 = 33,
|
||||
RC_CONSOLE_VIC20 = 34,
|
||||
RC_CONSOLE_AMIGA = 35,
|
||||
RC_CONSOLE_AMIGA_ST = 36,
|
||||
RC_CONSOLE_AMSTRAD_PC = 37,
|
||||
RC_CONSOLE_APPLE_II = 38,
|
||||
RC_CONSOLE_SATURN = 39,
|
||||
RC_CONSOLE_DREAMCAST = 40,
|
||||
RC_CONSOLE_PSP = 41,
|
||||
RC_CONSOLE_CDI = 42,
|
||||
RC_CONSOLE_3DO = 43,
|
||||
RC_CONSOLE_COLECOVISION = 44,
|
||||
RC_CONSOLE_INTELLIVISION = 45,
|
||||
RC_CONSOLE_VECTREX = 46,
|
||||
RC_CONSOLE_PC8800 = 47,
|
||||
RC_CONSOLE_PC9800 = 48,
|
||||
RC_CONSOLE_PCFX = 49,
|
||||
RC_CONSOLE_ATARI_5200 = 50,
|
||||
RC_CONSOLE_ATARI_7800 = 51,
|
||||
RC_CONSOLE_X68K = 52,
|
||||
RC_CONSOLE_WONDERSWAN = 53,
|
||||
RC_CONSOLE_CASSETTEVISION = 54,
|
||||
RC_CONSOLE_SUPER_CASSETTEVISION = 55,
|
||||
|
||||
RC_CONSOLE_HUBS = 100,
|
||||
RC_CONSOLE_EVENTS = 101
|
||||
};
|
||||
|
||||
const char* rc_console_name(int console_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RCONSOLES_H */
|
124
deps/rcheevos/include/rhash.h
vendored
Normal file
124
deps/rcheevos/include/rhash.h
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef RHASH_H
|
||||
#define RHASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rconsoles.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/* generates a hash from a block of memory.
|
||||
* returns non-zero on success, or zero on failure.
|
||||
*/
|
||||
int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer, size_t buffer_size);
|
||||
|
||||
/* generates a hash from a file.
|
||||
* returns non-zero on success, or zero on failure.
|
||||
*/
|
||||
int rc_hash_generate_from_file(char hash[33], int console_id, const char* path);
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/* data for rc_hash_iterate
|
||||
*/
|
||||
struct rc_hash_iterator
|
||||
{
|
||||
uint8_t* buffer;
|
||||
size_t buffer_size;
|
||||
uint8_t consoles[12];
|
||||
int index;
|
||||
const char* path;
|
||||
};
|
||||
|
||||
/* initializes a rc_hash_iterator
|
||||
* - path must be provided
|
||||
* - if buffer and buffer_size are provided, path may be a filename (i.e. for something extracted from a zip file)
|
||||
*/
|
||||
void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, uint8_t* buffer, size_t buffer_size);
|
||||
|
||||
/* releases resources associated to a rc_hash_iterator
|
||||
*/
|
||||
void rc_hash_destroy_iterator(struct rc_hash_iterator* iterator);
|
||||
|
||||
/* generates the next hash for the data in the rc_hash_iterator.
|
||||
* returns non-zero if a hash was generated, or zero if no more hashes can be generated for the data.
|
||||
*/
|
||||
int rc_hash_iterate(char hash[33], struct rc_hash_iterator* iterator);
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/* specifies a function to call when an error occurs to display the error message */
|
||||
typedef void (*rc_hash_message_callback)(const char*);
|
||||
void rc_hash_init_error_message_callback(rc_hash_message_callback callback);
|
||||
|
||||
/* specifies a function to call for verbose logging */
|
||||
void rc_hash_init_verbose_message_callback(rc_hash_message_callback callback);
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/* opens a file */
|
||||
typedef void* (*rc_hash_filereader_open_file_handler)(const char* path_utf8);
|
||||
|
||||
/* moves the file pointer - standard fseek parameters */
|
||||
typedef void (*rc_hash_filereader_seek_handler)(void* file_handle, size_t offset, int origin);
|
||||
|
||||
/* locates the file pointer */
|
||||
typedef size_t (*rc_hash_filereader_tell_handler)(void* file_handle);
|
||||
|
||||
/* reads the specified number of bytes from the file starting at the read pointer.
|
||||
* returns the number of bytes actually read.
|
||||
*/
|
||||
typedef size_t (*rc_hash_filereader_read_handler)(void* file_handle, void* buffer, size_t requested_bytes);
|
||||
|
||||
/* closes the file */
|
||||
typedef void (*rc_hash_filereader_close_file_handler)(void* file_handle);
|
||||
|
||||
struct rc_hash_filereader
|
||||
{
|
||||
rc_hash_filereader_open_file_handler open;
|
||||
rc_hash_filereader_seek_handler seek;
|
||||
rc_hash_filereader_tell_handler tell;
|
||||
rc_hash_filereader_read_handler read;
|
||||
rc_hash_filereader_close_file_handler close;
|
||||
};
|
||||
|
||||
void rc_hash_init_custom_filereader(struct rc_hash_filereader* reader);
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/* opens a track from the specified file. track 0 indicates the first data track should be opened.
|
||||
* returns a handle to be passed to the other functions, or NULL if the track could not be opened.
|
||||
*/
|
||||
typedef void* (*rc_hash_cdreader_open_track_handler)(const char* path, uint32_t track);
|
||||
|
||||
/* attempts to read the specified number of bytes from the file starting at the read pointer.
|
||||
* returns the number of bytes actually read.
|
||||
*/
|
||||
typedef size_t (*rc_hash_cdreader_read_sector_handler)(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes);
|
||||
|
||||
/* closes the track handle */
|
||||
typedef void (*rc_hash_cdreader_close_track_handler)(void* track_handle);
|
||||
|
||||
struct rc_hash_cdreader
|
||||
{
|
||||
rc_hash_cdreader_open_track_handler open_track;
|
||||
rc_hash_cdreader_read_sector_handler read_sector;
|
||||
rc_hash_cdreader_close_track_handler close_track;
|
||||
};
|
||||
|
||||
void rc_hash_init_default_cdreader();
|
||||
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RHASH_H */
|
9
deps/rcheevos/include/rurl.h
vendored
9
deps/rcheevos/include/rurl.h
vendored
@ -7,11 +7,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore);
|
||||
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore, const char* game_hash);
|
||||
|
||||
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value, const char* game_hash);
|
||||
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value);
|
||||
|
||||
int rc_url_get_gameid(char* buffer, size_t size, unsigned char hash[16]);
|
||||
int rc_url_get_gameid(char* buffer, size_t size, const char* hash);
|
||||
|
||||
int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
|
||||
|
||||
@ -25,6 +25,9 @@ int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, con
|
||||
|
||||
int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
|
||||
|
||||
int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
|
||||
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
34
deps/rcheevos/src/rcheevos/alloc.c
vendored
34
deps/rcheevos/src/rcheevos/alloc.c
vendored
@ -52,3 +52,37 @@ void rc_destroy_parse_state(rc_parse_state_t* parse)
|
||||
if (parse->scratch.memref != parse->scratch.memref_buffer)
|
||||
free(parse->scratch.memref);
|
||||
}
|
||||
|
||||
const char* rc_error_str(int ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case RC_OK: return "OK";
|
||||
case RC_INVALID_LUA_OPERAND: return "Invalid Lua operand";
|
||||
case RC_INVALID_MEMORY_OPERAND: return "Invalid memory operand";
|
||||
case RC_INVALID_CONST_OPERAND: return "Invalid constant operand";
|
||||
case RC_INVALID_FP_OPERAND: return "Invalid floating-point operand";
|
||||
case RC_INVALID_CONDITION_TYPE: return "Invalid condition type";
|
||||
case RC_INVALID_OPERATOR: return "Invalid operator";
|
||||
case RC_INVALID_REQUIRED_HITS: return "Invalid required hits";
|
||||
case RC_DUPLICATED_START: return "Duplicated start condition";
|
||||
case RC_DUPLICATED_CANCEL: return "Duplicated cancel condition";
|
||||
case RC_DUPLICATED_SUBMIT: return "Duplicated submit condition";
|
||||
case RC_DUPLICATED_VALUE: return "Duplicated value expression";
|
||||
case RC_DUPLICATED_PROGRESS: return "Duplicated progress expression";
|
||||
case RC_MISSING_START: return "Missing start condition";
|
||||
case RC_MISSING_CANCEL: return "Missing cancel condition";
|
||||
case RC_MISSING_SUBMIT: return "Missing submit condition";
|
||||
case RC_MISSING_VALUE: return "Missing value expression";
|
||||
case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
|
||||
case RC_MISSING_DISPLAY_STRING: return "Missing display string";
|
||||
case RC_OUT_OF_MEMORY: return "Out of memory";
|
||||
case RC_INVALID_VALUE_FLAG: return "Invalid flag in value expression";
|
||||
case RC_MISSING_VALUE_MEASURED: return "Missing measured flag in value expression";
|
||||
case RC_MULTIPLE_MEASURED: return "Multiple measured targets";
|
||||
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
|
||||
case RC_INVALID_COMPARISON: return "Invalid comparison";
|
||||
case RC_INVALID_STATE: return "Invalid state";
|
||||
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
44
deps/rcheevos/src/rcheevos/compat.c
vendored
Normal file
44
deps/rcheevos/src/rcheevos/compat.c
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
#include "compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
int rc_strncasecmp(const char* left, const char* right, size_t length)
|
||||
{
|
||||
while (length)
|
||||
{
|
||||
if (*left != *right)
|
||||
{
|
||||
const int diff = tolower(*left) - tolower(*right);
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
}
|
||||
|
||||
++left;
|
||||
++right;
|
||||
--length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* rc_strdup(const char* str)
|
||||
{
|
||||
const size_t length = strlen(str);
|
||||
char* buffer = (char*)malloc(length + 1);
|
||||
memcpy(buffer, str, length + 1);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int rc_snprintf(char* buffer, size_t size, const char* format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
/* assume buffer is large enough and ignore size */
|
||||
result = vsprintf(buffer, format, args);
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
55
deps/rcheevos/src/rcheevos/compat.h
vendored
Normal file
55
deps/rcheevos/src/rcheevos/compat.h
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef RC_COMPAT_H
|
||||
#define RC_COMPAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(MINGW) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||
|
||||
/* MinGW redefinitions */
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
/* Visual Studio redefinitions */
|
||||
|
||||
#ifndef strcasecmp
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
#ifndef strncasecmp
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
#ifndef strdup
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
#elif __STDC_VERSION__ < 199901L
|
||||
|
||||
/* C89 redefinitions */
|
||||
|
||||
#ifndef snprintf
|
||||
extern int rc_snprintf(char* buffer, size_t size, const char* format, ...);
|
||||
#define snprintf rc_snprintf
|
||||
#endif
|
||||
|
||||
#ifndef strncasecmp
|
||||
extern int rc_strncasecmp(const char* left, const char* right, size_t length);
|
||||
#define strncasecmp rc_strncasecmp
|
||||
#endif
|
||||
|
||||
#ifndef strdup
|
||||
extern char* rc_strdup(const char* str);
|
||||
#define strdup rc_strdup
|
||||
#endif
|
||||
|
||||
#endif /* __STDC_VERSION__ < 199901L */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RC_COMPAT_H */
|
122
deps/rcheevos/src/rcheevos/condition.c
vendored
122
deps/rcheevos/src/rcheevos/condition.c
vendored
@ -6,6 +6,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
rc_condition_t* self;
|
||||
const char* aux;
|
||||
int ret2;
|
||||
int can_modify = 0;
|
||||
|
||||
aux = *memaddr;
|
||||
self = RC_ALLOC(rc_condition_t, parse);
|
||||
@ -15,12 +16,15 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
switch (*aux) {
|
||||
case 'p': case 'P': self->type = RC_CONDITION_PAUSE_IF; break;
|
||||
case 'r': case 'R': self->type = RC_CONDITION_RESET_IF; break;
|
||||
case 'a': case 'A': self->type = RC_CONDITION_ADD_SOURCE; break;
|
||||
case 'b': case 'B': self->type = RC_CONDITION_SUB_SOURCE; break;
|
||||
case 'a': case 'A': self->type = RC_CONDITION_ADD_SOURCE; can_modify = 1; break;
|
||||
case 'b': case 'B': self->type = RC_CONDITION_SUB_SOURCE; can_modify = 1; break;
|
||||
case 'c': case 'C': self->type = RC_CONDITION_ADD_HITS; break;
|
||||
case 'n': case 'N': self->type = RC_CONDITION_AND_NEXT; break;
|
||||
case 'o': case 'O': self->type = RC_CONDITION_OR_NEXT; break;
|
||||
case 'm': case 'M': self->type = RC_CONDITION_MEASURED; break;
|
||||
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; break;
|
||||
case 'q': case 'Q': self->type = RC_CONDITION_MEASURED_IF; break;
|
||||
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; can_modify = 1; break;
|
||||
case 't': case 'T': self->type = RC_CONDITION_TRIGGER; break;
|
||||
default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0;
|
||||
}
|
||||
|
||||
@ -37,9 +41,14 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->operand1.type == RC_OPERAND_FP) {
|
||||
parse->offset = can_modify ? RC_INVALID_FP_OPERAND : RC_INVALID_COMPARISON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (*aux++) {
|
||||
case '=':
|
||||
self->oper = RC_CONDITION_EQ;
|
||||
self->oper = RC_OPERATOR_EQ;
|
||||
aux += *aux == '=';
|
||||
break;
|
||||
|
||||
@ -51,33 +60,45 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
self->oper = RC_CONDITION_NE;
|
||||
self->oper = RC_OPERATOR_NE;
|
||||
break;
|
||||
|
||||
case '<':
|
||||
self->oper = RC_CONDITION_LT;
|
||||
self->oper = RC_OPERATOR_LT;
|
||||
|
||||
if (*aux == '=') {
|
||||
self->oper = RC_CONDITION_LE;
|
||||
self->oper = RC_OPERATOR_LE;
|
||||
aux++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '>':
|
||||
self->oper = RC_CONDITION_GT;
|
||||
self->oper = RC_OPERATOR_GT;
|
||||
|
||||
if (*aux == '=') {
|
||||
self->oper = RC_CONDITION_GE;
|
||||
self->oper = RC_OPERATOR_GE;
|
||||
aux++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '*':
|
||||
self->oper = RC_OPERATOR_MULT;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
self->oper = RC_OPERATOR_DIV;
|
||||
break;
|
||||
|
||||
case '&':
|
||||
self->oper = RC_OPERATOR_AND;
|
||||
break;
|
||||
|
||||
case '_':
|
||||
case ')':
|
||||
case '\0':
|
||||
self->oper = RC_CONDITION_NONE;
|
||||
self->oper = RC_OPERATOR_NONE;
|
||||
self->operand2.type = RC_OPERAND_CONST;
|
||||
self->operand2.value.num = 1;
|
||||
self->required_hits = 0;
|
||||
@ -85,6 +106,36 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
return self;
|
||||
}
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_OPERATOR_MULT:
|
||||
case RC_OPERATOR_DIV:
|
||||
case RC_OPERATOR_AND:
|
||||
/* modifying operators are only valid on modifying statements */
|
||||
if (!can_modify) {
|
||||
parse->offset = RC_INVALID_OPERATOR;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
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:
|
||||
/* 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;
|
||||
}
|
||||
|
||||
ret2 = rc_parse_operand(&self->operand2, &aux, 1, is_indirect, parse);
|
||||
|
||||
if (ret2 < 0) {
|
||||
@ -92,6 +143,17 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->oper == RC_OPERATOR_NONE) {
|
||||
/* if operator is none, explicitly clear out the right side */
|
||||
self->operand2.type = RC_INVALID_CONST_OPERAND;
|
||||
self->operand2.value.num = 0;
|
||||
}
|
||||
|
||||
if (!can_modify && self->operand2.type == RC_OPERAND_FP) {
|
||||
parse->offset = RC_INVALID_COMPARISON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*aux == '(') {
|
||||
char* end;
|
||||
self->required_hits = (unsigned)strtoul(++aux, &end, 10);
|
||||
@ -127,13 +189,39 @@ int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state) {
|
||||
unsigned value2 = rc_evaluate_operand(&self->operand2, eval_state);
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_CONDITION_EQ: return value1 == value2;
|
||||
case RC_CONDITION_NE: return value1 != value2;
|
||||
case RC_CONDITION_LT: return value1 < value2;
|
||||
case RC_CONDITION_LE: return value1 <= value2;
|
||||
case RC_CONDITION_GT: return value1 > value2;
|
||||
case RC_CONDITION_GE: return value1 >= value2;
|
||||
case RC_CONDITION_NONE: return 1;
|
||||
case RC_OPERATOR_EQ: return value1 == value2;
|
||||
case RC_OPERATOR_NE: return value1 != value2;
|
||||
case RC_OPERATOR_LT: return value1 < value2;
|
||||
case RC_OPERATOR_LE: return value1 <= value2;
|
||||
case RC_OPERATOR_GT: return value1 > value2;
|
||||
case RC_OPERATOR_GE: return value1 >= value2;
|
||||
case RC_OPERATOR_NONE: return 1;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state) {
|
||||
unsigned value = rc_evaluate_operand(&self->operand1, eval_state);
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_OPERATOR_MULT:
|
||||
if (self->operand2.type == RC_OPERAND_FP)
|
||||
value = (int)((double)value * self->operand2.value.dbl);
|
||||
else
|
||||
value *= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_DIV:
|
||||
if (self->operand2.type == RC_OPERAND_FP)
|
||||
value = (int)((double)value / self->operand2.value.dbl);
|
||||
else
|
||||
value /= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_AND:
|
||||
value &= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
183
deps/rcheevos/src/rcheevos/condset.c
vendored
183
deps/rcheevos/src/rcheevos/condset.c
vendored
@ -9,15 +9,16 @@ static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause)
|
||||
case RC_CONDITION_PAUSE_IF:
|
||||
*in_pause = condition->pause = 1;
|
||||
break;
|
||||
|
||||
|
||||
case RC_CONDITION_ADD_SOURCE:
|
||||
case RC_CONDITION_SUB_SOURCE:
|
||||
case RC_CONDITION_ADD_HITS:
|
||||
case RC_CONDITION_AND_NEXT:
|
||||
case RC_CONDITION_OR_NEXT:
|
||||
case RC_CONDITION_ADD_ADDRESS:
|
||||
condition->pause = *in_pause;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
*in_pause = condition->pause = 0;
|
||||
break;
|
||||
@ -48,14 +49,15 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((*next)->oper == RC_CONDITION_NONE) {
|
||||
if ((*next)->oper == RC_OPERATOR_NONE) {
|
||||
switch ((*next)->type) {
|
||||
case RC_CONDITION_ADD_ADDRESS:
|
||||
case RC_CONDITION_ADD_HITS:
|
||||
case RC_CONDITION_ADD_SOURCE:
|
||||
case RC_CONDITION_SUB_SOURCE:
|
||||
case RC_CONDITION_AND_NEXT:
|
||||
break;
|
||||
case RC_CONDITION_OR_NEXT:
|
||||
break;
|
||||
|
||||
default:
|
||||
parse->offset = RC_INVALID_OPERATOR;
|
||||
@ -110,10 +112,14 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
|
||||
|
||||
static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc_eval_state_t* eval_state) {
|
||||
rc_condition_t* condition;
|
||||
int set_valid, cond_valid, prev_cond;
|
||||
int set_valid, cond_valid, and_next, or_next;
|
||||
unsigned measured_value = 0;
|
||||
int can_measure = 1, measured_from_hits = 0;
|
||||
|
||||
eval_state->primed = 1;
|
||||
set_valid = 1;
|
||||
prev_cond = 1;
|
||||
and_next = 1;
|
||||
or_next = 0;
|
||||
eval_state->add_value = eval_state->add_hits = eval_state->add_address = 0;
|
||||
|
||||
for (condition = self->conditions; condition != 0; condition = condition->next) {
|
||||
@ -121,93 +127,107 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
continue;
|
||||
}
|
||||
|
||||
/* STEP 1: process modifier conditions */
|
||||
switch (condition->type) {
|
||||
case RC_CONDITION_ADD_SOURCE:
|
||||
eval_state->add_value += rc_evaluate_operand(&condition->operand1, eval_state);
|
||||
eval_state->add_value += rc_evaluate_condition_value(condition, eval_state);
|
||||
eval_state->add_address = 0;
|
||||
continue;
|
||||
|
||||
|
||||
case RC_CONDITION_SUB_SOURCE:
|
||||
eval_state->add_value -= rc_evaluate_operand(&condition->operand1, eval_state);
|
||||
eval_state->add_address = 0;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_ADD_HITS:
|
||||
/* always evaluate the condition to ensure everything is updated correctly */
|
||||
cond_valid = rc_test_condition(condition, eval_state);
|
||||
|
||||
/* merge AndNext value and reset it for the next condition */
|
||||
cond_valid &= prev_cond;
|
||||
prev_cond = 1;
|
||||
|
||||
/* if the condition is true, tally it */
|
||||
if (cond_valid) {
|
||||
if (condition->required_hits == 0 || condition->current_hits < condition->required_hits) {
|
||||
condition->current_hits++;
|
||||
}
|
||||
|
||||
condition->is_true = (condition->required_hits == 0 || condition->current_hits >= condition->required_hits);
|
||||
}
|
||||
else {
|
||||
condition->is_true = 0;
|
||||
}
|
||||
|
||||
eval_state->add_value = 0;
|
||||
eval_state->add_address = 0;
|
||||
eval_state->add_hits += condition->current_hits;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_AND_NEXT:
|
||||
prev_cond &= rc_test_condition(condition, eval_state);
|
||||
eval_state->add_value = 0;
|
||||
eval_state->add_value -= rc_evaluate_condition_value(condition, eval_state);
|
||||
eval_state->add_address = 0;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_ADD_ADDRESS:
|
||||
eval_state->add_address = rc_evaluate_operand(&condition->operand1, eval_state);
|
||||
eval_state->add_address = rc_evaluate_condition_value(condition, eval_state);
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_MEASURED:
|
||||
if (condition->required_hits == 0) {
|
||||
/* Measured condition without a hit target measures the value of the left operand */
|
||||
measured_value = rc_evaluate_condition_value(condition, eval_state) + eval_state->add_value;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* always evaluate the condition to ensure everything is updated correctly */
|
||||
cond_valid = rc_test_condition(condition, eval_state);
|
||||
/* STEP 2: evaluate the current condition */
|
||||
condition->is_true = rc_test_condition(condition, eval_state);
|
||||
eval_state->add_value = 0;
|
||||
eval_state->add_address = 0;
|
||||
|
||||
/* merge AndNext value and reset it for the next condition */
|
||||
cond_valid &= prev_cond;
|
||||
prev_cond = 1;
|
||||
/* apply logic flags and reset them for the next condition */
|
||||
cond_valid = condition->is_true;
|
||||
cond_valid &= and_next;
|
||||
cond_valid |= or_next;
|
||||
and_next = 1;
|
||||
or_next = 0;
|
||||
|
||||
/* if the condition has a target hit count that has already been met, it's automatically true, even if not currently true. */
|
||||
if (condition->required_hits != 0 && (condition->current_hits + eval_state->add_hits) >= condition->required_hits) {
|
||||
cond_valid = 1;
|
||||
}
|
||||
else if (cond_valid) {
|
||||
condition->current_hits++;
|
||||
/* true conditions should update hit count */
|
||||
if (cond_valid) {
|
||||
eval_state->has_hits = 1;
|
||||
|
||||
if (condition->required_hits == 0) {
|
||||
/* not a hit-based requirement: ignore any additional logic! */
|
||||
/* no target hit count, just keep tallying */
|
||||
++condition->current_hits;
|
||||
}
|
||||
else if ((condition->current_hits + eval_state->add_hits) < condition->required_hits) {
|
||||
/* HitCount target has not yet been met, condition is not yet valid */
|
||||
cond_valid = 0;
|
||||
else if (condition->current_hits < condition->required_hits) {
|
||||
/* target hit count hasn't been met, tally and revalidate - only true if hit count becomes met */
|
||||
++condition->current_hits;
|
||||
cond_valid = (condition->current_hits == condition->required_hits);
|
||||
}
|
||||
else {
|
||||
/* target hit count has been met, do nothing */
|
||||
}
|
||||
}
|
||||
condition->is_true = cond_valid;
|
||||
eval_state->has_hits |= (condition->current_hits || eval_state->add_hits);
|
||||
else if (condition->current_hits > 0) {
|
||||
/* target has been true in the past, if the hit target is met, consider it true now */
|
||||
eval_state->has_hits = 1;
|
||||
cond_valid = (condition->current_hits == condition->required_hits);
|
||||
}
|
||||
|
||||
/* capture measured state */
|
||||
if (condition->type == RC_CONDITION_MEASURED) {
|
||||
unsigned int measured_value;
|
||||
if (condition->required_hits > 0)
|
||||
/* STEP 3: handle logic flags */
|
||||
switch (condition->type) {
|
||||
case RC_CONDITION_ADD_HITS:
|
||||
eval_state->add_hits += condition->current_hits;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_AND_NEXT:
|
||||
and_next = cond_valid;
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_OR_NEXT:
|
||||
or_next = cond_valid;
|
||||
continue;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (eval_state->add_hits) {
|
||||
if (condition->required_hits != 0) {
|
||||
/* if the condition has a target hit count, we have to recalculate cond_valid including the AddHits counter */
|
||||
measured_from_hits = 1;
|
||||
measured_value = condition->current_hits + eval_state->add_hits;
|
||||
else
|
||||
measured_value = rc_evaluate_operand(&condition->operand1, eval_state) + eval_state->add_value;
|
||||
cond_valid = (measured_value >= condition->required_hits);
|
||||
}
|
||||
else {
|
||||
/* no target hit count. we can't tell if the add_hits value is from this frame or not, so ignore it.
|
||||
complex condition will only be true if the current condition is true */
|
||||
}
|
||||
|
||||
if (measured_value > eval_state->measured_value)
|
||||
eval_state->measured_value = measured_value;
|
||||
eval_state->add_hits = 0;
|
||||
}
|
||||
else if (condition->required_hits != 0) {
|
||||
/* if there's a hit target, capture the current hits for recording Measured value later */
|
||||
measured_from_hits = 1;
|
||||
measured_value = condition->current_hits;
|
||||
}
|
||||
|
||||
/* reset AddHits and AddSource/SubSource values */
|
||||
eval_state->add_value = eval_state->add_hits = eval_state->add_address = 0;
|
||||
|
||||
/* STEP 4: handle special flags */
|
||||
switch (condition->type) {
|
||||
case RC_CONDITION_PAUSE_IF:
|
||||
/* as soon as we find a PauseIf that evaluates to true, stop processing the rest of the group */
|
||||
@ -227,20 +247,38 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
/* PauseIf has a HitCount that hasn't been met, ignore it for now. */
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_RESET_IF:
|
||||
if (cond_valid) {
|
||||
eval_state->was_reset = 1; /* let caller know to reset all hit counts */
|
||||
set_valid = 0; /* cannot be valid if we've hit a reset condition */
|
||||
}
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_MEASURED_IF:
|
||||
if (!cond_valid)
|
||||
can_measure = 0;
|
||||
break;
|
||||
|
||||
case RC_CONDITION_TRIGGER:
|
||||
/* update truthiness of set, but do not update truthiness of primed state */
|
||||
set_valid &= cond_valid;
|
||||
continue;
|
||||
|
||||
default:
|
||||
set_valid &= cond_valid;
|
||||
break;
|
||||
}
|
||||
|
||||
/* STEP 5: update overall truthiness of set and primed state */
|
||||
eval_state->primed &= cond_valid;
|
||||
set_valid &= cond_valid;
|
||||
}
|
||||
|
||||
/* if not suppressed, update the measured value */
|
||||
if (measured_value > eval_state->measured_value && can_measure) {
|
||||
eval_state->measured_value = measured_value;
|
||||
eval_state->measured_from_hits = measured_from_hits;
|
||||
}
|
||||
|
||||
return set_valid;
|
||||
@ -255,6 +293,7 @@ int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state) {
|
||||
if (self->has_pause) {
|
||||
if ((self->is_paused = rc_test_condset_internal(self, 1, eval_state))) {
|
||||
/* one or more Pause conditions exists, if any of them are true, stop processing this group */
|
||||
eval_state->primed = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
573
deps/rcheevos/src/rcheevos/consoleinfo.c
vendored
Normal file
573
deps/rcheevos/src/rcheevos/consoleinfo.c
vendored
Normal file
@ -0,0 +1,573 @@
|
||||
#include "rcheevos.h"
|
||||
#include "rconsoles.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
const char* rc_console_name(int console_id)
|
||||
{
|
||||
switch (console_id)
|
||||
{
|
||||
case RC_CONSOLE_3DO:
|
||||
return "3DO";
|
||||
|
||||
case RC_CONSOLE_AMIGA:
|
||||
return "Amiga";
|
||||
|
||||
case RC_CONSOLE_AMIGA_ST:
|
||||
return "Amiga ST";
|
||||
|
||||
case RC_CONSOLE_AMSTRAD_PC:
|
||||
return "Amstrad CPC";
|
||||
|
||||
case RC_CONSOLE_APPLE_II:
|
||||
return "Apple II";
|
||||
|
||||
case RC_CONSOLE_ARCADE:
|
||||
return "Arcade";
|
||||
|
||||
case RC_CONSOLE_ATARI_2600:
|
||||
return "Atari 2600";
|
||||
|
||||
case RC_CONSOLE_ATARI_5200:
|
||||
return "Atari 5200";
|
||||
|
||||
case RC_CONSOLE_ATARI_7800:
|
||||
return "Atari 7800";
|
||||
|
||||
case RC_CONSOLE_ATARI_JAGUAR:
|
||||
return "Atari Jaguar";
|
||||
|
||||
case RC_CONSOLE_ATARI_LYNX:
|
||||
return "Atari Lynx";
|
||||
|
||||
case RC_CONSOLE_CASSETTEVISION:
|
||||
return "CassetteVision";
|
||||
|
||||
case RC_CONSOLE_CDI:
|
||||
return "CD-I";
|
||||
|
||||
case RC_CONSOLE_COLECOVISION:
|
||||
return "ColecoVision";
|
||||
|
||||
case RC_CONSOLE_COMMODORE_64:
|
||||
return "Commodore 64";
|
||||
|
||||
case RC_CONSOLE_DREAMCAST:
|
||||
return "Dreamcast";
|
||||
|
||||
case RC_CONSOLE_EVENTS:
|
||||
return "Events";
|
||||
|
||||
case RC_CONSOLE_GAMEBOY:
|
||||
return "GameBoy";
|
||||
|
||||
case RC_CONSOLE_GAMEBOY_ADVANCE:
|
||||
return "GameBoy Advance";
|
||||
|
||||
case RC_CONSOLE_GAMEBOY_COLOR:
|
||||
return "GameBoy Color";
|
||||
|
||||
case RC_CONSOLE_GAMECUBE:
|
||||
return "GameCube";
|
||||
|
||||
case RC_CONSOLE_GAME_GEAR:
|
||||
return "Game Gear";
|
||||
|
||||
case RC_CONSOLE_HUBS:
|
||||
return "Hubs";
|
||||
|
||||
case RC_CONSOLE_INTELLIVISION:
|
||||
return "Intellivision";
|
||||
|
||||
case RC_CONSOLE_MASTER_SYSTEM:
|
||||
return "Master System";
|
||||
|
||||
case RC_CONSOLE_MEGA_DRIVE:
|
||||
return "Sega Genesis";
|
||||
|
||||
case RC_CONSOLE_MS_DOS:
|
||||
return "MS-DOS";
|
||||
|
||||
case RC_CONSOLE_MSX:
|
||||
return "MSX";
|
||||
|
||||
case RC_CONSOLE_NINTENDO:
|
||||
return "Nintendo Entertainment System";
|
||||
|
||||
case RC_CONSOLE_NINTENDO_64:
|
||||
return "Nintendo 64";
|
||||
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
return "Nintendo DS";
|
||||
|
||||
case RC_CONSOLE_NEOGEO_POCKET:
|
||||
return "Neo Geo Pocket";
|
||||
|
||||
case RC_CONSOLE_ORIC:
|
||||
return "Oric";
|
||||
|
||||
case RC_CONSOLE_PC8800:
|
||||
return "PC-8000/8800";
|
||||
|
||||
case RC_CONSOLE_PC9800:
|
||||
return "PC-9800";
|
||||
|
||||
case RC_CONSOLE_PCFX:
|
||||
return "PCFX";
|
||||
|
||||
case RC_CONSOLE_PC_ENGINE:
|
||||
return "PCEngine";
|
||||
|
||||
case RC_CONSOLE_PLAYSTATION:
|
||||
return "PlayStation";
|
||||
|
||||
case RC_CONSOLE_PLAYSTATION_2:
|
||||
return "PlayStation 2";
|
||||
|
||||
case RC_CONSOLE_PSP:
|
||||
return "PlayStation Portable";
|
||||
|
||||
case RC_CONSOLE_POKEMON_MINI:
|
||||
return "Pokemon Mini";
|
||||
|
||||
case RC_CONSOLE_SEGA_32X:
|
||||
return "Sega 32X";
|
||||
|
||||
case RC_CONSOLE_SEGA_CD:
|
||||
return "Sega CD";
|
||||
|
||||
case RC_CONSOLE_SATURN:
|
||||
return "Sega Saturn";
|
||||
|
||||
case RC_CONSOLE_SG1000:
|
||||
return "SG-1000";
|
||||
|
||||
case RC_CONSOLE_SUPER_NINTENDO:
|
||||
return "Super Nintendo Entertainment System";
|
||||
|
||||
case RC_CONSOLE_SUPER_CASSETTEVISION:
|
||||
return "Super CassetteVision";
|
||||
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return "WonderSwan";
|
||||
|
||||
case RC_CONSOLE_VECTREX:
|
||||
return "Vectrex";
|
||||
|
||||
case RC_CONSOLE_VIC20:
|
||||
return "VIC-20";
|
||||
|
||||
case RC_CONSOLE_VIRTUAL_BOY:
|
||||
return "Virtual Boy";
|
||||
|
||||
case RC_CONSOLE_WII:
|
||||
return "Wii";
|
||||
|
||||
case RC_CONSOLE_WII_U:
|
||||
return "Wii-U";
|
||||
|
||||
case RC_CONSOLE_X68K:
|
||||
return "X68K";
|
||||
|
||||
case RC_CONSOLE_XBOX:
|
||||
return "XBOX";
|
||||
|
||||
case RC_CONSOLE_ZX81:
|
||||
return "ZX-81";
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 3DO ===== */
|
||||
/* http://www.arcaderestoration.com/memorymap/48/3DO+Bios.aspx */
|
||||
/* NOTE: the Opera core attempts to expose the NVRAM as RETRO_SAVE_RAM, but the 3DO documentation
|
||||
* says that applications should only access NVRAM through API calls as it's shared across mulitple
|
||||
* games. This suggests that even if the core does expose it, it may change depending on which other
|
||||
* games the user has played - so ignore it.
|
||||
*/
|
||||
static const rc_memory_region_t _rc_memory_regions_3do[] = {
|
||||
{ 0x000000U, 0x1FFFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Main RAM" },
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_3do = { _rc_memory_regions_3do, 1 };
|
||||
|
||||
/* ===== Apple II ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_appleii[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Main RAM" },
|
||||
{ 0x010000U, 0x01FFFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Auxillary RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_appleii = { _rc_memory_regions_appleii, 2 };
|
||||
|
||||
/* ===== Atari 2600 ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_atari2600[] = {
|
||||
{ 0x000000U, 0x00007FU, 0x000080U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_atari2600 = { _rc_memory_regions_atari2600, 1 };
|
||||
|
||||
/* ===== Atari 7800 ===== */
|
||||
/* http://www.atarihq.com/danb/files/78map.txt */
|
||||
/* http://pdf.textfiles.com/technical/7800_devkit.pdf */
|
||||
static const rc_memory_region_t _rc_memory_regions_atari7800[] = {
|
||||
{ 0x000000U, 0x0017FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Hardware Interface" },
|
||||
{ 0x001800U, 0x0027FFU, 0x001800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x002800U, 0x002FFFU, 0x002800U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirrored RAM" },
|
||||
{ 0x003000U, 0x0037FFU, 0x003000U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirrored RAM" },
|
||||
{ 0x003800U, 0x003FFFU, 0x003800U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Mirrored RAM" },
|
||||
{ 0x004000U, 0x007FFFU, 0x004000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" },
|
||||
{ 0x008000U, 0x00FFFFU, 0x008000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_atari7800 = { _rc_memory_regions_atari7800, 7 };
|
||||
|
||||
/* ===== Atari Jaguar ===== */
|
||||
/* https://www.mulle-kybernetik.com/jagdox/memorymap.html */
|
||||
static const rc_memory_region_t _rc_memory_regions_atari_jaguar[] = {
|
||||
{ 0x000000U, 0x1FFFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_atari_jaguar = { _rc_memory_regions_atari_jaguar, 1 };
|
||||
|
||||
/* ===== Atari Lynx ===== */
|
||||
/* http://www.retroisle.com/atari/lynx/Technical/Programming/lynxprgdumm.php */
|
||||
static const rc_memory_region_t _rc_memory_regions_atari_lynx[] = {
|
||||
{ 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Zero Page" },
|
||||
{ 0x000100U, 0x0001FFU, 0x000100U, RC_MEMORY_TYPE_SYSTEM_RAM, "Stack" },
|
||||
{ 0x000200U, 0x00FBFFU, 0x000200U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x00FC00U, 0x00FCFFU, 0x00FC00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "SUZY hardware access" },
|
||||
{ 0x00FD00U, 0x00FDFFU, 0x00FD00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "MIKEY hardware access" },
|
||||
{ 0x00FE00U, 0x00FFF7U, 0x00FE00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Boot ROM" },
|
||||
{ 0x00FFF8U, 0x00FFFFU, 0x00FFF8U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Hardware vectors" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_atari_lynx = { _rc_memory_regions_atari_lynx, 7 };
|
||||
|
||||
/* ===== ColecoVision ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_colecovision[] = {
|
||||
{ 0x000000U, 0x0003FFU, 0x006000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_colecovision = { _rc_memory_regions_colecovision, 1 };
|
||||
|
||||
/* ===== GameBoy / GameBoy Color ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_gameboy[] = {
|
||||
{ 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt vector" },
|
||||
{ 0x000100U, 0x00014FU, 0x000100U, RC_MEMORY_TYPE_READONLY, "Cartridge header" },
|
||||
{ 0x000150U, 0x003FFFU, 0x000150U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM (fixed)" }, /* bank 0 */
|
||||
{ 0x004000U, 0x007FFFU, 0x004000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM (paged)" }, /* bank 1-XX (switchable) */
|
||||
{ 0x008000U, 0x0097FFU, 0x008000U, RC_MEMORY_TYPE_VIDEO_RAM, "Tile RAM" },
|
||||
{ 0x009800U, 0x009BFFU, 0x009800U, RC_MEMORY_TYPE_VIDEO_RAM, "BG1 map data" },
|
||||
{ 0x009C00U, 0x009FFFU, 0x009C00U, RC_MEMORY_TYPE_VIDEO_RAM, "BG2 map data" },
|
||||
{ 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM"},
|
||||
{ 0x00C000U, 0x00CFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (fixed)" },
|
||||
{ 0x00D000U, 0x00DFFFU, 0x00D000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (bank 1)" },
|
||||
{ 0x00E000U, 0x00FDFFU, 0x00C000U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Echo RAM" },
|
||||
{ 0x00FE00U, 0x00FE9FU, 0x00FE00U, RC_MEMORY_TYPE_VIDEO_RAM, "Sprite RAM"},
|
||||
{ 0x00FEA0U, 0x00FEFFU, 0x00FEA0U, RC_MEMORY_TYPE_READONLY, "Unusable"},
|
||||
{ 0x00FF00U, 0x00FF7FU, 0x00FF00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Hardware I/O"},
|
||||
{ 0x00FF80U, 0x00FFFEU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "Quick RAM"},
|
||||
{ 0x00FFFFU, 0x00FFFFU, 0x00FFFFU, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt enable"},
|
||||
|
||||
/* GameBoy Color provides six extra banks of memory that can be paged out through the $DXXX
|
||||
* memory space, but the timing of that does not correspond with blanks, which is when achievements
|
||||
* are processed. As such, it is desirable to always have access to these extra banks. We do this
|
||||
* by expecting the extra banks to be addressable at addresses not supported by the native system. */
|
||||
{ 0x010000U, 0x015FFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (banks 2-7, GBC only)" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_gameboy = { _rc_memory_regions_gameboy, 16 };
|
||||
static const rc_memory_regions_t rc_memory_regions_gameboy_color = { _rc_memory_regions_gameboy, 17 };
|
||||
|
||||
/* ===== GameBoy Advance ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_gameboy_advance[] = {
|
||||
{ 0x000000U, 0x007FFFU, 0x03000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" },
|
||||
{ 0x008000U, 0x047FFFU, 0x02000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_gameboy_advance = { _rc_memory_regions_gameboy_advance, 2 };
|
||||
|
||||
/* ===== Game Gear ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_game_gear[] = {
|
||||
{ 0x000000U, 0x001FFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_game_gear = { _rc_memory_regions_game_gear, 1 };
|
||||
|
||||
/* ===== Intellivision ===== */
|
||||
/* http://wiki.intellivision.us/index.php%3Ftitle%3DMemory_Map */
|
||||
static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
|
||||
{ 0x000000U, 0x00007FU, 0x000000U, RC_MEMORY_TYPE_VIDEO_RAM, "STIC Registers" },
|
||||
{ 0x000080U, 0x0000FFU, 0x000080U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||
{ 0x000100U, 0x00035FU, 0x000100U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x000360U, 0x0003FFU, 0x000360U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||
{ 0x000400U, 0x000FFFU, 0x000400U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
||||
{ 0x001000U, 0x001FFFU, 0x001000U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||
{ 0x002000U, 0x002FFFU, 0x002000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
||||
{ 0x003000U, 0x003FFFU, 0x003000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" },
|
||||
{ 0x004000U, 0x00FFFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
|
||||
|
||||
/* ===== Master System ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_master_system[] = {
|
||||
{ 0x000000U, 0x001FFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_master_system = { _rc_memory_regions_master_system, 1 };
|
||||
|
||||
/* ===== MegaDrive (Genesis) ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_megadrive[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0xFF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x010000U, 0x01FFFFU, 0x000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_megadrive = { _rc_memory_regions_megadrive, 2 };
|
||||
|
||||
/* ===== Neo Geo Pocket ===== */
|
||||
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
|
||||
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
|
||||
/* MednafenNGP exposes 16KB, but the doc suggests there's 24-32KB */
|
||||
{ 0x000000U, 0x003FFFU, 0x000000U, 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 };
|
||||
|
||||
/* ===== Nintendo Entertainment System ===== */
|
||||
/* https://wiki.nesdev.com/w/index.php/CPU_memory_map */
|
||||
static const rc_memory_region_t _rc_memory_regions_nes[] = {
|
||||
{ 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, 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"},
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_nes = { _rc_memory_regions_nes, 11 };
|
||||
|
||||
/* ===== Nintendo 64 ===== */
|
||||
/* https://raw.githubusercontent.com/mikeryan/n64dev/master/docs/n64ops/n64ops%23h.txt */
|
||||
static const rc_memory_region_t _rc_memory_regions_n64[] = {
|
||||
{ 0x000000U, 0x1FFFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RDRAM 1 */
|
||||
{ 0x200000U, 0x3FFFFFU, 0x00020000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RDRAM 2 */
|
||||
{ 0x400000U, 0x7FFFFFU, 0x80000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" } /* expansion pak - cannot find any details for real address */
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_n64 = { _rc_memory_regions_n64, 3 };
|
||||
|
||||
/* ===== Nintendo DS ===== */
|
||||
/* https://www.akkit.org/info/gbatek.htm#dsmemorymaps */
|
||||
static const rc_memory_region_t _rc_memory_regions_nintendo_ds[] = {
|
||||
{ 0x000000U, 0x3FFFFFU, 0x02000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_nintendo_ds = { _rc_memory_regions_nintendo_ds, 1 };
|
||||
|
||||
/* ===== Oric ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_oric[] = {
|
||||
/* actual size depends on machine type - up to 64KB */
|
||||
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_oric = { _rc_memory_regions_oric, 1 };
|
||||
|
||||
/* ===== PC-8800 ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_pc8800[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Main RAM" },
|
||||
{ 0x010000U, 0x010FFFU, 0x010000U, RC_MEMORY_TYPE_VIDEO_RAM, "Text VRAM" } /* technically VRAM, but often used as system RAM */
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_pc8800 = { _rc_memory_regions_pc8800, 2 };
|
||||
|
||||
/* ===== PC Engine ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_pcengine[] = {
|
||||
{ 0x000000U, 0x001FFFU, 0x1F0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x002000U, 0x011FFFU, 0x100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD RAM" },
|
||||
{ 0x012000U, 0x041FFFU, 0x0D0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Super System Card RAM" },
|
||||
{ 0x042000U, 0x0427FFU, 0x1EE000U, RC_MEMORY_TYPE_SAVE_RAM, "CD Battery-backed RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_pcengine = { _rc_memory_regions_pcengine, 4 };
|
||||
|
||||
/* ===== PlayStation ===== */
|
||||
/* http://www.raphnet.net/electronique/psx_adaptor/Playstation.txt */
|
||||
static const rc_memory_region_t _rc_memory_regions_playstation[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
|
||||
{ 0x010000U, 0x1FFFFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_playstation = { _rc_memory_regions_playstation, 2 };
|
||||
|
||||
/* ===== Pokemon Mini ===== */
|
||||
/* https://www.pokemon-mini.net/documentation/memory-map/ */
|
||||
static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
|
||||
{ 0x000000U, 0x000FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "BIOS RAM" },
|
||||
{ 0x001000U, 0x001FFFU, 0x001000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_pokemini = { _rc_memory_regions_pokemini, 2 };
|
||||
|
||||
/* ===== Sega CD ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_segacd[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x00FF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "68000 RAM" },
|
||||
{ 0x010000U, 0x08FFFFU, 0x80000000U, RC_MEMORY_TYPE_SAVE_RAM, "CD PRG RAM" } /* normally banked into $020000-$03FFFF */
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_segacd = { _rc_memory_regions_segacd, 2 };
|
||||
|
||||
/* ===== Sega Saturn ===== */
|
||||
/* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */
|
||||
static const rc_memory_region_t _rc_memory_regions_saturn[] = {
|
||||
{ 0x000000U, 0x0FFFFFU, 0x00200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Work RAM Low" },
|
||||
{ 0x100000U, 0x1FFFFFU, 0x06000000U, RC_MEMORY_TYPE_SAVE_RAM, "Work RAM High" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_saturn = { _rc_memory_regions_saturn, 2 };
|
||||
|
||||
/* ===== SG-1000 ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_sg1000[] = {
|
||||
{ 0x000000U, 0x0003FFU, 0xC000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
/* TODO: should cartridge memory be exposed ($0000-$BFFF)? it's usually just ROM data, but may contain on-cartridge RAM
|
||||
* This not is also concerning: http://www.smspower.org/Development/MemoryMap
|
||||
* Cartridges may disable the system RAM and thus take over the full 64KB address space. */
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_sg1000 = { _rc_memory_regions_sg1000, 1 };
|
||||
|
||||
/* ===== Super Cassette Vision ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_scv[] = {
|
||||
{ 0x000000U, 0x000FFFU, 0x000000U, RC_MEMORY_TYPE_READONLY, "System ROM" },
|
||||
{ 0x001000U, 0x001FFFU, 0x001000U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||
{ 0x002000U, 0x003FFFU, 0x002000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" },
|
||||
{ 0x004000U, 0x007FFFU, 0x004000U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||
{ 0x008000U, 0x00DFFFU, 0x008000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM" },
|
||||
{ 0x00E000U, 0x00FF7FU, 0x00E000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" },
|
||||
{ 0x00FF80U, 0x00FFFFU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_scv = { _rc_memory_regions_scv, 7 };
|
||||
|
||||
/* ===== Super Nintendo ===== */
|
||||
/* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */
|
||||
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" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_snes = { _rc_memory_regions_snes, 2 };
|
||||
|
||||
/* ===== WonderSwan ===== */
|
||||
/* http://daifukkat.su/docs/wsman/#ovr_memmap */
|
||||
static const rc_memory_region_t _rc_memory_regions_wonderswan[] = {
|
||||
/* RAM ends at 0x3FFF for WonderSwan, WonderSwan color uses all 64KB */
|
||||
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_wonderswan = { _rc_memory_regions_wonderswan, 1 };
|
||||
|
||||
/* ===== Vectrex ===== */
|
||||
/* https://roadsidethoughts.com/vectrex/vectrex-memory-map.htm */
|
||||
static const rc_memory_region_t _rc_memory_regions_vectrex[] = {
|
||||
{ 0x000000U, 0x0003FFU, 0x00C800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_vectrex = { _rc_memory_regions_vectrex, 1 };
|
||||
|
||||
/* ===== Virtual Boy ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_virtualboy[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x05000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x010000U, 0x01FFFFU, 0x06000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_virtualboy = { _rc_memory_regions_virtualboy, 2 };
|
||||
|
||||
/* ===== default ===== */
|
||||
static const rc_memory_regions_t rc_memory_regions_none = { 0, 0 };
|
||||
|
||||
const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
{
|
||||
switch (console_id)
|
||||
{
|
||||
case RC_CONSOLE_3DO:
|
||||
return &rc_memory_regions_3do;
|
||||
|
||||
case RC_CONSOLE_APPLE_II:
|
||||
return &rc_memory_regions_appleii;
|
||||
|
||||
case RC_CONSOLE_ATARI_2600:
|
||||
return &rc_memory_regions_atari2600;
|
||||
|
||||
case RC_CONSOLE_ATARI_7800:
|
||||
return &rc_memory_regions_atari7800;
|
||||
|
||||
case RC_CONSOLE_ATARI_JAGUAR:
|
||||
return &rc_memory_regions_atari_jaguar;
|
||||
|
||||
case RC_CONSOLE_ATARI_LYNX:
|
||||
return &rc_memory_regions_atari_lynx;
|
||||
|
||||
case RC_CONSOLE_COLECOVISION:
|
||||
return &rc_memory_regions_colecovision;
|
||||
|
||||
case RC_CONSOLE_GAMEBOY:
|
||||
return &rc_memory_regions_gameboy;
|
||||
|
||||
case RC_CONSOLE_GAMEBOY_COLOR:
|
||||
return &rc_memory_regions_gameboy_color;
|
||||
|
||||
case RC_CONSOLE_GAMEBOY_ADVANCE:
|
||||
return &rc_memory_regions_gameboy_advance;
|
||||
|
||||
case RC_CONSOLE_GAME_GEAR:
|
||||
return &rc_memory_regions_game_gear;
|
||||
|
||||
case RC_CONSOLE_INTELLIVISION:
|
||||
return &rc_memory_regions_intellivision;
|
||||
|
||||
case RC_CONSOLE_MASTER_SYSTEM:
|
||||
return &rc_memory_regions_master_system;
|
||||
|
||||
case RC_CONSOLE_MEGA_DRIVE:
|
||||
case RC_CONSOLE_SEGA_32X:
|
||||
/* NOTE: 32x adds an extra 512KB of memory (256KB RAM + 256KB VRAM) to the
|
||||
* Genesis, but we currently don't support it. */
|
||||
return &rc_memory_regions_megadrive;
|
||||
|
||||
case RC_CONSOLE_NEOGEO_POCKET:
|
||||
return &rc_memory_regions_neo_geo_pocket;
|
||||
|
||||
case RC_CONSOLE_NINTENDO:
|
||||
return &rc_memory_regions_nes;
|
||||
|
||||
case RC_CONSOLE_NINTENDO_64:
|
||||
return &rc_memory_regions_n64;
|
||||
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
return &rc_memory_regions_nintendo_ds;
|
||||
|
||||
case RC_CONSOLE_ORIC:
|
||||
return &rc_memory_regions_oric;
|
||||
|
||||
case RC_CONSOLE_PC8800:
|
||||
return &rc_memory_regions_pc8800;
|
||||
|
||||
case RC_CONSOLE_PC_ENGINE:
|
||||
return &rc_memory_regions_pcengine;
|
||||
|
||||
case RC_CONSOLE_PLAYSTATION:
|
||||
return &rc_memory_regions_playstation;
|
||||
|
||||
case RC_CONSOLE_POKEMON_MINI:
|
||||
return &rc_memory_regions_pokemini;
|
||||
|
||||
case RC_CONSOLE_SATURN:
|
||||
return &rc_memory_regions_saturn;
|
||||
|
||||
case RC_CONSOLE_SEGA_CD:
|
||||
return &rc_memory_regions_segacd;
|
||||
|
||||
case RC_CONSOLE_SG1000:
|
||||
return &rc_memory_regions_sg1000;
|
||||
|
||||
case RC_CONSOLE_SUPER_CASSETTEVISION:
|
||||
return &rc_memory_regions_scv;
|
||||
|
||||
case RC_CONSOLE_SUPER_NINTENDO:
|
||||
return &rc_memory_regions_snes;
|
||||
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return &rc_memory_regions_wonderswan;
|
||||
|
||||
case RC_CONSOLE_VECTREX:
|
||||
return &rc_memory_regions_vectrex;
|
||||
|
||||
case RC_CONSOLE_VIRTUAL_BOY:
|
||||
return &rc_memory_regions_virtualboy;
|
||||
|
||||
default:
|
||||
return &rc_memory_regions_none;
|
||||
}
|
||||
}
|
41
deps/rcheevos/src/rcheevos/expression.c
vendored
41
deps/rcheevos/src/rcheevos/expression.c
vendored
@ -1,41 +0,0 @@
|
||||
#include "internal.h"
|
||||
|
||||
rc_expression_t* rc_parse_expression(const char** memaddr, rc_parse_state_t* parse) {
|
||||
rc_expression_t* self;
|
||||
rc_term_t** next;
|
||||
|
||||
self = RC_ALLOC(rc_expression_t, parse);
|
||||
next = &self->terms;
|
||||
|
||||
for (;;) {
|
||||
*next = rc_parse_term(memaddr, 0, parse);
|
||||
|
||||
if (parse->offset < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
next = &(*next)->next;
|
||||
|
||||
if (**memaddr != '_') {
|
||||
break;
|
||||
}
|
||||
|
||||
(*memaddr)++;
|
||||
}
|
||||
|
||||
*next = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
int rc_evaluate_expression(rc_expression_t* self, rc_eval_state_t* eval_state) {
|
||||
rc_term_t* term;
|
||||
int value;
|
||||
|
||||
value = 0;
|
||||
|
||||
for (term = self->terms; term != 0; term = term->next) {
|
||||
value += rc_evaluate_term(term, eval_state);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
8
deps/rcheevos/src/rcheevos/format.c
vendored
8
deps/rcheevos/src/rcheevos/format.c
vendored
@ -1,5 +1,7 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@ -11,7 +13,7 @@ int rc_parse_format(const char* format_str) {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 'T':
|
||||
if (!strcmp(format_str, "IME")) {
|
||||
return RC_FORMAT_FRAMES;
|
||||
@ -21,7 +23,7 @@ int rc_parse_format(const char* format_str) {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 'S':
|
||||
if (!strcmp(format_str, "ECS")) {
|
||||
return RC_FORMAT_SECONDS;
|
||||
@ -34,7 +36,7 @@ int rc_parse_format(const char* format_str) {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 'M':
|
||||
if (!strcmp(format_str, "ILLISECS")) {
|
||||
return RC_FORMAT_CENTISECS;
|
||||
|
15
deps/rcheevos/src/rcheevos/internal.h
vendored
15
deps/rcheevos/src/rcheevos/internal.h
vendored
@ -6,7 +6,6 @@
|
||||
#define RC_ALLOW_ALIGN(T) struct __align_ ## T { char ch; T t; };
|
||||
RC_ALLOW_ALIGN(rc_condition_t)
|
||||
RC_ALLOW_ALIGN(rc_condset_t)
|
||||
RC_ALLOW_ALIGN(rc_expression_t)
|
||||
RC_ALLOW_ALIGN(rc_lboard_t)
|
||||
RC_ALLOW_ALIGN(rc_memref_value_t)
|
||||
RC_ALLOW_ALIGN(rc_operand_t)
|
||||
@ -15,7 +14,6 @@ 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_term_t)
|
||||
RC_ALLOW_ALIGN(rc_trigger_t)
|
||||
RC_ALLOW_ALIGN(rc_value_t)
|
||||
RC_ALLOW_ALIGN(char)
|
||||
@ -36,8 +34,6 @@ typedef struct {
|
||||
rc_condition_t condition;
|
||||
rc_condset_t condset;
|
||||
rc_trigger_t trigger;
|
||||
rc_term_t term;
|
||||
rc_expression_t expression;
|
||||
rc_lboard_t lboard;
|
||||
rc_memref_value_t memref_value;
|
||||
rc_richpresence_t richpresence;
|
||||
@ -61,6 +57,8 @@ typedef struct {
|
||||
unsigned measured_value; /* Measured */
|
||||
char was_reset; /* ResetIf triggered */
|
||||
char has_hits; /* one of more hit counts is non-zero */
|
||||
char primed; /* true if all non-Trigger conditions are true */
|
||||
char measured_from_hits; /* true if the measured_value came from a condition's hit count */
|
||||
}
|
||||
rc_eval_state_t;
|
||||
|
||||
@ -86,7 +84,7 @@ void rc_destroy_parse_state(rc_parse_state_t* parse);
|
||||
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch);
|
||||
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length);
|
||||
|
||||
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect);
|
||||
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_indirect);
|
||||
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
|
||||
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state);
|
||||
@ -99,16 +97,11 @@ void rc_reset_condset(rc_condset_t* self);
|
||||
|
||||
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect);
|
||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse);
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
rc_term_t* rc_parse_term(const char** memaddr, int is_indirect, rc_parse_state_t* parse);
|
||||
int rc_evaluate_term(rc_term_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
rc_expression_t* rc_parse_expression(const char** memaddr, rc_parse_state_t* parse);
|
||||
int rc_evaluate_expression(rc_expression_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
|
||||
void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_state_t* parse);
|
||||
|
115
deps/rcheevos/src/rcheevos/lboard.c
vendored
115
deps/rcheevos/src/rcheevos/lboard.c
vendored
@ -133,7 +133,7 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||
return;
|
||||
}
|
||||
|
||||
self->started = self->submitted = 0;
|
||||
self->state = RC_LBOARD_STATE_WAITING;
|
||||
}
|
||||
|
||||
int rc_lboard_size(const char* memaddr) {
|
||||
@ -164,82 +164,89 @@ rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, in
|
||||
|
||||
int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
int start_ok, cancel_ok, submit_ok;
|
||||
int action = -1;
|
||||
|
||||
rc_update_memref_values(self->memrefs, peek, peek_ud);
|
||||
|
||||
/* ASSERT: these are always tested once every frame, to ensure delta variables work properly */
|
||||
if (self->state == RC_LBOARD_STATE_INACTIVE)
|
||||
return RC_LBOARD_STATE_INACTIVE;
|
||||
|
||||
/* these are always tested once every frame, to ensure hit counts work properly */
|
||||
start_ok = rc_test_trigger(&self->start, peek, peek_ud, L);
|
||||
cancel_ok = rc_test_trigger(&self->cancel, peek, peek_ud, L);
|
||||
submit_ok = rc_test_trigger(&self->submit, peek, peek_ud, L);
|
||||
|
||||
if (self->submitted) {
|
||||
/* if we've already submitted or canceled the leaderboard, don't reactivate it until it becomes inactive. */
|
||||
if (!start_ok) {
|
||||
self->submitted = 0;
|
||||
}
|
||||
}
|
||||
else if (!self->started) {
|
||||
/* leaderboard is not active, if the start condition is true, activate it */
|
||||
if (start_ok && !cancel_ok) {
|
||||
if (submit_ok) {
|
||||
/* start and submit both true in the same frame, just submit without announcing the leaderboard is available */
|
||||
action = RC_LBOARD_TRIGGERED;
|
||||
/* prevent multiple submissions/notifications */
|
||||
self->submitted = 1;
|
||||
switch (self->state)
|
||||
{
|
||||
case RC_LBOARD_STATE_WAITING:
|
||||
case RC_LBOARD_STATE_TRIGGERED:
|
||||
case RC_LBOARD_STATE_CANCELED:
|
||||
/* don't activate/reactivate until the start condition becomes false */
|
||||
if (start_ok) {
|
||||
*value = 0;
|
||||
return RC_LBOARD_STATE_INACTIVE; /* just return inactive for all of these */
|
||||
}
|
||||
else if (self->start.requirement != 0 || self->start.alternative != 0) {
|
||||
self->started = 1;
|
||||
action = RC_LBOARD_STARTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* leaderboard is active */
|
||||
if (cancel_ok) {
|
||||
/* cancel condition is true, deactivate the leaderboard */
|
||||
self->started = 0;
|
||||
action = RC_LBOARD_CANCELED;
|
||||
/* prevent multiple cancel notifications */
|
||||
self->submitted = 1;
|
||||
}
|
||||
else if (submit_ok) {
|
||||
/* submit condition is true, submit the current value */
|
||||
self->started = 0;
|
||||
action = RC_LBOARD_TRIGGERED;
|
||||
self->submitted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == -1) {
|
||||
action = self->started ? RC_LBOARD_ACTIVE : RC_LBOARD_INACTIVE;
|
||||
/* start condition is false, allow the leaderboard to start on future frames */
|
||||
self->state = RC_LBOARD_STATE_ACTIVE;
|
||||
break;
|
||||
|
||||
case RC_LBOARD_STATE_ACTIVE:
|
||||
/* leaderboard attempt is not in progress. if the start condition is true and the cancel condition is not, start the attempt */
|
||||
if (start_ok && !cancel_ok) {
|
||||
if (submit_ok) {
|
||||
/* start and submit are both true in the same frame, just submit without announcing the leaderboard is available */
|
||||
self->state = RC_LBOARD_STATE_TRIGGERED;
|
||||
}
|
||||
else if (self->start.requirement == 0 && self->start.alternative == 0) {
|
||||
/* start condition is empty - this leaderboard is submit-only with no measured progress */
|
||||
}
|
||||
else {
|
||||
/* start the leaderboard attempt */
|
||||
self->state = RC_LBOARD_STATE_STARTED;
|
||||
|
||||
/* reset any hit counts in the value */
|
||||
if (self->value.conditions)
|
||||
rc_reset_condset(self->value.conditions);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_LBOARD_STATE_STARTED:
|
||||
/* leaderboard attempt in progress */
|
||||
if (cancel_ok) {
|
||||
/* cancel condition is true, abort the attempt */
|
||||
self->state = RC_LBOARD_STATE_CANCELED;
|
||||
}
|
||||
else if (submit_ok) {
|
||||
/* submit condition is true, submit the current value */
|
||||
self->state = RC_LBOARD_STATE_TRIGGERED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate the value */
|
||||
switch (action) {
|
||||
case RC_LBOARD_STARTED:
|
||||
if (self->value.conditions)
|
||||
rc_reset_condset(self->value.conditions);
|
||||
/* fall through */
|
||||
case RC_LBOARD_ACTIVE:
|
||||
*value = rc_evaluate_value(self->progress != 0 ? self->progress : &self->value, peek, peek_ud, L);
|
||||
break;
|
||||
switch (self->state) {
|
||||
case RC_LBOARD_STATE_STARTED:
|
||||
if (self->progress) {
|
||||
*value = rc_evaluate_value(self->progress, peek, peek_ud, L);
|
||||
break;
|
||||
}
|
||||
/* fallthrough to RC_LBOARD_STATE_TRIGGERED */
|
||||
|
||||
case RC_LBOARD_TRIGGERED:
|
||||
case RC_LBOARD_STATE_TRIGGERED:
|
||||
*value = rc_evaluate_value(&self->value, peek, peek_ud, L);
|
||||
break;
|
||||
|
||||
case RC_LBOARD_INACTIVE:
|
||||
case RC_LBOARD_CANCELED:
|
||||
default:
|
||||
*value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return action;
|
||||
return self->state;
|
||||
}
|
||||
|
||||
void rc_reset_lboard(rc_lboard_t* self) {
|
||||
self->started = self->submitted = 0;
|
||||
self->state = RC_LBOARD_STATE_WAITING;
|
||||
|
||||
rc_reset_trigger(&self->start);
|
||||
rc_reset_trigger(&self->submit);
|
||||
|
61
deps/rcheevos/src/rcheevos/memref.c
vendored
61
deps/rcheevos/src/rcheevos/memref.c
vendored
@ -5,7 +5,7 @@
|
||||
|
||||
#define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF
|
||||
|
||||
static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect) {
|
||||
static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
rc_memref_t* memref;
|
||||
int i;
|
||||
|
||||
@ -20,7 +20,7 @@ static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* pa
|
||||
/* have to track unique address/size/bcd combinations - use scratch.memref for sizing mode */
|
||||
for (i = 0; i < parse->scratch.memref_count; ++i) {
|
||||
memref = &parse->scratch.memref[i];
|
||||
if (memref->address == address && memref->size == size && memref->is_bcd == is_bcd) {
|
||||
if (memref->address == address && memref->size == size) {
|
||||
return &parse->scratch.obj.memref_value;
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,6 @@ static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* pa
|
||||
memref = &parse->scratch.memref[parse->scratch.memref_count++];
|
||||
memref->address = address;
|
||||
memref->size = size;
|
||||
memref->is_bcd = is_bcd;
|
||||
memref->is_indirect = is_indirect;
|
||||
}
|
||||
|
||||
@ -65,7 +64,7 @@ static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* pa
|
||||
return RC_ALLOC(rc_memref_value_t, parse);
|
||||
}
|
||||
|
||||
static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect) {
|
||||
static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
rc_memref_value_t** next_memref_value;
|
||||
rc_memref_value_t* memref_value;
|
||||
rc_memref_value_t* indirect_memref_value;
|
||||
@ -75,8 +74,8 @@ static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t*
|
||||
next_memref_value = parse->first_memref;
|
||||
while (*next_memref_value) {
|
||||
memref_value = *next_memref_value;
|
||||
if (!memref_value->memref.is_indirect && memref_value->memref.address == address &&
|
||||
memref_value->memref.size == size && memref_value->memref.is_bcd == is_bcd) {
|
||||
if (!memref_value->memref.is_indirect && memref_value->memref.address == address &&
|
||||
memref_value->memref.size == size) {
|
||||
return memref_value;
|
||||
}
|
||||
|
||||
@ -96,7 +95,6 @@ static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t*
|
||||
memref_value = RC_ALLOC(rc_memref_value_t, parse);
|
||||
memref_value->memref.address = address;
|
||||
memref_value->memref.size = size;
|
||||
memref_value->memref.is_bcd = is_bcd;
|
||||
memref_value->memref.is_indirect = is_indirect;
|
||||
memref_value->value = 0;
|
||||
memref_value->previous = 0;
|
||||
@ -110,7 +108,6 @@ static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t*
|
||||
indirect_memref_value = RC_ALLOC(rc_memref_value_t, parse);
|
||||
indirect_memref_value->memref.address = MEMREF_PLACEHOLDER_ADDRESS;
|
||||
indirect_memref_value->memref.size = size;
|
||||
indirect_memref_value->memref.is_bcd = is_bcd;
|
||||
indirect_memref_value->memref.is_indirect = 1;
|
||||
indirect_memref_value->value = 0;
|
||||
indirect_memref_value->previous = 0;
|
||||
@ -123,16 +120,19 @@ static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t*
|
||||
return memref_value;
|
||||
}
|
||||
|
||||
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect) {
|
||||
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
|
||||
if (!parse->first_memref)
|
||||
return rc_alloc_memref_value_sizing_mode(parse, address, size, is_bcd, is_indirect);
|
||||
return rc_alloc_memref_value_sizing_mode(parse, address, size, is_indirect);
|
||||
|
||||
return rc_alloc_memref_value_constuct_mode(parse, address, size, is_bcd, is_indirect);
|
||||
return rc_alloc_memref_value_constuct_mode(parse, address, size, is_indirect);
|
||||
}
|
||||
|
||||
static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud) {
|
||||
unsigned value;
|
||||
|
||||
if (!peek)
|
||||
return 0;
|
||||
|
||||
switch (self->size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
@ -177,56 +177,19 @@ static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud)
|
||||
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = peek(self->address, 1, ud);
|
||||
|
||||
if (self->is_bcd) {
|
||||
value = ((value >> 4) & 0x0f) * 10 + (value & 0x0f);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value = peek(self->address, 2, ud);
|
||||
|
||||
if (self->is_bcd) {
|
||||
value = ((value >> 12) & 0x0f) * 1000
|
||||
+ ((value >> 8) & 0x0f) * 100
|
||||
+ ((value >> 4) & 0x0f) * 10
|
||||
+ ((value >> 0) & 0x0f) * 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS:
|
||||
/* peek 4 bytes - don't expect the caller to understand 24-bit numbers */
|
||||
value = peek(self->address, 4, ud);
|
||||
|
||||
if (self->is_bcd) {
|
||||
value = ((value >> 20) & 0x0f) * 100000
|
||||
+ ((value >> 16) & 0x0f) * 10000
|
||||
+ ((value >> 12) & 0x0f) * 1000
|
||||
+ ((value >> 8) & 0x0f) * 100
|
||||
+ ((value >> 4) & 0x0f) * 10
|
||||
+ ((value >> 0) & 0x0f) * 1;
|
||||
} else {
|
||||
value &= 0x00FFFFFF;
|
||||
}
|
||||
|
||||
value = peek(self->address, 4, ud) & 0x00FFFFFF;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value = peek(self->address, 4, ud);
|
||||
|
||||
if (self->is_bcd) {
|
||||
value = ((value >> 28) & 0x0f) * 10000000
|
||||
+ ((value >> 24) & 0x0f) * 1000000
|
||||
+ ((value >> 20) & 0x0f) * 100000
|
||||
+ ((value >> 16) & 0x0f) * 10000
|
||||
+ ((value >> 12) & 0x0f) * 1000
|
||||
+ ((value >> 8) & 0x0f) * 100
|
||||
+ ((value >> 4) & 0x0f) * 10
|
||||
+ ((value >> 0) & 0x0f) * 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
390
deps/rcheevos/src/rcheevos/operand.c
vendored
390
deps/rcheevos/src/rcheevos/operand.c
vendored
@ -70,7 +70,6 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
const char* aux = *memaddr;
|
||||
char* end;
|
||||
unsigned long address;
|
||||
char is_bcd = 0;
|
||||
char size;
|
||||
|
||||
switch (*aux++) {
|
||||
@ -78,15 +77,18 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
self->type = RC_OPERAND_DELTA;
|
||||
break;
|
||||
|
||||
case 'b': case 'B':
|
||||
self->type = RC_OPERAND_ADDRESS;
|
||||
is_bcd = 1;
|
||||
break;
|
||||
|
||||
case 'p': case 'P':
|
||||
self->type = RC_OPERAND_PRIOR;
|
||||
break;
|
||||
|
||||
case 'b': case 'B':
|
||||
self->type = RC_OPERAND_BCD;
|
||||
break;
|
||||
|
||||
case '~':
|
||||
self->type = RC_OPERAND_INVERTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
self->type = RC_OPERAND_ADDRESS;
|
||||
aux--;
|
||||
@ -104,25 +106,33 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
aux++;
|
||||
|
||||
switch (*aux++) {
|
||||
case 'm': case 'M': size = RC_MEMSIZE_BIT_0; break;
|
||||
case 'n': case 'N': size = RC_MEMSIZE_BIT_1; break;
|
||||
case 'o': case 'O': size = RC_MEMSIZE_BIT_2; break;
|
||||
case 'p': case 'P': size = RC_MEMSIZE_BIT_3; break;
|
||||
case 'q': case 'Q': size = RC_MEMSIZE_BIT_4; break;
|
||||
case 'r': case 'R': size = RC_MEMSIZE_BIT_5; break;
|
||||
case 's': case 'S': size = RC_MEMSIZE_BIT_6; break;
|
||||
case 't': case 'T': size = RC_MEMSIZE_BIT_7; break;
|
||||
case 'l': case 'L': size = RC_MEMSIZE_LOW; break;
|
||||
case 'u': case 'U': size = RC_MEMSIZE_HIGH; break;
|
||||
case 'h': case 'H': size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'w': case 'W': size = RC_MEMSIZE_24_BITS; break;
|
||||
case 'x': case 'X': size = RC_MEMSIZE_32_BITS; break;
|
||||
case 'm': case 'M': self->size = RC_MEMSIZE_BIT_0; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'n': case 'N': self->size = RC_MEMSIZE_BIT_1; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'o': case 'O': self->size = RC_MEMSIZE_BIT_2; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'p': case 'P': self->size = RC_MEMSIZE_BIT_3; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'q': case 'Q': self->size = RC_MEMSIZE_BIT_4; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'r': case 'R': self->size = RC_MEMSIZE_BIT_5; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 's': case 'S': self->size = RC_MEMSIZE_BIT_6; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 't': case 'T': self->size = RC_MEMSIZE_BIT_7; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'l': case 'L': self->size = RC_MEMSIZE_LOW; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'u': case 'U': self->size = RC_MEMSIZE_HIGH; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'k': case 'K': self->size = RC_MEMSIZE_BITCOUNT; size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'h': case 'H': self->size = size = RC_MEMSIZE_8_BITS; break;
|
||||
case 'w': case 'W': self->size = size = RC_MEMSIZE_24_BITS; break;
|
||||
case 'x': case 'X': self->size = size = RC_MEMSIZE_32_BITS; break;
|
||||
|
||||
default: /* fall through */
|
||||
case '0': 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':
|
||||
aux--;
|
||||
/* fallthrough */
|
||||
case ' ':
|
||||
size = RC_MEMSIZE_16_BITS;
|
||||
self->size = size = RC_MEMSIZE_16_BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
}
|
||||
|
||||
address = strtoul(aux, &end, 16);
|
||||
@ -135,7 +145,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
address = 0xffffffffU;
|
||||
}
|
||||
|
||||
self->value.memref = rc_alloc_memref_value(parse, address, size, is_bcd, is_indirect);
|
||||
self->value.memref = rc_alloc_memref_value(parse, address, size, is_indirect);
|
||||
if (parse->offset < 0)
|
||||
return parse->offset;
|
||||
|
||||
@ -143,14 +153,17 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
|
||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse) {
|
||||
const char* aux = *memaddr;
|
||||
char* end;
|
||||
int ret;
|
||||
unsigned long value;
|
||||
int negative;
|
||||
|
||||
self->size = RC_MEMSIZE_32_BITS;
|
||||
|
||||
switch (*aux) {
|
||||
case 'h': case 'H':
|
||||
case 'h': case 'H': /* hex constant */
|
||||
if (aux[2] == 'x' || aux[2] == 'X') {
|
||||
/* H0x1234 is a typo - either H1234 or 0xH1234 was probably meant */
|
||||
return RC_INVALID_CONST_OPERAND;
|
||||
@ -171,115 +184,9 @@ static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, in
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
if (aux[1] == 'x' || aux[1] == 'X') {
|
||||
/* fall through */
|
||||
default:
|
||||
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case '+': case '-':
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9':
|
||||
value = strtoul(aux, &end, 10);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_CONST_OPERAND;
|
||||
}
|
||||
|
||||
if (value > 0xffffffffU) {
|
||||
value = 0xffffffffU;
|
||||
}
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
self->value.num = (unsigned)value;
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
case '@':
|
||||
ret = rc_parse_operand_lua(self, &aux, parse);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
*memaddr = aux;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
|
||||
const char* aux = *memaddr;
|
||||
char* end;
|
||||
int ret;
|
||||
unsigned long value;
|
||||
long svalue;
|
||||
|
||||
switch (*aux) {
|
||||
case 'h': case 'H':
|
||||
value = strtoul(++aux, &end, 16);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_CONST_OPERAND;
|
||||
}
|
||||
|
||||
if (value > 0xffffffffU) {
|
||||
value = 0xffffffffU;
|
||||
}
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
self->value.num = (unsigned)value;
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
case 'v': case 'V':
|
||||
svalue = strtol(++aux, &end, 10);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_CONST_OPERAND;
|
||||
}
|
||||
|
||||
if (svalue > 0xffffffffU) {
|
||||
svalue = 0xffffffffU;
|
||||
}
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
self->value.num = (unsigned)svalue;
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
if (aux[1] == 'x' || aux[1] == 'X') {
|
||||
/* fall through */
|
||||
default:
|
||||
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case '.':
|
||||
case '+': case '-':
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9':
|
||||
self->value.dbl = strtod(aux, &end);
|
||||
case 'f': case 'F': /* floating point constant */
|
||||
self->value.dbl = strtod(++aux, &end);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_FP_OPERAND;
|
||||
@ -292,9 +199,77 @@ static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, int i
|
||||
else {
|
||||
self->type = RC_OPERAND_FP;
|
||||
}
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
|
||||
case 'v': case 'V': /* signed integer constant */
|
||||
negative = 0;
|
||||
++aux;
|
||||
|
||||
if (*aux == '-')
|
||||
{
|
||||
negative = 1;
|
||||
++aux;
|
||||
}
|
||||
else if (*aux == '+')
|
||||
{
|
||||
++aux;
|
||||
}
|
||||
|
||||
value = strtoul(aux, &end, 10);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_CONST_OPERAND;
|
||||
}
|
||||
|
||||
if (value > 0x7fffffffU) {
|
||||
value = 0x7fffffffU;
|
||||
}
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
|
||||
if (negative)
|
||||
self->value.num = (unsigned)(-((long)value));
|
||||
else
|
||||
self->value.num = (unsigned)value;
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
if (aux[1] == 'x' || aux[1] == 'X') {
|
||||
/* fall through */
|
||||
default:
|
||||
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through for case '0' where not '0x' */
|
||||
case '+': case '-':
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9':
|
||||
value = strtoul(aux, &end, 10);
|
||||
|
||||
if (end == aux) {
|
||||
return RC_INVALID_CONST_OPERAND;
|
||||
}
|
||||
|
||||
if (value > 0xffffffffU) {
|
||||
value = 0xffffffffU;
|
||||
}
|
||||
|
||||
self->type = RC_OPERAND_CONST;
|
||||
self->value.num = (unsigned)value;
|
||||
|
||||
aux = end;
|
||||
break;
|
||||
|
||||
case '@':
|
||||
ret = rc_parse_operand_lua(self, &aux, parse);
|
||||
|
||||
@ -309,15 +284,6 @@ static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, int i
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse) {
|
||||
if (is_trigger) {
|
||||
return rc_parse_operand_trigger(self, memaddr, is_indirect, parse);
|
||||
}
|
||||
else {
|
||||
return rc_parse_operand_term(self, memaddr, is_indirect, parse);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef RC_DISABLE_LUA
|
||||
|
||||
typedef struct {
|
||||
@ -339,6 +305,8 @@ static int rc_luapeek(lua_State* L) {
|
||||
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
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) {
|
||||
#ifndef RC_DISABLE_LUA
|
||||
rc_luapeek_t luapeek;
|
||||
@ -346,15 +314,15 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
|
||||
unsigned value = 0;
|
||||
|
||||
/* step 1: read memory */
|
||||
switch (self->type) {
|
||||
case RC_OPERAND_CONST:
|
||||
value = self->value.num;
|
||||
break;
|
||||
return self->value.num;
|
||||
|
||||
case RC_OPERAND_FP:
|
||||
/* This is handled by rc_evaluate_expression. */
|
||||
/* This is handled by rc_evaluate_condition_value. */
|
||||
return 0;
|
||||
|
||||
|
||||
case RC_OPERAND_LUA:
|
||||
#ifndef RC_DISABLE_LUA
|
||||
|
||||
@ -366,7 +334,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
luapeek.ud = eval_state->peek_userdata;
|
||||
|
||||
lua_pushlightuserdata(eval_state->L, &luapeek);
|
||||
|
||||
|
||||
if (lua_pcall(eval_state->L, 2, 1, 0) == LUA_OK) {
|
||||
if (lua_isboolean(eval_state->L, -1)) {
|
||||
value = lua_toboolean(eval_state->L, -1);
|
||||
@ -384,6 +352,8 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
break;
|
||||
|
||||
case RC_OPERAND_ADDRESS:
|
||||
case RC_OPERAND_BCD:
|
||||
case RC_OPERAND_INVERTED:
|
||||
value = rc_get_indirect_memref(self->value.memref, eval_state)->value;
|
||||
break;
|
||||
|
||||
@ -396,5 +366,131 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* step 2: mask off appropriate bits */
|
||||
switch (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;
|
||||
}
|
||||
|
||||
/* step 3: apply logic */
|
||||
switch (self->type)
|
||||
{
|
||||
case RC_OPERAND_BCD:
|
||||
switch (self->size)
|
||||
{
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = ((value >> 4) & 0x0f) * 10
|
||||
+ ((value ) & 0x0f);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value = ((value >> 12) & 0x0f) * 1000
|
||||
+ ((value >> 8) & 0x0f) * 100
|
||||
+ ((value >> 4) & 0x0f) * 10
|
||||
+ ((value ) & 0x0f);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS:
|
||||
value = ((value >> 20) & 0x0f) * 100000
|
||||
+ ((value >> 16) & 0x0f) * 10000
|
||||
+ ((value >> 12) & 0x0f) * 1000
|
||||
+ ((value >> 8) & 0x0f) * 100
|
||||
+ ((value >> 4) & 0x0f) * 10
|
||||
+ ((value ) & 0x0f);
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value = ((value >> 28) & 0x0f) * 10000000
|
||||
+ ((value >> 24) & 0x0f) * 1000000
|
||||
+ ((value >> 20) & 0x0f) * 100000
|
||||
+ ((value >> 16) & 0x0f) * 10000
|
||||
+ ((value >> 12) & 0x0f) * 1000
|
||||
+ ((value >> 8) & 0x0f) * 100
|
||||
+ ((value >> 4) & 0x0f) * 10
|
||||
+ ((value ) & 0x0f);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_OPERAND_INVERTED:
|
||||
switch (self->size)
|
||||
{
|
||||
case RC_MEMSIZE_LOW:
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value ^= 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value ^= 0xff;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
value ^= 0xffff;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS:
|
||||
value ^= 0xffffff;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
value ^= 0xffffffff;
|
||||
break;
|
||||
|
||||
default:
|
||||
value ^= 0x01;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
58
deps/rcheevos/src/rcheevos/richpresence.c
vendored
58
deps/rcheevos/src/rcheevos/richpresence.c
vendored
@ -1,9 +1,8 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* special formats only used by rc_richpresence_display_part_t.display_type. must not overlap other RC_FORMAT values */
|
||||
enum {
|
||||
@ -116,6 +115,7 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
/* just calculating size, can't confirm lookup exists */
|
||||
part = RC_ALLOC(rc_richpresence_display_part_t, parse);
|
||||
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
@ -124,6 +124,10 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
if (parse->offset < 0)
|
||||
return 0;
|
||||
++ptr;
|
||||
} else {
|
||||
/* no closing parenthesis - allocate space for the invalid string */
|
||||
--in; /* already skipped over @ */
|
||||
rc_alloc_str(parse, line, (int)(ptr - in));
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -139,6 +143,7 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
part->first_lookup_item = lookup->first_item;
|
||||
part->display_type = lookup->format;
|
||||
|
||||
in = line;
|
||||
line = ++ptr;
|
||||
while (ptr < endline && *ptr != ')')
|
||||
++ptr;
|
||||
@ -149,6 +154,12 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
return 0;
|
||||
++ptr;
|
||||
}
|
||||
else {
|
||||
/* non-terminated macro, dump the macro and the remaining portion of the line */
|
||||
--in; /* already skipped over @ */
|
||||
part->display_type = RC_FORMAT_STRING;
|
||||
part->text = rc_alloc_str(parse, in, (int)(ptr - in));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@ -170,7 +181,7 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
|
||||
|
||||
/* assert: the allocated string is going to be smaller than the memory used for the parameter of the macro */
|
||||
part->display_type = RC_FORMAT_UNKNOWN_MACRO;
|
||||
part->text = rc_alloc_str(parse, line, ptr - line);
|
||||
part->text = rc_alloc_str(parse, line, (int)(ptr - line));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,7 +205,7 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
|
||||
const char* defaultlabel = 0;
|
||||
char* endptr = 0;
|
||||
unsigned key;
|
||||
int chars;
|
||||
unsigned chars;
|
||||
|
||||
next = &lookup->first_item;
|
||||
|
||||
@ -372,7 +383,7 @@ int rc_richpresence_size(const char* script) {
|
||||
rc_richpresence_t* self;
|
||||
rc_parse_state_t parse;
|
||||
rc_init_parse_state(&parse, 0, 0, 0);
|
||||
|
||||
|
||||
self = RC_ALLOC(rc_richpresence_t, &parse);
|
||||
rc_parse_richpresence_internal(self, script, &parse);
|
||||
|
||||
@ -389,7 +400,7 @@ rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_S
|
||||
rc_init_parse_state_memrefs(&parse, &self->memrefs);
|
||||
|
||||
rc_parse_richpresence_internal(self, script, &parse);
|
||||
|
||||
|
||||
rc_destroy_parse_state(&parse);
|
||||
return parse.offset >= 0 ? self : 0;
|
||||
}
|
||||
@ -398,8 +409,10 @@ int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsi
|
||||
rc_richpresence_display_t* display;
|
||||
rc_richpresence_display_part_t* part;
|
||||
rc_richpresence_lookup_item_t* item;
|
||||
char tmp[256];
|
||||
char* ptr;
|
||||
int chars;
|
||||
const char* text;
|
||||
size_t chars;
|
||||
unsigned value;
|
||||
|
||||
rc_update_memref_values(richpresence->memrefs, peek, peek_ud);
|
||||
@ -412,37 +425,52 @@ int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsi
|
||||
while (part) {
|
||||
switch (part->display_type) {
|
||||
case RC_FORMAT_STRING:
|
||||
chars = snprintf(ptr, buffersize, "%s", part->text);
|
||||
text = part->text;
|
||||
chars = strlen(text);
|
||||
break;
|
||||
|
||||
case RC_FORMAT_LOOKUP:
|
||||
value = rc_evaluate_value(&part->value, peek, peek_ud, L);
|
||||
item = part->first_lookup_item;
|
||||
if (!item) {
|
||||
text = "";
|
||||
chars = 0;
|
||||
} else {
|
||||
while (item->next_item && item->value != value)
|
||||
item = item->next_item;
|
||||
|
||||
chars = snprintf(ptr, buffersize, "%s", item->label);
|
||||
text = item->label;
|
||||
chars = strlen(text);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_FORMAT_UNKNOWN_MACRO:
|
||||
chars = snprintf(ptr, buffersize, "[Unknown macro]%s", part->text);
|
||||
chars = snprintf(tmp, sizeof(tmp), "[Unknown macro]%s", part->text);
|
||||
text = tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
value = rc_evaluate_value(&part->value, peek, peek_ud, L);
|
||||
chars = rc_format_value(ptr, buffersize, value, part->display_type);
|
||||
chars = rc_format_value(tmp, sizeof(tmp), value, part->display_type);
|
||||
text = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chars > 0) {
|
||||
ptr += chars;
|
||||
buffersize -= chars;
|
||||
if (chars > 0 && buffersize > 0) {
|
||||
if ((unsigned)chars >= buffersize) {
|
||||
/* prevent write past end of buffer */
|
||||
memcpy(ptr, text, buffersize - 1);
|
||||
ptr[buffersize - 1] = '\0';
|
||||
buffersize = 0;
|
||||
}
|
||||
else {
|
||||
memcpy(ptr, text, chars);
|
||||
ptr[chars] = '\0';
|
||||
buffersize -= (unsigned)chars;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += chars;
|
||||
part = part->next;
|
||||
}
|
||||
|
||||
|
589
deps/rcheevos/src/rcheevos/runtime.c
vendored
Normal file
589
deps/rcheevos/src/rcheevos/runtime.c
vendored
Normal file
@ -0,0 +1,589 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include "../rhash/md5.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE 256
|
||||
|
||||
void rc_runtime_init(rc_runtime_t* self) {
|
||||
memset(self, 0, sizeof(rc_runtime_t));
|
||||
self->next_memref = &self->memrefs;
|
||||
}
|
||||
|
||||
void rc_runtime_destroy(rc_runtime_t* self) {
|
||||
unsigned i;
|
||||
|
||||
if (self->triggers) {
|
||||
for (i = 0; i < self->trigger_count; ++i)
|
||||
free(self->triggers[i].buffer);
|
||||
|
||||
free(self->triggers);
|
||||
self->triggers = NULL;
|
||||
|
||||
self->trigger_count = self->trigger_capacity = 0;
|
||||
}
|
||||
|
||||
if (self->lboards) {
|
||||
free(self->lboards);
|
||||
self->lboards = NULL;
|
||||
|
||||
self->lboard_count = self->lboard_capacity = 0;
|
||||
}
|
||||
|
||||
while (self->richpresence) {
|
||||
rc_runtime_richpresence_t* previous = self->richpresence->previous;
|
||||
|
||||
free(self->richpresence->buffer);
|
||||
free(self->richpresence);
|
||||
self->richpresence = previous;
|
||||
}
|
||||
|
||||
if (self->richpresence_display_buffer) {
|
||||
free(self->richpresence_display_buffer);
|
||||
self->richpresence_display_buffer = NULL;
|
||||
}
|
||||
|
||||
self->next_memref = 0;
|
||||
self->memrefs = 0;
|
||||
}
|
||||
|
||||
static void rc_runtime_checksum(const char* memaddr, unsigned char* md5) {
|
||||
md5_state_t state;
|
||||
md5_init(&state);
|
||||
md5_append(&state, (unsigned char*)memaddr, (int)strlen(memaddr));
|
||||
md5_finish(&state, md5);
|
||||
}
|
||||
|
||||
static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, unsigned 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);
|
||||
|
||||
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, unsigned id) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
if (self->triggers[i].id == id && self->triggers[i].trigger != NULL)
|
||||
rc_runtime_deactivate_trigger_by_index(self, i);
|
||||
}
|
||||
}
|
||||
|
||||
int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char* memaddr, lua_State* L, int funcs_idx) {
|
||||
void* trigger_buffer;
|
||||
rc_trigger_t* trigger;
|
||||
rc_parse_state_t parse;
|
||||
unsigned char md5[16];
|
||||
int size;
|
||||
char owns_memref;
|
||||
unsigned i;
|
||||
|
||||
if (memaddr == NULL)
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
rc_runtime_checksum(memaddr, md5);
|
||||
|
||||
/* check to see if the id is already registered with an active trigger */
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
if (self->triggers[i].id == id && self->triggers[i].trigger != NULL) {
|
||||
if (memcmp(self->triggers[i].md5, md5, 16) == 0) {
|
||||
/* if the checksum hasn't changed, we can reuse the existing item */
|
||||
rc_reset_trigger(self->triggers[i].trigger);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
/* checksum has changed, deactivate the the item */
|
||||
rc_runtime_deactivate_trigger_by_index(self, i);
|
||||
|
||||
/* deactivate may reorder the list so we should continue from the current index. however, we
|
||||
* assume that only one trigger is active per id, so having found that, just stop scanning.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* check to see if a disabled trigger for the specific id matches the trigger being registered */
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
if (self->triggers[i].id == id && memcmp(self->triggers[i].md5, md5, 16) == 0) {
|
||||
/* retrieve the trigger pointer from the buffer */
|
||||
size = 0;
|
||||
trigger = (rc_trigger_t*)rc_alloc(self->triggers[i].buffer, &size, sizeof(rc_trigger_t), RC_ALIGNOF(rc_trigger_t), 0);
|
||||
self->triggers[i].trigger = trigger;
|
||||
|
||||
rc_reset_trigger(trigger);
|
||||
return RC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* item has not been previously registered, determine how much space we need for it, and allocate it */
|
||||
size = rc_trigger_size(memaddr);
|
||||
if (size < 0)
|
||||
return size;
|
||||
|
||||
trigger_buffer = malloc(size);
|
||||
if (!trigger_buffer)
|
||||
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);
|
||||
|
||||
if (parse.offset < 0) {
|
||||
free(trigger_buffer);
|
||||
*self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */
|
||||
return parse.offset;
|
||||
}
|
||||
|
||||
/* if at least one memref was allocated within the trigger, we can't free the buffer when the trigger is deactivated */
|
||||
owns_memref = (*self->next_memref != NULL);
|
||||
if (owns_memref) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* grow the trigger buffer if necessary */
|
||||
if (self->trigger_count == self->trigger_capacity) {
|
||||
self->trigger_capacity += 32;
|
||||
if (!self->triggers)
|
||||
self->triggers = (rc_runtime_trigger_t*)malloc(self->trigger_capacity * sizeof(rc_runtime_trigger_t));
|
||||
else
|
||||
self->triggers = (rc_runtime_trigger_t*)realloc(self->triggers, self->trigger_capacity * sizeof(rc_runtime_trigger_t));
|
||||
}
|
||||
|
||||
/* assign the new trigger */
|
||||
self->triggers[self->trigger_count].id = id;
|
||||
self->triggers[self->trigger_count].trigger = trigger;
|
||||
self->triggers[self->trigger_count].buffer = trigger_buffer;
|
||||
memcpy(self->triggers[self->trigger_count].md5, md5, 16);
|
||||
self->triggers[self->trigger_count].owns_memrefs = owns_memref;
|
||||
++self->trigger_count;
|
||||
|
||||
/* reset it, and return it */
|
||||
trigger->memrefs = NULL;
|
||||
rc_reset_trigger(trigger);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, unsigned id)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
if (self->triggers[i].id == id && self->triggers[i].trigger != NULL)
|
||||
return self->triggers[i].trigger;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, unsigned 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);
|
||||
|
||||
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, unsigned id) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < self->lboard_count; ++i) {
|
||||
if (self->lboards[i].id == id && self->lboards[i].lboard != NULL)
|
||||
rc_runtime_deactivate_lboard_by_index(self, i);
|
||||
}
|
||||
}
|
||||
|
||||
int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* memaddr, lua_State* L, int funcs_idx) {
|
||||
void* lboard_buffer;
|
||||
unsigned char md5[16];
|
||||
rc_lboard_t* lboard;
|
||||
rc_parse_state_t parse;
|
||||
int size;
|
||||
char owns_memref;
|
||||
unsigned i;
|
||||
|
||||
if (memaddr == 0)
|
||||
return RC_INVALID_MEMORY_OPERAND;
|
||||
|
||||
rc_runtime_checksum(memaddr, md5);
|
||||
|
||||
/* check to see if the id is already registered with an active lboard */
|
||||
for (i = 0; i < self->lboard_count; ++i) {
|
||||
if (self->lboards[i].id == id && self->lboards[i].lboard != NULL) {
|
||||
if (memcmp(self->lboards[i].md5, md5, 16) == 0) {
|
||||
/* if the checksum hasn't changed, we can reuse the existing item */
|
||||
rc_reset_lboard(self->lboards[i].lboard);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
/* checksum has changed, deactivate the the item */
|
||||
rc_runtime_deactivate_lboard_by_index(self, i);
|
||||
|
||||
/* deactivate may reorder the list so we should continue from the current index. however, we
|
||||
* assume that only one trigger is active per id, so having found that, just stop scanning.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* check to see if a disabled lboard for the specific id matches the lboard being registered */
|
||||
for (i = 0; i < self->lboard_count; ++i) {
|
||||
if (self->lboards[i].id == id && memcmp(self->lboards[i].md5, md5, 16) == 0) {
|
||||
/* retrieve the lboard pointer from the buffer */
|
||||
size = 0;
|
||||
lboard = (rc_lboard_t*)rc_alloc(self->lboards[i].buffer, &size, sizeof(rc_lboard_t), RC_ALIGNOF(rc_lboard_t), 0);
|
||||
self->lboards[i].lboard = lboard;
|
||||
|
||||
rc_reset_lboard(lboard);
|
||||
return RC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* item has not been previously registered, determine how much space we need for it, and allocate it */
|
||||
size = rc_lboard_size(memaddr);
|
||||
if (size < 0)
|
||||
return size;
|
||||
|
||||
lboard_buffer = malloc(size);
|
||||
if (!lboard_buffer)
|
||||
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);
|
||||
|
||||
if (parse.offset < 0) {
|
||||
free(lboard_buffer);
|
||||
*self->next_memref = NULL; /* disassociate any memrefs allocated by the failed parse */
|
||||
return parse.offset;
|
||||
}
|
||||
|
||||
/* if at least one memref was allocated within the trigger, we can't free the buffer when the trigger is deactivated */
|
||||
owns_memref = (*self->next_memref != NULL);
|
||||
if (owns_memref) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* grow the lboard buffer if necessary */
|
||||
if (self->lboard_count == self->lboard_capacity) {
|
||||
self->lboard_capacity += 16;
|
||||
if (!self->lboards)
|
||||
self->lboards = (rc_runtime_lboard_t*)malloc(self->lboard_capacity * sizeof(rc_runtime_lboard_t));
|
||||
else
|
||||
self->lboards = (rc_runtime_lboard_t*)realloc(self->lboards, self->lboard_capacity * sizeof(rc_runtime_lboard_t));
|
||||
}
|
||||
|
||||
/* assign the new lboard */
|
||||
self->lboards[self->lboard_count].id = id;
|
||||
self->lboards[self->lboard_count].value = 0;
|
||||
self->lboards[self->lboard_count].lboard = lboard;
|
||||
self->lboards[self->lboard_count].buffer = lboard_buffer;
|
||||
memcpy(self->lboards[self->lboard_count].md5, md5, 16);
|
||||
self->lboards[self->lboard_count].owns_memrefs = owns_memref;
|
||||
++self->lboard_count;
|
||||
|
||||
/* reset it, and return it */
|
||||
lboard->memrefs = NULL;
|
||||
rc_reset_lboard(lboard);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, unsigned id)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < self->lboard_count; ++i) {
|
||||
if (self->lboards[i].id == id && self->lboards[i].lboard != NULL)
|
||||
return self->lboards[i].lboard;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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_richpresence_display_t* display;
|
||||
rc_parse_state_t parse;
|
||||
int size;
|
||||
|
||||
if (script == NULL)
|
||||
return RC_MISSING_DISPLAY_STRING;
|
||||
|
||||
size = rc_richpresence_size(script);
|
||||
if (size < 0)
|
||||
return size;
|
||||
|
||||
if (!self->richpresence_display_buffer) {
|
||||
self->richpresence_display_buffer = (char*)malloc(RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE * sizeof(char));
|
||||
if (!self->richpresence_display_buffer)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
}
|
||||
self->richpresence_display_buffer[0] = '\0';
|
||||
|
||||
previous = self->richpresence;
|
||||
if (previous) {
|
||||
if (!previous->owns_memrefs) {
|
||||
free(previous->buffer);
|
||||
previous = previous->previous;
|
||||
}
|
||||
}
|
||||
|
||||
self->richpresence = (rc_runtime_richpresence_t*)malloc(sizeof(rc_runtime_richpresence_t));
|
||||
if (!self->richpresence)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
self->richpresence->previous = previous;
|
||||
self->richpresence->owns_memrefs = 0;
|
||||
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;
|
||||
rc_parse_richpresence_internal(richpresence, script, &parse);
|
||||
rc_destroy_parse_state(&parse);
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
/* if at least one memref was allocated within the rich presence, we can't free the buffer when the rich presence is deactivated */
|
||||
self->richpresence->owns_memrefs = (*self->next_memref != NULL);
|
||||
if (self->richpresence->owns_memrefs) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
richpresence->memrefs = NULL;
|
||||
self->richpresence_update_timer = 0;
|
||||
|
||||
if (!richpresence->first_display || !richpresence->first_display->display) {
|
||||
/* non-existant rich presence, treat like static empty string */
|
||||
*self->richpresence_display_buffer = '\0';
|
||||
self->richpresence->richpresence = NULL;
|
||||
}
|
||||
else if (richpresence->first_display->next || /* has conditional display strings */
|
||||
richpresence->first_display->display->next || /* has macros */
|
||||
richpresence->first_display->display->value.conditions) { /* is only a macro */
|
||||
/* dynamic rich presence - reset all of the conditions */
|
||||
display = richpresence->first_display;
|
||||
while (display != NULL) {
|
||||
rc_reset_trigger(&display->trigger);
|
||||
display = display->next;
|
||||
}
|
||||
|
||||
rc_evaluate_richpresence(self->richpresence->richpresence, self->richpresence_display_buffer, RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1, NULL, NULL, L);
|
||||
}
|
||||
else {
|
||||
/* static rich presence - copy the static string */
|
||||
const char* src = richpresence->first_display->display->text;
|
||||
char* dst = self->richpresence_display_buffer;
|
||||
const char* end = dst + RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1;
|
||||
while (*src && dst < end)
|
||||
*dst++ = *src++;
|
||||
*dst = '\0';
|
||||
|
||||
/* by setting self->richpresence to null, it won't be evaluated in do_frame() */
|
||||
self->richpresence = NULL;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
const char* rc_runtime_get_richpresence(const rc_runtime_t* self)
|
||||
{
|
||||
if (self->richpresence_display_buffer)
|
||||
return self->richpresence_display_buffer;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
rc_runtime_event_t runtime_event;
|
||||
unsigned i;
|
||||
|
||||
runtime_event.value = 0;
|
||||
|
||||
rc_update_memref_values(self->memrefs, peek, ud);
|
||||
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
rc_trigger_t* trigger = self->triggers[i].trigger;
|
||||
int trigger_state;
|
||||
|
||||
if (!trigger)
|
||||
continue;
|
||||
|
||||
trigger_state = trigger->state;
|
||||
|
||||
switch (rc_evaluate_trigger(trigger, peek, ud, L))
|
||||
{
|
||||
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:
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
break;
|
||||
|
||||
case RC_TRIGGER_STATE_PAUSED:
|
||||
if (trigger_state != RC_TRIGGER_STATE_PAUSED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_TRIGGER_STATE_PRIMED:
|
||||
if (trigger_state != RC_TRIGGER_STATE_PRIMED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_TRIGGER_STATE_ACTIVE:
|
||||
if (trigger_state != RC_TRIGGER_STATE_ACTIVE) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < self->lboard_count; ++i) {
|
||||
rc_lboard_t* lboard = self->lboards[i].lboard;
|
||||
int lboard_state;
|
||||
|
||||
if (!lboard)
|
||||
continue;
|
||||
|
||||
lboard_state = lboard->state;
|
||||
switch (rc_evaluate_lboard(lboard, &runtime_event.value, peek, ud, L))
|
||||
{
|
||||
case RC_LBOARD_STATE_STARTED: /* leaderboard is running */
|
||||
if (lboard_state != RC_LBOARD_STATE_STARTED) {
|
||||
self->lboards[i].value = runtime_event.value;
|
||||
|
||||
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_STARTED;
|
||||
runtime_event.id = self->lboards[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
else if (runtime_event.value != self->lboards[i].value) {
|
||||
self->lboards[i].value = runtime_event.value;
|
||||
|
||||
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_UPDATED;
|
||||
runtime_event.id = self->lboards[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_LBOARD_STATE_CANCELED:
|
||||
if (lboard_state != RC_LBOARD_STATE_CANCELED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_CANCELED;
|
||||
runtime_event.id = self->lboards[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_LBOARD_STATE_TRIGGERED:
|
||||
if (lboard_state != RC_RUNTIME_EVENT_LBOARD_TRIGGERED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_TRIGGERED;
|
||||
runtime_event.id = self->lboards[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->richpresence && self->richpresence->richpresence) {
|
||||
if (self->richpresence_update_timer == 0) {
|
||||
/* generate into a temporary buffer so we don't get a partially updated string if it's read while its being updated */
|
||||
char buffer[RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE];
|
||||
int len = rc_evaluate_richpresence(self->richpresence->richpresence, buffer, RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1, peek, ud, L);
|
||||
|
||||
/* copy into the real buffer - write the 0 terminator first to ensure reads don't overflow the buffer */
|
||||
if (len > 0) {
|
||||
buffer[RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE - 1] = '\0';
|
||||
memcpy(self->richpresence_display_buffer, buffer, RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/* schedule the next update for 60 frames later - most systems use a 60 fps framerate (some use more 50 or 75)
|
||||
* since we're only sending to the server every two minutes, that's only every 7200 frames while active, which
|
||||
* is evenly divisible by 50, 60, and 75.
|
||||
*/
|
||||
self->richpresence_update_timer = 59;
|
||||
}
|
||||
else {
|
||||
self->richpresence_update_timer--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rc_runtime_reset(rc_runtime_t* self) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
if (self->triggers[i].trigger)
|
||||
rc_reset_trigger(self->triggers[i].trigger);
|
||||
}
|
||||
|
||||
for (i = 0; i < self->lboard_count; ++i) {
|
||||
if (self->lboards[i].lboard)
|
||||
rc_reset_lboard(self->lboards[i].lboard);
|
||||
}
|
||||
|
||||
if (self->richpresence) {
|
||||
rc_richpresence_display_t* display = self->richpresence->richpresence->first_display;
|
||||
while (display != 0) {
|
||||
rc_reset_trigger(&display->trigger);
|
||||
display = display->next;
|
||||
}
|
||||
}
|
||||
}
|
445
deps/rcheevos/src/rcheevos/runtime_progress.c
vendored
Normal file
445
deps/rcheevos/src/rcheevos/runtime_progress.c
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include "../rhash/md5.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define RC_RUNTIME_MARKER 0x0A504152 /* RAP\n */
|
||||
|
||||
#define RC_RUNTIME_CHUNK_MEMREFS 0x4645524D /* MREF */
|
||||
#define RC_RUNTIME_CHUNK_ACHIEVEMENT 0x56484341 /* ACHV */
|
||||
|
||||
#define RC_RUNTIME_CHUNK_DONE 0x454E4F44 /* DONE */
|
||||
|
||||
typedef struct rc_runtime_progress_t {
|
||||
rc_runtime_t* runtime;
|
||||
|
||||
int offset;
|
||||
unsigned char* buffer;
|
||||
|
||||
int chunk_size_offset;
|
||||
|
||||
lua_State* L;
|
||||
} rc_runtime_progress_t;
|
||||
|
||||
#define RC_TRIGGER_STATE_UNUPDATED 0x7F
|
||||
|
||||
#define RC_MEMREF_FLAG_PREV_IS_PRIOR 0x00010000
|
||||
|
||||
static void rc_runtime_progress_write_uint(rc_runtime_progress_t* progress, unsigned value)
|
||||
{
|
||||
if (progress->buffer) {
|
||||
progress->buffer[progress->offset + 0] = value & 0xFF; value >>= 8;
|
||||
progress->buffer[progress->offset + 1] = value & 0xFF; value >>= 8;
|
||||
progress->buffer[progress->offset + 2] = value & 0xFF; value >>= 8;
|
||||
progress->buffer[progress->offset + 3] = value & 0xFF;
|
||||
}
|
||||
|
||||
progress->offset += 4;
|
||||
}
|
||||
|
||||
static unsigned rc_runtime_progress_read_uint(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned value = progress->buffer[progress->offset + 0] |
|
||||
(progress->buffer[progress->offset + 1] << 8) |
|
||||
(progress->buffer[progress->offset + 2] << 16) |
|
||||
(progress->buffer[progress->offset + 3] << 24);
|
||||
|
||||
progress->offset += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void rc_runtime_progress_write_md5(rc_runtime_progress_t* progress, unsigned char* md5)
|
||||
{
|
||||
if (progress->buffer)
|
||||
memcpy(&progress->buffer[progress->offset], md5, 16);
|
||||
|
||||
progress->offset += 16;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_match_md5(rc_runtime_progress_t* progress, unsigned char* md5)
|
||||
{
|
||||
int result = 0;
|
||||
if (progress->buffer)
|
||||
result = (memcmp(&progress->buffer[progress->offset], md5, 16) == 0);
|
||||
|
||||
progress->offset += 16;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void rc_runtime_progress_start_chunk(rc_runtime_progress_t* progress, unsigned chunk_id)
|
||||
{
|
||||
rc_runtime_progress_write_uint(progress, chunk_id);
|
||||
|
||||
progress->chunk_size_offset = progress->offset;
|
||||
|
||||
progress->offset += 4;
|
||||
}
|
||||
|
||||
static void rc_runtime_progress_end_chunk(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned length;
|
||||
int offset;
|
||||
|
||||
progress->offset = (progress->offset + 3) & ~0x03; /* align to 4 byte boundary */
|
||||
|
||||
if (progress->buffer) {
|
||||
/* ignore chunk size field when calculating chunk size */
|
||||
length = (unsigned)(progress->offset - progress->chunk_size_offset - 4);
|
||||
|
||||
/* temporarily update the write pointer to write the chunk size field */
|
||||
offset = progress->offset;
|
||||
progress->offset = progress->chunk_size_offset;
|
||||
rc_runtime_progress_write_uint(progress, length);
|
||||
progress->offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_runtime_progress_init(rc_runtime_progress_t* progress, rc_runtime_t* runtime, lua_State* L)
|
||||
{
|
||||
memset(progress, 0, sizeof(rc_runtime_progress_t));
|
||||
progress->runtime = runtime;
|
||||
progress->L = L;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress)
|
||||
{
|
||||
rc_memref_value_t* memref = progress->runtime->memrefs;
|
||||
unsigned int flags = 0;
|
||||
|
||||
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_MEMREFS);
|
||||
|
||||
while (memref) {
|
||||
flags = memref->memref.size;
|
||||
if (memref->previous == memref->prior)
|
||||
flags |= RC_MEMREF_FLAG_PREV_IS_PRIOR;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, memref->memref.address);
|
||||
rc_runtime_progress_write_uint(progress, flags);
|
||||
rc_runtime_progress_write_uint(progress, memref->value);
|
||||
rc_runtime_progress_write_uint(progress, memref->prior);
|
||||
|
||||
memref = memref->next;
|
||||
}
|
||||
|
||||
rc_runtime_progress_end_chunk(progress);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned entries;
|
||||
unsigned address, flags, value, prior;
|
||||
char size;
|
||||
rc_memref_value_t* memref;
|
||||
|
||||
/* re-read the chunk size to determine how many memrefs are present */
|
||||
progress->offset -= 4;
|
||||
entries = rc_runtime_progress_read_uint(progress) / 16;
|
||||
|
||||
while (entries != 0) {
|
||||
address = rc_runtime_progress_read_uint(progress);
|
||||
flags = rc_runtime_progress_read_uint(progress);
|
||||
value = rc_runtime_progress_read_uint(progress);
|
||||
prior = rc_runtime_progress_read_uint(progress);
|
||||
|
||||
size = flags & 0xFF;
|
||||
|
||||
memref = progress->runtime->memrefs;
|
||||
while (memref) {
|
||||
if (memref->memref.address == address && memref->memref.size == size) {
|
||||
memref->value = value;
|
||||
memref->previous = (flags & RC_MEMREF_FLAG_PREV_IS_PRIOR) ? prior : value;
|
||||
memref->prior = prior;
|
||||
}
|
||||
|
||||
memref = memref->next;
|
||||
}
|
||||
|
||||
--entries;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
|
||||
{
|
||||
rc_condition_t* cond;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, condset->is_paused);
|
||||
|
||||
cond = condset->conditions;
|
||||
while (cond) {
|
||||
rc_runtime_progress_write_uint(progress, cond->current_hits);
|
||||
rc_runtime_progress_write_uint(progress, cond->is_true);
|
||||
|
||||
cond = cond->next;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
|
||||
{
|
||||
rc_condition_t* cond;
|
||||
|
||||
condset->is_paused = rc_runtime_progress_read_uint(progress);
|
||||
|
||||
cond = condset->conditions;
|
||||
while (cond) {
|
||||
cond->current_hits = rc_runtime_progress_read_uint(progress);
|
||||
cond->is_true = rc_runtime_progress_read_uint(progress) & 0xFF;
|
||||
|
||||
cond = cond->next;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_trigger(rc_runtime_progress_t* progress, rc_trigger_t* trigger)
|
||||
{
|
||||
rc_condset_t* condset;
|
||||
int result;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, trigger->state);
|
||||
rc_runtime_progress_write_uint(progress, trigger->measured_value);
|
||||
|
||||
if (trigger->requirement) {
|
||||
result = rc_runtime_progress_write_condset(progress, trigger->requirement);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
condset = trigger->alternative;
|
||||
while (condset)
|
||||
{
|
||||
result = rc_runtime_progress_write_condset(progress, condset);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
condset = condset->next;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_trigger(rc_runtime_progress_t* progress, rc_trigger_t* trigger)
|
||||
{
|
||||
rc_condset_t* condset;
|
||||
int result;
|
||||
|
||||
trigger->state = rc_runtime_progress_read_uint(progress);
|
||||
trigger->measured_value = rc_runtime_progress_read_uint(progress);
|
||||
|
||||
if (trigger->requirement) {
|
||||
result = rc_runtime_progress_read_condset(progress, trigger->requirement);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
condset = trigger->alternative;
|
||||
while (condset)
|
||||
{
|
||||
result = rc_runtime_progress_read_condset(progress, condset);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
condset = condset->next;
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned i;
|
||||
int result;
|
||||
|
||||
for (i = 0; i < progress->runtime->trigger_count; ++i)
|
||||
{
|
||||
rc_runtime_trigger_t* runtime_trigger = &progress->runtime->triggers[i];
|
||||
if (!runtime_trigger->trigger)
|
||||
continue;
|
||||
|
||||
switch (runtime_trigger->trigger->state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
/* don't store state for inactive or triggered achievements */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_ACHIEVEMENT);
|
||||
rc_runtime_progress_write_uint(progress, runtime_trigger->id);
|
||||
rc_runtime_progress_write_md5(progress, runtime_trigger->md5);
|
||||
|
||||
result = rc_runtime_progress_write_trigger(progress, runtime_trigger->trigger);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
rc_runtime_progress_end_chunk(progress);
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_read_achievement(rc_runtime_progress_t* progress)
|
||||
{
|
||||
unsigned id = rc_runtime_progress_read_uint(progress);
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < progress->runtime->trigger_count; ++i) {
|
||||
rc_runtime_trigger_t* runtime_trigger = &progress->runtime->triggers[i];
|
||||
if (runtime_trigger->id == id && runtime_trigger->trigger != NULL) {
|
||||
/* ignore triggered and waiting achievements */
|
||||
if (runtime_trigger->trigger->state == RC_TRIGGER_STATE_UNUPDATED) {
|
||||
/* only update state if definition hasn't changed (md5 matches) */
|
||||
if (rc_runtime_progress_match_md5(progress, runtime_trigger->md5))
|
||||
return rc_runtime_progress_read_trigger(progress, runtime_trigger->trigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_runtime_progress_serialize_internal(rc_runtime_progress_t* progress)
|
||||
{
|
||||
md5_state_t state;
|
||||
unsigned char md5[16];
|
||||
int result;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, RC_RUNTIME_MARKER);
|
||||
|
||||
if ((result = rc_runtime_progress_write_memrefs(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
if ((result = rc_runtime_progress_write_achievements(progress)) != RC_OK)
|
||||
return result;
|
||||
|
||||
rc_runtime_progress_write_uint(progress, RC_RUNTIME_CHUNK_DONE);
|
||||
rc_runtime_progress_write_uint(progress, 16);
|
||||
|
||||
if (progress->buffer) {
|
||||
md5_init(&state);
|
||||
md5_append(&state, progress->buffer, progress->offset);
|
||||
md5_finish(&state, md5);
|
||||
}
|
||||
|
||||
rc_runtime_progress_write_md5(progress, md5);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L)
|
||||
{
|
||||
rc_runtime_progress_t progress;
|
||||
int result;
|
||||
|
||||
rc_runtime_progress_init(&progress, (rc_runtime_t*)runtime, L);
|
||||
|
||||
result = rc_runtime_progress_serialize_internal(&progress);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
return progress.offset;
|
||||
}
|
||||
|
||||
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L)
|
||||
{
|
||||
rc_runtime_progress_t progress;
|
||||
|
||||
rc_runtime_progress_init(&progress, (rc_runtime_t*)runtime, L);
|
||||
progress.buffer = (unsigned char*)buffer;
|
||||
|
||||
return rc_runtime_progress_serialize_internal(&progress);
|
||||
}
|
||||
|
||||
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char* serialized, lua_State* L)
|
||||
{
|
||||
rc_runtime_progress_t progress;
|
||||
md5_state_t state;
|
||||
unsigned char md5[16];
|
||||
unsigned chunk_id;
|
||||
unsigned chunk_size;
|
||||
unsigned next_chunk_offset;
|
||||
unsigned i;
|
||||
int result = RC_OK;
|
||||
|
||||
rc_runtime_progress_init(&progress, runtime, L);
|
||||
progress.buffer = (unsigned char*)serialized;
|
||||
|
||||
if (rc_runtime_progress_read_uint(&progress) != RC_RUNTIME_MARKER) {
|
||||
rc_runtime_reset(runtime);
|
||||
return RC_INVALID_STATE;
|
||||
}
|
||||
|
||||
for (i = 0; i < runtime->trigger_count; ++i) {
|
||||
rc_runtime_trigger_t* runtime_trigger = &runtime->triggers[i];
|
||||
if (runtime_trigger->trigger) {
|
||||
switch (runtime_trigger->trigger->state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
/* don't update state for inactive or triggered achievements */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* mark active achievements as unupdated. anything that's still unupdated
|
||||
* after deserializing the progress will be reset to waiting */
|
||||
runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
chunk_id = rc_runtime_progress_read_uint(&progress);
|
||||
chunk_size = rc_runtime_progress_read_uint(&progress);
|
||||
next_chunk_offset = progress.offset + chunk_size;
|
||||
|
||||
switch (chunk_id)
|
||||
{
|
||||
case RC_RUNTIME_CHUNK_MEMREFS:
|
||||
result = rc_runtime_progress_read_memrefs(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_ACHIEVEMENT:
|
||||
result = rc_runtime_progress_read_achievement(&progress);
|
||||
break;
|
||||
|
||||
case RC_RUNTIME_CHUNK_DONE:
|
||||
md5_init(&state);
|
||||
md5_append(&state, progress.buffer, progress.offset);
|
||||
md5_finish(&state, md5);
|
||||
if (!rc_runtime_progress_match_md5(&progress, md5))
|
||||
result = RC_INVALID_STATE;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (chunk_size & 0xFFFF0000)
|
||||
result = RC_INVALID_STATE; /* assume unknown chunk > 64KB is invalid */
|
||||
break;
|
||||
}
|
||||
|
||||
progress.offset = next_chunk_offset;
|
||||
} while (result == RC_OK && chunk_id != RC_RUNTIME_CHUNK_DONE);
|
||||
|
||||
if (result != RC_OK) {
|
||||
rc_runtime_reset(runtime);
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < runtime->trigger_count; ++i) {
|
||||
rc_trigger_t* trigger = runtime->triggers[i].trigger;
|
||||
if (trigger && trigger->state == RC_TRIGGER_STATE_UNUPDATED)
|
||||
rc_reset_trigger(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
103
deps/rcheevos/src/rcheevos/term.c
vendored
103
deps/rcheevos/src/rcheevos/term.c
vendored
@ -1,103 +0,0 @@
|
||||
#include "internal.h"
|
||||
|
||||
rc_term_t* rc_parse_term(const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
|
||||
rc_term_t* self;
|
||||
const char* aux;
|
||||
char size;
|
||||
int ret2;
|
||||
|
||||
aux = *memaddr;
|
||||
self = RC_ALLOC(rc_term_t, parse);
|
||||
self->invert = 0;
|
||||
|
||||
ret2 = rc_parse_operand(&self->operand1, &aux, 0, is_indirect, parse);
|
||||
|
||||
if (ret2 < 0) {
|
||||
parse->offset = ret2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*aux == '*') {
|
||||
aux++;
|
||||
|
||||
if (*aux == '~') {
|
||||
aux++;
|
||||
self->invert = 1;
|
||||
}
|
||||
|
||||
ret2 = rc_parse_operand(&self->operand2, &aux, 0, is_indirect, parse);
|
||||
|
||||
if (ret2 < 0) {
|
||||
parse->offset = ret2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->invert) {
|
||||
switch (self->operand2.type) {
|
||||
case RC_OPERAND_ADDRESS:
|
||||
case RC_OPERAND_DELTA:
|
||||
case RC_OPERAND_PRIOR:
|
||||
size = self->operand2.value.memref->memref.size;
|
||||
break;
|
||||
default:
|
||||
size = RC_MEMSIZE_32_BITS;
|
||||
break;
|
||||
}
|
||||
|
||||
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:
|
||||
/* invert is already 1 */
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
case RC_MEMSIZE_HIGH:
|
||||
self->invert = 0xf;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
self->invert = 0xffU;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_16_BITS:
|
||||
self->invert = 0xffffU;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_24_BITS:
|
||||
self->invert = 0xffffffU;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_32_BITS:
|
||||
self->invert = 0xffffffffU;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
self->operand2.type = RC_OPERAND_CONST;
|
||||
self->operand2.value.num = 1;
|
||||
}
|
||||
|
||||
*memaddr = aux;
|
||||
return self;
|
||||
}
|
||||
|
||||
int rc_evaluate_term(rc_term_t* self, rc_eval_state_t* eval_state) {
|
||||
/* Operands are usually memory references and are always retrieved as unsigned. The floating
|
||||
* point operand is signed, and will automatically make the result signed. Otherwise, multiply
|
||||
* by the secondary operand (which is usually 1) and cast to signed.
|
||||
*/
|
||||
unsigned value = rc_evaluate_operand(&self->operand1, eval_state);
|
||||
|
||||
if (self->operand2.type != RC_OPERAND_FP) {
|
||||
return (int)(value * (rc_evaluate_operand(&self->operand2, eval_state) ^ self->invert));
|
||||
}
|
||||
|
||||
return (int)((double)value * self->operand2.value.dbl);
|
||||
}
|
72
deps/rcheevos/src/rcheevos/trigger.c
vendored
72
deps/rcheevos/src/rcheevos/trigger.c
vendored
@ -1,10 +1,7 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#if !defined( __CELLOS_LV2__) && !defined(__MWERKS__)
|
||||
#include <memory.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <string.h> /* memset */
|
||||
|
||||
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse) {
|
||||
rc_condset_t** next;
|
||||
@ -36,7 +33,7 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
|
||||
|
||||
next = &(*next)->next;
|
||||
}
|
||||
|
||||
|
||||
*next = 0;
|
||||
*memaddr = aux;
|
||||
|
||||
@ -62,7 +59,7 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
|
||||
rc_trigger_t* self;
|
||||
rc_parse_state_t parse;
|
||||
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
|
||||
|
||||
|
||||
self = RC_ALLOC(rc_trigger_t, &parse);
|
||||
rc_init_parse_state_memrefs(&parse, &self->memrefs);
|
||||
|
||||
@ -92,14 +89,15 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
rc_condset_t* condset;
|
||||
int ret;
|
||||
char is_paused;
|
||||
char is_primed;
|
||||
|
||||
/* previously triggered, do nothing - return INACTIVE so caller doesn't report a repeated trigger */
|
||||
if (self->state == RC_TRIGGER_STATE_TRIGGERED)
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
|
||||
rc_update_memref_values(self->memrefs, peek, ud);
|
||||
|
||||
/* not yet active, only update the memrefs - so deltas are corrent when it becomes active */
|
||||
/* not yet active, only update the memrefs - so deltas are correct when it becomes active */
|
||||
if (self->state == RC_TRIGGER_STATE_INACTIVE)
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
|
||||
@ -109,22 +107,41 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
eval_state.peek_userdata = ud;
|
||||
eval_state.L = L;
|
||||
|
||||
ret = self->requirement != 0 ? rc_test_condset(self->requirement, &eval_state) : 1;
|
||||
condset = self->alternative;
|
||||
if (self->requirement != NULL) {
|
||||
ret = rc_test_condset(self->requirement, &eval_state);
|
||||
is_paused = self->requirement->is_paused;
|
||||
is_primed = eval_state.primed;
|
||||
} else {
|
||||
ret = 1;
|
||||
is_paused = 0;
|
||||
is_primed = 1;
|
||||
}
|
||||
|
||||
condset = self->alternative;
|
||||
if (condset) {
|
||||
int sub = 0;
|
||||
char sub_paused = 1;
|
||||
char sub_primed = 0;
|
||||
|
||||
do {
|
||||
sub |= rc_test_condset(condset, &eval_state);
|
||||
condset = condset->next;
|
||||
}
|
||||
while (condset != 0);
|
||||
sub_paused &= condset->is_paused;
|
||||
sub_primed |= eval_state.primed;
|
||||
|
||||
condset = condset->next;
|
||||
} while (condset != 0);
|
||||
|
||||
/* to trigger, the core must be true and at least one alt must be true */
|
||||
ret &= sub;
|
||||
is_primed &= sub_primed;
|
||||
|
||||
/* if the core is not paused, all alts must be paused to count as a paused trigger */
|
||||
is_paused |= sub_paused;
|
||||
}
|
||||
|
||||
self->measured_value = eval_state.measured_value;
|
||||
/* if paused, the measured value may not be captured, keep the old value */
|
||||
if (!is_paused)
|
||||
self->measured_value = eval_state.measured_value;
|
||||
|
||||
/* if the state is WAITING and the trigger is ready to fire, ignore it and reset the hit counts */
|
||||
/* otherwise, if the state is WAITING, proceed to activating the trigger */
|
||||
@ -138,6 +155,10 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
/* if any ResetIf condition was true, reset the hit counts */
|
||||
rc_reset_trigger_hitcounts(self);
|
||||
|
||||
/* if the measured value came from a hit count, reset it too */
|
||||
if (eval_state.measured_from_hits)
|
||||
self->measured_value = 0;
|
||||
|
||||
/* if there were hit counts to clear, return RESET, but don't change the state */
|
||||
if (self->has_hits) {
|
||||
self->has_hits = 0;
|
||||
@ -146,6 +167,7 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
|
||||
/* any hits that were tallied were just reset */
|
||||
eval_state.has_hits = 0;
|
||||
is_primed = 0;
|
||||
}
|
||||
else if (ret) {
|
||||
/* trigger was triggered */
|
||||
@ -156,20 +178,16 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
/* did not trigger this frame - update the information we'll need for next time */
|
||||
self->has_hits = eval_state.has_hits;
|
||||
|
||||
/* check to see if the trigger is paused */
|
||||
is_paused = (self->requirement != NULL) ? self->requirement->is_paused : 0;
|
||||
if (!is_paused) {
|
||||
/* if the core is not paused, all alts must be paused to count as a paused trigger */
|
||||
is_paused = (self->alternative != NULL);
|
||||
for (condset = self->alternative; condset != NULL; condset = condset->next) {
|
||||
if (!condset->is_paused) {
|
||||
is_paused = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_paused) {
|
||||
self->state = RC_TRIGGER_STATE_PAUSED;
|
||||
}
|
||||
else if (is_primed) {
|
||||
self->state = RC_TRIGGER_STATE_PRIMED;
|
||||
}
|
||||
else {
|
||||
self->state = RC_TRIGGER_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
self->state = is_paused ? RC_TRIGGER_STATE_PAUSED : RC_TRIGGER_STATE_ACTIVE;
|
||||
return self->state;
|
||||
}
|
||||
|
||||
@ -184,4 +202,6 @@ void rc_reset_trigger(rc_trigger_t* self) {
|
||||
rc_reset_trigger_hitcounts(self);
|
||||
|
||||
self->state = RC_TRIGGER_STATE_WAITING;
|
||||
self->measured_value = 0;
|
||||
self->has_hits = 0;
|
||||
}
|
||||
|
169
deps/rcheevos/src/rcheevos/value.c
vendored
169
deps/rcheevos/src/rcheevos/value.c
vendored
@ -1,9 +1,7 @@
|
||||
#include "internal.h"
|
||||
|
||||
#if !defined( __CELLOS_LV2__) && !defined(__MWERKS__)
|
||||
#include <memory.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <string.h> /* memset */
|
||||
#include <ctype.h> /* isdigit */
|
||||
|
||||
static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
|
||||
rc_condition_t** next;
|
||||
@ -12,12 +10,10 @@ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse
|
||||
|
||||
has_measured = 0;
|
||||
in_add_address = 0;
|
||||
self->expressions = 0;
|
||||
|
||||
/* this largely duplicates rc_parse_condset, but we cannot call it directly, as we need to check the
|
||||
/* this largely duplicates rc_parse_condset, but we cannot call it directly, as we need to check the
|
||||
* type of each condition as we go */
|
||||
self->conditions = RC_ALLOC(rc_condset_t, parse);
|
||||
self->conditions->next = 0;
|
||||
self->conditions->has_pause = 0;
|
||||
|
||||
next = &self->conditions->conditions;
|
||||
@ -49,7 +45,7 @@ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse
|
||||
return;
|
||||
}
|
||||
has_measured = 1;
|
||||
if ((*next)->required_hits == 0 && (*next)->oper != RC_CONDITION_NONE)
|
||||
if ((*next)->required_hits == 0 && (*next)->oper != RC_OPERATOR_NONE)
|
||||
(*next)->required_hits = (unsigned)-1;
|
||||
break;
|
||||
|
||||
@ -69,42 +65,125 @@ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse
|
||||
(*memaddr)++;
|
||||
}
|
||||
|
||||
*next = 0;
|
||||
|
||||
if (!has_measured) {
|
||||
parse->offset = RC_MISSING_VALUE_MEASURED;
|
||||
}
|
||||
|
||||
if (parse->buffer) {
|
||||
*next = 0;
|
||||
self->conditions->next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
|
||||
rc_expression_t** next;
|
||||
void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
|
||||
rc_condition_t** next;
|
||||
rc_condset_t** next_clause;
|
||||
rc_condition_t* cond;
|
||||
char buffer[64] = "A:";
|
||||
const char* buffer_ptr;
|
||||
char* ptr;
|
||||
int end_of_clause;
|
||||
|
||||
/* if it starts with a condition flag (M: A: B: C:), parse the conditions */
|
||||
if ((*memaddr)[1] == ':') {
|
||||
rc_parse_cond_value(self, memaddr, parse);
|
||||
return;
|
||||
}
|
||||
/* convert legacy format into condset */
|
||||
self->conditions = RC_ALLOC(rc_condset_t, parse);
|
||||
self->conditions->has_pause = 0;
|
||||
|
||||
self->conditions = 0;
|
||||
next = &self->expressions;
|
||||
next = &self->conditions->conditions;
|
||||
next_clause = &self->conditions->next;
|
||||
|
||||
for (;;) {
|
||||
*next = rc_parse_expression(memaddr, parse);
|
||||
ptr = &buffer[2];
|
||||
end_of_clause = 0;
|
||||
|
||||
do {
|
||||
switch (**memaddr) {
|
||||
case '_': /* add next */
|
||||
case '$': /* maximum of */
|
||||
case '\0': /* end of string */
|
||||
case ':': /* end of leaderboard clause */
|
||||
case ')': /* end of rich presence macro */
|
||||
end_of_clause = 1;
|
||||
*ptr = '\0';
|
||||
break;
|
||||
|
||||
case '*':
|
||||
*ptr++ = '*';
|
||||
|
||||
buffer_ptr = *memaddr + 1;
|
||||
if (*buffer_ptr == '-') {
|
||||
/* negative value automatically needs prefix, 'f' handles both float and digits, so use it */
|
||||
*ptr++ = 'f';
|
||||
}
|
||||
else {
|
||||
/* if it looks like a floating point number, add the 'f' prefix */
|
||||
while (isdigit(*buffer_ptr))
|
||||
++buffer_ptr;
|
||||
if (*buffer_ptr == '.')
|
||||
*ptr++ = 'f';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
*ptr++ = **memaddr;
|
||||
break;
|
||||
}
|
||||
|
||||
++(*memaddr);
|
||||
} while (!end_of_clause);
|
||||
|
||||
buffer_ptr = buffer;
|
||||
cond = rc_parse_condition(&buffer_ptr, parse, 0);
|
||||
if (parse->offset < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
next = &(*next)->next;
|
||||
switch (cond->oper) {
|
||||
case RC_OPERATOR_MULT:
|
||||
case RC_OPERATOR_DIV:
|
||||
case RC_OPERATOR_AND:
|
||||
case RC_OPERATOR_NONE:
|
||||
break;
|
||||
|
||||
if (**memaddr != '$') {
|
||||
break;
|
||||
default:
|
||||
parse->offset = RC_INVALID_OPERATOR;
|
||||
return;
|
||||
}
|
||||
|
||||
(*memaddr)++;
|
||||
}
|
||||
cond->pause = 0;
|
||||
*next = cond;
|
||||
|
||||
*next = 0;
|
||||
switch ((*memaddr)[-1]) {
|
||||
case '_': /* add next */
|
||||
next = &cond->next;
|
||||
break;
|
||||
|
||||
case '$': /* max of */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = 0;
|
||||
*next_clause = RC_ALLOC(rc_condset_t, parse);
|
||||
(*next_clause)->has_pause = 0;
|
||||
next = &(*next_clause)->conditions;
|
||||
next_clause = &(*next_clause)->next;
|
||||
break;
|
||||
|
||||
default: /* end of valid string */
|
||||
--(*memaddr); /* undo the increment we performed when copying the string */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = 0;
|
||||
*next_clause = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
|
||||
/* if it starts with a condition flag (M: A: B: C:), parse the conditions */
|
||||
if ((*memaddr)[1] == ':') {
|
||||
rc_parse_cond_value(self, memaddr, parse);
|
||||
}
|
||||
else {
|
||||
rc_parse_legacy_value(self, memaddr, parse);
|
||||
}
|
||||
}
|
||||
|
||||
int rc_value_size(const char* memaddr) {
|
||||
@ -123,7 +202,7 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
|
||||
rc_value_t* self;
|
||||
rc_parse_state_t parse;
|
||||
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
|
||||
|
||||
|
||||
self = RC_ALLOC(rc_value_t, &parse);
|
||||
rc_init_parse_state_memrefs(&parse, &self->memrefs);
|
||||
|
||||
@ -133,26 +212,11 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
|
||||
return parse.offset >= 0 ? self : 0;
|
||||
}
|
||||
|
||||
static int rc_evaluate_expr_value(rc_value_t* self, rc_eval_state_t* eval_state) {
|
||||
rc_expression_t* exp;
|
||||
int value, max;
|
||||
|
||||
exp = self->expressions;
|
||||
max = rc_evaluate_expression(exp, eval_state);
|
||||
|
||||
for (exp = exp->next; exp != 0; exp = exp->next) {
|
||||
value = rc_evaluate_expression(exp, eval_state);
|
||||
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
rc_eval_state_t eval_state;
|
||||
rc_condset_t* condset;
|
||||
int result = 0;
|
||||
|
||||
memset(&eval_state, 0, sizeof(eval_state));
|
||||
eval_state.peek = peek;
|
||||
eval_state.peek_userdata = ud;
|
||||
@ -160,10 +224,17 @@ int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L)
|
||||
|
||||
rc_update_memref_values(self->memrefs, peek, ud);
|
||||
|
||||
if (self->expressions) {
|
||||
return rc_evaluate_expr_value(self, &eval_state);
|
||||
rc_test_condset(self->conditions, &eval_state);
|
||||
result = (int)eval_state.measured_value;
|
||||
|
||||
condset = self->conditions->next;
|
||||
while (condset != NULL) {
|
||||
rc_test_condset(condset, &eval_state);
|
||||
if ((int)eval_state.measured_value > result)
|
||||
result = (int)eval_state.measured_value;
|
||||
|
||||
condset = condset->next;
|
||||
}
|
||||
|
||||
rc_test_condset(self->conditions, &eval_state);
|
||||
return (int)eval_state.measured_value;
|
||||
return result;
|
||||
}
|
||||
|
1491
deps/rcheevos/src/rhash/hash.c
vendored
Normal file
1491
deps/rcheevos/src/rhash/hash.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18
deps/rcheevos/src/rhash/md5.h
vendored
Normal file
18
deps/rcheevos/src/rhash/md5.h
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef RC_MD5_H
|
||||
#define RC_MD5_H
|
||||
|
||||
/* NOTE: this is NOT the md5.h included in the rcheevos repository. It provides the same
|
||||
* functionality using code already present in RetroArch */
|
||||
|
||||
/* android build has libretro-common/include in path, but not the base directory.
|
||||
* other builds prioritize rcheevos/include over libretro-common/include.
|
||||
* to ensure we get the correct include file, use a complicated relative path */
|
||||
#include <../../../libretro-common/include/rhash.h>
|
||||
|
||||
#define md5_state_t MD5_CTX
|
||||
#define md5_byte_t unsigned char
|
||||
#define md5_init(state) MD5_Init(state)
|
||||
#define md5_append(state, buffer, size) MD5_Update(state, buffer, size)
|
||||
#define md5_finish(state, hash) MD5_Final(hash, state)
|
||||
|
||||
#endif
|
203
deps/rcheevos/src/rurl/url.c
vendored
203
deps/rcheevos/src/rurl/url.c
vendored
@ -1,15 +1,7 @@
|
||||
#include "rurl.h"
|
||||
|
||||
#ifdef RARCH_INTERNAL
|
||||
#include <rhash.h> /* libretro-common/include/rhash.h */
|
||||
#define md5_state_t MD5_CTX
|
||||
#define md5_byte_t unsigned char
|
||||
#define md5_init(state) MD5_Init(state)
|
||||
#define md5_append(state, buffer, size) MD5_Update(state, buffer, size)
|
||||
#define md5_finish(state, hash) MD5_Final(hash, state)
|
||||
#else
|
||||
#include "..\rhash\md5.h"
|
||||
#endif
|
||||
#include "../rcheevos/compat.h"
|
||||
#include "../rhash/md5.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -25,37 +17,41 @@ static int rc_url_encode(char* encoded, size_t len, const char* str) {
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
case '-': case '_': case '.': case '~':
|
||||
if (len >= 2) {
|
||||
*encoded++ = *str++;
|
||||
len--;
|
||||
}
|
||||
else {
|
||||
if (len < 2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*encoded++ = *str++;
|
||||
--len;
|
||||
break;
|
||||
|
||||
|
||||
case ' ':
|
||||
if (len < 2)
|
||||
return -1;
|
||||
|
||||
*encoded++ = '+';
|
||||
++str;
|
||||
--len;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (len >= 4) {
|
||||
snprintf(encoded, len, "%%%02x", (unsigned char)*str);
|
||||
encoded += 3;
|
||||
str++;
|
||||
len -= 3;
|
||||
}
|
||||
else {
|
||||
if (len < 4)
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(encoded, len, "%%%02x", (unsigned char)*str);
|
||||
encoded += 3;
|
||||
++str;
|
||||
len -= 3;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
|
||||
case '\0':
|
||||
*encoded = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore) {
|
||||
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token,
|
||||
unsigned cheevo_id, int hardcore, const char* game_hash) {
|
||||
char urle_user_name[64];
|
||||
char urle_login_token[64];
|
||||
int written;
|
||||
@ -63,11 +59,11 @@ int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
@ -78,10 +74,14 @@ int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const
|
||||
hardcore ? 1 : 0
|
||||
);
|
||||
|
||||
if (game_hash && strlen(game_hash) == 32 && (size - (size_t)written) >= 35) {
|
||||
written += snprintf(buffer + written, size - (size_t)written, "&m=%s", game_hash);
|
||||
}
|
||||
|
||||
return (size_t)written >= size ? -1 : 0;
|
||||
}
|
||||
|
||||
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value, const char* game_hash) {
|
||||
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value) {
|
||||
char urle_user_name[64];
|
||||
char urle_login_token[64];
|
||||
char signature[64];
|
||||
@ -92,7 +92,7 @@ int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@ -115,20 +115,15 @@ int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const
|
||||
hash[ 8], hash[ 9], hash[10], hash[11],hash[12], hash[13], hash[14], hash[15]
|
||||
);
|
||||
|
||||
if (game_hash && strlen(game_hash) == 32 && (size - (size_t)written) >= 35) {
|
||||
written += snprintf(buffer + written, size - (size_t)written, "&m=%s", game_hash);
|
||||
}
|
||||
|
||||
return (size_t)written >= size ? -1 : 0;
|
||||
}
|
||||
|
||||
int rc_url_get_gameid(char* buffer, size_t size, unsigned char hash[16]) {
|
||||
int rc_url_get_gameid(char* buffer, size_t size, const char* hash) {
|
||||
int written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
"http://retroachievements.org/dorequest.php?r=gameid&m=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
hash[ 0], hash[ 1], hash[ 2], hash[ 3], hash[ 4], hash[ 5], hash[ 6], hash[ 7],
|
||||
hash[ 8], hash[ 9], hash[10], hash[11],hash[12], hash[13], hash[14], hash[15]
|
||||
"http://retroachievements.org/dorequest.php?r=gameid&m=%s",
|
||||
hash
|
||||
);
|
||||
|
||||
return (size_t)written >= size ? -1 : 0;
|
||||
@ -142,11 +137,11 @@ int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const cha
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
@ -178,11 +173,11 @@ int rc_url_login_with_password(char* buffer, size_t size, const char* user_name,
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_password, sizeof(urle_password), password) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
@ -202,11 +197,11 @@ int rc_url_login_with_token(char* buffer, size_t size, const char* user_name, co
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
@ -226,11 +221,11 @@ int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, con
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
@ -252,11 +247,11 @@ int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const
|
||||
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
written = snprintf(
|
||||
buffer,
|
||||
size,
|
||||
@ -268,3 +263,111 @@ int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const
|
||||
|
||||
return (size_t)written >= size ? -1 : 0;
|
||||
}
|
||||
|
||||
static int rc_url_append_param_equals(char* buffer, size_t buffer_size, size_t buffer_offset, const char* param)
|
||||
{
|
||||
int written = 0;
|
||||
size_t param_len;
|
||||
|
||||
if (buffer_offset >= buffer_size)
|
||||
return -1;
|
||||
|
||||
if (buffer_offset) {
|
||||
buffer += buffer_offset;
|
||||
buffer_size -= buffer_offset;
|
||||
|
||||
if (buffer[-1] != '?') {
|
||||
*buffer++ = '&';
|
||||
buffer_size--;
|
||||
written = 1;
|
||||
}
|
||||
}
|
||||
|
||||
param_len = strlen(param);
|
||||
if (param_len + 1 >= buffer_size)
|
||||
return -1;
|
||||
memcpy(buffer, param, param_len);
|
||||
buffer[param_len] = '=';
|
||||
|
||||
written += (int)param_len + 1;
|
||||
return written + (int)buffer_offset;
|
||||
}
|
||||
|
||||
static int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, unsigned value)
|
||||
{
|
||||
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
|
||||
if (written > 0) {
|
||||
char num[16];
|
||||
int chars = sprintf(num, "%u", value);
|
||||
|
||||
if (chars + written < (int)buffer_size)
|
||||
{
|
||||
memcpy(&buffer[written], num, chars + 1);
|
||||
*buffer_offset = written + chars;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rc_url_append_str(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, const char* value)
|
||||
{
|
||||
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
|
||||
if (written > 0)
|
||||
{
|
||||
buffer += written;
|
||||
buffer_size -= written;
|
||||
|
||||
if (rc_url_encode(buffer, buffer_size, value) == 0)
|
||||
{
|
||||
written += (int)strlen(buffer);
|
||||
*buffer_offset = written;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rc_url_build_dorequest(char* url_buffer, size_t url_buffer_size, size_t* buffer_offset,
|
||||
const char* api, const char* user_name)
|
||||
{
|
||||
const char* base_url = "http://retroachievements.org/dorequest.php";
|
||||
size_t written = strlen(base_url);
|
||||
int failure = 0;
|
||||
|
||||
if (url_buffer_size < written + 1)
|
||||
return -1;
|
||||
memcpy(url_buffer, base_url, written);
|
||||
url_buffer[written++] = '?';
|
||||
|
||||
failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "r", api);
|
||||
failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "u", user_name);
|
||||
|
||||
*buffer_offset += written;
|
||||
return failure;
|
||||
}
|
||||
|
||||
int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
|
||||
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence)
|
||||
{
|
||||
size_t written = 0;
|
||||
int failure = rc_url_build_dorequest(url_buffer, url_buffer_size, &written, "ping", user_name);
|
||||
failure |= rc_url_append_unum(url_buffer, url_buffer_size, &written, "g", gameid);
|
||||
|
||||
written = 0;
|
||||
failure |= rc_url_append_str(post_buffer, post_buffer_size, &written, "t", login_token);
|
||||
|
||||
if (rich_presence && *rich_presence)
|
||||
failure |= rc_url_append_str(post_buffer, post_buffer_size, &written, "m", rich_presence);
|
||||
|
||||
if (failure) {
|
||||
if (url_buffer_size)
|
||||
url_buffer[0] = '\0';
|
||||
if (post_buffer_size)
|
||||
post_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
return failure;
|
||||
}
|
||||
|
@ -181,17 +181,20 @@ ACHIEVEMENTS
|
||||
#include "../cheevos-new/parser.c"
|
||||
|
||||
#include "../deps/rcheevos/src/rcheevos/alloc.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/compat.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/condition.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/condset.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/expression.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/consoleinfo.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/format.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/lboard.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/memref.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/operand.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/term.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/richpresence.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/runtime.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/runtime_progress.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/trigger.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/value.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/memref.c"
|
||||
#include "../deps/rcheevos/src/rcheevos/richpresence.c"
|
||||
#include "../deps/rcheevos/src/rhash/hash.c"
|
||||
#include "../deps/rcheevos/src/rurl/url.c"
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user