3rdparty: Update rcheevos to v12.5.0

This commit is contained in:
JordanTheToaster
2026-09-19 13:52:30 -04:00
committed by Ty
parent 64358ac8ea
commit 23f357479b
22 changed files with 318 additions and 99 deletions
+7
View File
@@ -1,3 +1,10 @@
# v12.5.0
* fallback to file hashing for buffered m3u/cue files
* fix crash caused by {recall} of constant appearing in multiple alts
* fix validation warning message when redundancy appears after AndNext/OrNext chain
* fix handling of SubSource chain starting with a float constant
* fix several potential issues with malformed data
# v12.4.0
* add avatar_last_updated field to rc_client_user_t and rc_api_login_response_t
* add more fields to fetch_games_list API
+1
View File
@@ -94,6 +94,7 @@ enum {
RC_CONSOLE_TI83 = 79,
RC_CONSOLE_UZEBOX = 80,
RC_CONSOLE_FAMICOM_DISK_SYSTEM = 81,
RC_CONSOLE_PLAYSTATION_3 = 82,
RC_CONSOLE_HUBS = 100,
RC_CONSOLE_EVENTS = 101,
+142 -28
View File
@@ -710,10 +710,15 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel
}
*out = dst = (char*)rc_buffer_reserve(buffer, len - 1); /* -2 for quotes, +1 for null terminator */
if (!dst)
return 0;
do {
while (src < field->value_end && *src != '\"') {
if (*src == '\\') {
++src;
if (src >= field->value_end)
return 0;
if (*src == 'n') {
/* newline */
++src;
@@ -731,7 +736,7 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel
if (*src == 'u') {
/* unicode character */
uint32_t ucs32_char;
if (src + 5 >= field->value_end) /* incomplete unicode character */
if ((size_t)(field->value_end - src) < 6) /* incomplete unicode character */
return 0;
ucs32_char = rc_json_decode_hex4(src + 1);
@@ -739,7 +744,7 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel
if (ucs32_char >= 0xD800 && ucs32_char < 0xE000) {
/* surrogate lead - look for surrogate tail */
if (ucs32_char < 0xDC00 && src[0] == '\\' && src[1] == 'u') {
if (ucs32_char < 0xDC00 && (size_t)(field->value_end - src) >= 6 && src[0] == '\\' && src[1] == 'u') {
const uint32_t surrogate = rc_json_decode_hex4(src + 2);
src += 6;
@@ -770,10 +775,16 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel
}
*dst++ = *src++;
} while (*src != '\"');
}
if (src >= field->value_end)
return 0;
} else {
*out = dst = (char*)rc_buffer_reserve(buffer, len + 1); /* +1 for null terminator */
if (!dst)
return 0;
memcpy(dst, src, len);
dst += len;
}
@@ -990,6 +1001,116 @@ int rc_json_get_required_float(float* out, rc_api_response_t* response, const rc
return rc_json_missing_field(response, field);
}
/* Parses exactly count decimal digits without reading beyond end. */
static int rc_json_parse_datetime_digits(const char** input, const char* end, int count, int* value) {
int result = 0;
while (count > 0) {
if (*input >= end || **input < '0' || **input > '9')
return 0;
result *= 10;
result += (**input - '0');
(*input)++;
count--;
}
*value = result;
return 1;
}
/* Consumes an expected separator without reading beyond end. */
static int rc_json_match_datetime_char(const char** input, const char* end, char expected) {
if (*input >= end || **input != expected)
return 0;
(*input)++;
return 1;
}
/* Valid suffixes are Z, fractional seconds, and numeric timezone offsets. */
static int rc_json_validate_datetime_suffix(const char* input, const char* end) {
int offset_hour;
int offset_minute;
if (input == end)
return 1;
if (*input == '.') {
input++;
if (input == end || *input < '0' || *input > '9')
return 0;
do {
input++;
} while (input < end && *input >= '0' && *input <= '9');
if (input == end)
return 1;
}
if (*input == 'Z')
return (++input == end);
if (*input != '+' && *input != '-')
return 0;
++input;
if (!rc_json_parse_datetime_digits(&input, end, 2, &offset_hour) ||
!rc_json_match_datetime_char(&input, end, ':') ||
!rc_json_parse_datetime_digits(&input, end, 2, &offset_minute) ||
input != end) {
return 0;
}
return (offset_hour <= 23 && offset_minute <= 59);
}
/* Parses YYYY-MM-DD[ T]HH:MM:SS and only updates tm after the complete
* bounded value, including any suffix, has been validated. */
static int rc_json_parse_datetime(const char* input, const char* end, struct tm* tm) {
static const unsigned char days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int year, month, day, hour, minute, second;
int days_in_month;
if (!rc_json_parse_datetime_digits(&input, end, 4, &year) ||
!rc_json_match_datetime_char(&input, end, '-') ||
!rc_json_parse_datetime_digits(&input, end, 2, &month) ||
!rc_json_match_datetime_char(&input, end, '-') ||
!rc_json_parse_datetime_digits(&input, end, 2, &day) ||
input >= end || (*input != ' ' && *input != 'T')) {
return 0;
}
input++;
if (!rc_json_parse_datetime_digits(&input, end, 2, &hour) ||
!rc_json_match_datetime_char(&input, end, ':') ||
!rc_json_parse_datetime_digits(&input, end, 2, &minute) ||
!rc_json_match_datetime_char(&input, end, ':') ||
!rc_json_parse_datetime_digits(&input, end, 2, &second) ||
!rc_json_validate_datetime_suffix(input, end)) {
return 0;
}
if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59)
return 0;
days_in_month = days_per_month[month - 1];
if (month == 2 && (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0))
days_in_month++;
if (day < 1 || day > days_in_month)
return 0;
memset(tm, 0, sizeof(*tm));
tm->tm_year = year - 1900;
tm->tm_mon = month - 1;
tm->tm_mday = day;
tm->tm_hour = hour;
tm->tm_min = minute;
tm->tm_sec = second;
return 1;
}
int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char* field_name) {
struct tm tm;
@@ -1000,32 +1121,25 @@ int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char*
(void)field_name;
#endif
if (field->value_start && *field->value_start == '\"') {
memset(&tm, 0, sizeof(tm));
if (sscanf_s(field->value_start + 1, "%d-%d-%d %d:%d:%d", /* DB format "2013-10-20 22:12:21" */
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6 ||
/* NOTE: relies on sscanf stopping when it sees a non-digit after the seconds. could be 'Z', '.', '+', or '-' */
sscanf_s(field->value_start + 1, "%d-%d-%dT%d:%d:%d", /* ISO format "2013-10-20T22:12:21.000000Z */
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
tm.tm_mon--; /* 0-based */
tm.tm_year -= 1900; /* 1900 based */
if (field->value_start && field->value_end > field->value_start &&
*field->value_start == '\"' && field->value_end[-1] == '\"' &&
rc_json_parse_datetime(field->value_start + 1, field->value_end - 1, &tm)) {
/* mktime converts a struct tm to a time_t using the local timezone.
* the input string is UTC. since timegm is not universally cross-platform,
* figure out the offset between UTC and local time by applying the
* timezone conversion twice and manually removing the difference */
{
time_t local_timet = mktime(&tm);
time_t skewed_timet, tz_offset;
struct tm gmt_tm;
gmtime_s(&gmt_tm, &local_timet);
skewed_timet = mktime(&gmt_tm); /* applies local time adjustment second time */
tz_offset = skewed_timet - local_timet;
*out = local_timet - tz_offset;
}
return 1;
/* mktime converts a struct tm to a time_t using the local timezone.
* the input string is UTC. since timegm is not universally cross-platform,
* figure out the offset between UTC and local time by applying the
* timezone conversion twice and manually removing the difference */
{
time_t local_timet = mktime(&tm);
time_t skewed_timet, tz_offset;
struct tm gmt_tm;
gmtime_s(&gmt_tm, &local_timet);
skewed_timet = mktime(&gmt_tm); /* applies local time adjustment second time */
tz_offset = skewed_timet - local_timet;
*out = local_timet - tz_offset;
}
return 1;
}
*out = 0;
+7 -5
View File
@@ -140,7 +140,7 @@ static int rc_api_process_fetch_game_data_achievements(rc_api_response_t* respon
while (rc_json_get_array_entry_object(achievement_fields, sizeof(achievement_fields) / sizeof(achievement_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&achievement->id, response, &achievement_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&achievement->title, response, &achievement_fields[1], "Title"))
if (!rc_json_get_required_string(&achievement->title, response, &achievement_fields[1], "Title") || !achievement->title)
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&achievement->description, response, &achievement_fields[2], "Description"))
return RC_MISSING_VALUE;
@@ -197,16 +197,18 @@ static int rc_api_process_fetch_game_data_achievements(rc_api_response_t* respon
achievement->type = RC_ACHIEVEMENT_TYPE_STANDARD;
/* legacy support : if title contains[m], change type to missable and remove[m] from title */
if (memcmp(achievement->title, "[m]", 3) == 0) {
/* since we're comparing a suffix, calculate the length in case it contains null bytes */
len = strlen(achievement->title);
if (len >= 3 && memcmp(achievement->title, "[m]", 3) == 0) {
len = 3;
while (achievement->title[len] == ' ')
++len;
achievement->title += len;
achievement->type = RC_ACHIEVEMENT_TYPE_MISSABLE;
}
else if (achievement_fields[1].value_end && memcmp(achievement_fields[1].value_end - 4, "[m]", 3) == 0) {
len = strlen(achievement->title) - 3;
while (achievement->title[len - 1] == ' ')
else if (len >= 3 && memcmp(achievement->title + len - 3, "[m]", 3) == 0) {
len -= 3;
while (len > 0 && achievement->title[len - 1] == ' ')
--len;
((char*)achievement->title)[len] = '\0';
achievement->type = RC_ACHIEVEMENT_TYPE_MISSABLE;
+1 -1
View File
@@ -455,7 +455,7 @@ int rc_api_process_fetch_all_user_progress_server_response(rc_api_fetch_all_user
entry = response->entries;
while (rc_json_get_next_object_field(&iterator, &field))
{
entry->game_id = strtol(field.name, &end, 10);
entry->game_id = (uint32_t)strtol(field.name, &end, 10);
field.name = "";
if (!rc_json_get_required_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]),
+2 -2
View File
@@ -318,8 +318,6 @@ void rc_client_enable_logging(rc_client_t* client, int level, rc_client_message_
/* ===== Common ===== */
static rc_clock_t rc_client_clock_get_now_millisecs(const rc_client_t* client)
{
#if defined(__APPLE__) && defined(__MACH__)
#ifdef CLOCK_MONOTONIC
/* clock_gettime() was added to Darwin in iOS 10.0 and macOS 10.12.
@@ -336,6 +334,8 @@ static rc_clock_t rc_client_clock_get_now_millisecs(const rc_client_t* client)
#endif
#endif
static rc_clock_t rc_client_clock_get_now_millisecs(const rc_client_t* client)
{
#if defined(CLOCK_MONOTONIC)
struct timespec now;
(void)client;
+2 -2
View File
@@ -1,4 +1,4 @@
#if !defined(RC_NO_THREADS) && !defined(_WIN32) && !defined(GEKKO) && !defined(_3DS) && (!defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0) < 500)
#if !defined(RC_NO_THREADS) && !defined(_WIN32) && !defined(GEKKO) && !defined(__3DS__) && !defined(_3DS) && (!defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0) < 500)
/* We'll want to use pthread_mutexattr_settype/PTHREAD_MUTEX_RECURSIVE, but glibc only conditionally exposes pthread_mutexattr_settype and PTHREAD_MUTEX_RECURSIVE depending on feature flags
* Defining _XOPEN_SOURCE must be done at the top of the source file, before including any headers
* pthread_mutexattr_settype/PTHREAD_MUTEX_RECURSIVE are specified the Single UNIX Specification (Version 2, 1997), along with POSIX later on (IEEE Standard 1003.1-2008), so should cover practically any pthread implementation
@@ -197,7 +197,7 @@ void rc_mutex_unlock(rc_mutex_t* mutex)
LWP_MutexUnlock(mutex->handle);
}
#elif defined(_3DS)
#elif defined(__3DS__) || defined(_3DS)
void rc_mutex_init(rc_mutex_t* mutex)
{
+1 -1
View File
@@ -102,7 +102,7 @@ RC_BEGIN_C_DECLS
typedef struct rc_mutex_t {
mutex_t handle;
} rc_mutex_t;
#elif defined(_3DS)
#elif defined(__3DS__) || defined(_3DS)
#include <3ds/synchronization.h>
typedef RecursiveLock rc_mutex_t;
#else
+44 -37
View File
@@ -25,6 +25,7 @@ typedef struct rc_disallowed_core_settings_t
{
const char* library_name;
const rc_disallowed_setting_t* disallowed_settings;
uint32_t system_id;
} rc_disallowed_core_settings_t;
@@ -100,6 +101,11 @@ static const rc_disallowed_setting_t _rc_disallowed_gpgx_wide_settings[] = {
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_melonds_ds_settings[] = {
{ "melonds_console_mode", "dsi" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_mesen_settings[] = {
{ "mesen_region", ",PAL,Dendy" },
{ NULL, NULL }
@@ -166,32 +172,33 @@ static const rc_disallowed_setting_t _rc_disallowed_virtual_jaguar_settings[] =
};
static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
{ "Beetle PSX", _rc_disallowed_beetle_psx_settings },
{ "Beetle PSX HW", _rc_disallowed_beetle_psx_hw_settings },
{ "bsnes-mercury", _rc_disallowed_bsnes_settings },
{ "cap32", _rc_disallowed_cap32_settings },
{ "dolphin-emu", _rc_disallowed_dolphin_settings },
{ "DOSBox-pure", _rc_disallowed_dosbox_pure_settings },
{ "DuckStation", _rc_disallowed_duckstation_settings },
{ "ecwolf", _rc_disallowed_ecwolf_settings },
{ "FCEUmm", _rc_disallowed_fceumm_settings },
{ "FinalBurn Neo", _rc_disallowed_fbneo_settings },
{ "Flycast", _rc_disallowed_flycast_settings },
{ "Genesis Plus GX", _rc_disallowed_gpgx_settings },
{ "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings },
{ "Mesen", _rc_disallowed_mesen_settings },
{ "Mesen-S", _rc_disallowed_mesen_s_settings },
{ "NeoCD", _rc_disallowed_neocd_settings },
{ "PPSSPP", _rc_disallowed_ppsspp_settings },
{ "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings },
{ "PicoDrive", _rc_disallowed_picodrive_settings },
{ "QUASI88", _rc_disallowed_quasi88_settings },
{ "SMS Plus GX", _rc_disallowed_smsplus_settings },
{ "Snes9x", _rc_disallowed_snes9x_settings },
{ "SwanStation", _rc_disallowed_swanstation_settings },
{ "VICE x64", _rc_disallowed_vice_settings },
{ "Virtual Jaguar", _rc_disallowed_virtual_jaguar_settings },
{ NULL, NULL }
{ "Beetle PSX", _rc_disallowed_beetle_psx_settings, 0 },
{ "Beetle PSX HW", _rc_disallowed_beetle_psx_hw_settings, 0 },
{ "bsnes-mercury", _rc_disallowed_bsnes_settings, 0 },
{ "cap32", _rc_disallowed_cap32_settings, 0 },
{ "dolphin-emu", _rc_disallowed_dolphin_settings, 0 },
{ "DOSBox-pure", _rc_disallowed_dosbox_pure_settings, 0 },
{ "DuckStation", _rc_disallowed_duckstation_settings, 0 },
{ "ecwolf", _rc_disallowed_ecwolf_settings, 0 },
{ "FCEUmm", _rc_disallowed_fceumm_settings, 0 },
{ "FinalBurn Neo", _rc_disallowed_fbneo_settings, 0 },
{ "Flycast", _rc_disallowed_flycast_settings, 0 },
{ "Genesis Plus GX", _rc_disallowed_gpgx_settings, 0 },
{ "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings, 0 },
{ "melonDS", _rc_disallowed_melonds_ds_settings, RC_CONSOLE_NINTENDO_DS },
{ "Mesen", _rc_disallowed_mesen_settings, 0 },
{ "Mesen-S", _rc_disallowed_mesen_s_settings, 0 },
{ "NeoCD", _rc_disallowed_neocd_settings, 0 },
{ "PPSSPP", _rc_disallowed_ppsspp_settings, 0 },
{ "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings, 0 },
{ "PicoDrive", _rc_disallowed_picodrive_settings, 0 },
{ "QUASI88", _rc_disallowed_quasi88_settings, 0 },
{ "SMS Plus GX", _rc_disallowed_smsplus_settings, 0 },
{ "Snes9x", _rc_disallowed_snes9x_settings, 0 },
{ "SwanStation", _rc_disallowed_swanstation_settings, 0 },
{ "VICE x64", _rc_disallowed_vice_settings, 0 },
{ "Virtual Jaguar", _rc_disallowed_virtual_jaguar_settings, 0 },
{ NULL, NULL, 0 }
};
static int rc_libretro_string_equal_nocase_wildcard(const char* test, const char* match) {
@@ -230,7 +237,7 @@ static int rc_libretro_match_token(const char* val, const char* token, size_t si
}
}
if (memcmp(token, val, size) == 0 && val[size] == 0) {
if (strncmp(token, val, size) == 0 && val[size] == '\0') {
/* exact match, match with result = true */
*result = 1;
return 1;
@@ -292,13 +299,13 @@ int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_set
key_len = strlen(key);
if (key[key_len - 1] == '*') {
if (memcmp(setting, key, key_len - 1) == 0) {
if (strncmp(setting, key, key_len - 1) == 0) {
if (rc_libretro_match_value(value, disallowed_settings->value))
return 0;
}
}
else {
if (memcmp(setting, key, key_len + 1) == 0) {
if (strcmp(setting, key) == 0) {
if (rc_libretro_match_value(value, disallowed_settings->value))
return 0;
}
@@ -308,16 +315,14 @@ int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_set
return 1;
}
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name) {
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings_for_system(const char* library_name, uint32_t system_id) {
const rc_disallowed_core_settings_t* core_filter = rc_disallowed_core_settings;
size_t library_name_length;
if (!library_name || !library_name[0])
return NULL;
library_name_length = strlen(library_name) + 1;
while (core_filter->library_name) {
if (memcmp(core_filter->library_name, library_name, library_name_length) == 0)
if (core_filter->system_id == system_id && strcmp(core_filter->library_name, library_name) == 0)
return core_filter->disallowed_settings;
++core_filter;
@@ -326,6 +331,10 @@ const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* l
return NULL;
}
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name) {
return rc_libretro_get_disallowed_settings_for_system(library_name, 0);
}
typedef struct rc_disallowed_core_systems_t
{
const char* library_name;
@@ -340,15 +349,13 @@ static const rc_disallowed_core_systems_t rc_disallowed_core_systems[] = {
int rc_libretro_is_system_allowed(const char* library_name, uint32_t console_id) {
const rc_disallowed_core_systems_t* core_filter = rc_disallowed_core_systems;
size_t library_name_length;
size_t i;
if (!library_name || !library_name[0])
return 1;
library_name_length = strlen(library_name) + 1;
while (core_filter->library_name) {
if (memcmp(core_filter->library_name, library_name, library_name_length) == 0) {
if (strcmp(core_filter->library_name, library_name) == 0) {
for (i = 0; i < sizeof(core_filter->disallowed_consoles) / sizeof(core_filter->disallowed_consoles[0]); ++i) {
if (core_filter->disallowed_consoles[i] == console_id)
return 0;
@@ -600,7 +607,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
/* if we need to extract a disconnect bit, the largest block we can read is up to
* the next time that bit flips */
/* https://stackoverflow.com/questions/12247186/find-the-lowest-set-bit */
disconnect_size = (desc->disconnect & -((int)desc->disconnect));
disconnect_size = (uint32_t)(desc->disconnect & -((int)desc->disconnect));
desc_size = disconnect_size - (real_address & (disconnect_size - 1));
}
+2 -1
View File
@@ -25,8 +25,9 @@ typedef struct rc_disallowed_setting_t
} rc_disallowed_setting_t;
RC_EXPORT const rc_disallowed_setting_t* RC_CCONV rc_libretro_get_disallowed_settings(const char* library_name);
RC_EXPORT const rc_disallowed_setting_t* RC_CCONV rc_libretro_get_disallowed_settings_for_system(const char* library_name, uint32_t system_id);
RC_EXPORT int RC_CCONV rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value);
RC_EXPORT int RC_CCONV rc_libretro_is_system_allowed(const char* library_name, uint32_t console_id);
RC_EXPORT int RC_CCONV rc_libretro_is_system_allowed(const char* library_name, uint32_t system_id);
/*****************************************************************************\
| Memory Mapping |
+11
View File
@@ -56,6 +56,11 @@ uint8_t* rc_buffer_reserve(rc_buffer_t* buffer, size_t amount)
{
rc_buffer_chunk_t* chunk = &buffer->chunk;
size_t remaining;
/* we should never need 1GB of space for anything, and avoids any underflow below */
if (amount >= 0x40000000)
return NULL;
while (chunk)
{
remaining = chunk->end - chunk->write;
@@ -112,6 +117,9 @@ void rc_buffer_consume(rc_buffer_t* buffer, const uint8_t* start, uint8_t* end)
void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount)
{
uint8_t* ptr = rc_buffer_reserve(buffer, amount);
if (!ptr)
return NULL;
rc_buffer_consume(buffer, ptr, ptr + amount);
return (void*)ptr;
}
@@ -119,6 +127,9 @@ void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount)
char* rc_buffer_strncpy(rc_buffer_t* buffer, const char* src, size_t len)
{
uint8_t* dst = rc_buffer_reserve(buffer, len + 1);
if (!dst)
return NULL;
memcpy(dst, src, len);
dst[len] = '\0';
rc_buffer_consume(buffer, dst, dst + len + 1);
+1 -1
View File
@@ -8,7 +8,7 @@
RC_BEGIN_C_DECLS
#define RCHEEVOS_VERSION_MAJOR 12
#define RCHEEVOS_VERSION_MINOR 4
#define RCHEEVOS_VERSION_MINOR 5
#define RCHEEVOS_VERSION_PATCH 0
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
+11 -1
View File
@@ -412,8 +412,18 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
parse->addsource_parent.value.memref = (rc_memref_t*)negate;
parse->addsource_parent.size = zero.size;
if (parse->addsource_parent.type == RC_OPERAND_CONST)
if (parse->addsource_parent.type == RC_OPERAND_CONST) {
parse->addsource_parent.value.num = rc_get_modified_memref_value(negate, NULL, NULL);
}
else if (parse->addsource_parent.type == RC_OPERAND_FP) {
rc_typed_value_t typed_value;
typed_value.value.u32 = rc_get_modified_memref_value(negate, NULL, NULL);
parse->addsource_parent.value.dbl = (double)typed_value.value.f32;
}
else {
/* not actually an address, just a non-delta memref read */
parse->addsource_parent.type = parse->addsource_parent.memref_access_type = RC_OPERAND_ADDRESS;
}
}
/* subtract the condition from the chain */
+2 -1
View File
@@ -363,7 +363,8 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
classification = combining_classification;
}
else {
else if (classification != RC_CONDITION_CLASSIFICATION_INDIRECT) {
/* if it's not COMBINING or INDIRECT, reset so it will get recalculated for the next clause */
combining_classification = RC_CONDITION_CLASSIFICATION_COMBINING;
}
+19
View File
@@ -174,6 +174,9 @@ const char* rc_console_name(uint32_t console_id)
case RC_CONSOLE_PLAYSTATION_2:
return "PlayStation 2";
case RC_CONSOLE_PLAYSTATION_3:
return "PlayStation 3";
case RC_CONSOLE_PSP:
return "PlayStation Portable";
@@ -827,6 +830,19 @@ static const rc_memory_region_t _rc_memory_regions_psp[] = {
};
static const rc_memory_regions_t rc_memory_regions_psp = { _rc_memory_regions_psp, 3 };
/* ===== PlayStation 3 ===== */
/* Address layout from RPCS3 vm.cpp _find_map allocation order: */
/* sys_rsx_context_allocate allocates rsx_context first at 0x10000000 (256 MB). */
/* sys_memory_allocate (64K) allocates user64k at 0x20000000 (512 MB). */
/* sys_memory_allocate (1M) allocates user1m at 0x40000000 (256 MB). */
static const rc_memory_region_t _rc_memory_regions_playstation3[] = {
{ 0x00000000U, 0x0FFFFFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Main RAM" },
{ 0x10000000U, 0x2FFFFFFFU, 0x10000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "RSX Context + User RAM (64K pages)" },
{ 0x30000000U, 0x3FFFFFFFU, 0x30000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "User RAM (64K pages)" },
{ 0x40000000U, 0x4FFFFFFFU, 0x40000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "User RAM (1M pages)" },
};
static const rc_memory_regions_t rc_memory_regions_playstation3 = { _rc_memory_regions_playstation3, 4 };
/* ===== Pokemon Mini ===== */
/* https://www.pokemon-mini.net/documentation/memory-map/ */
static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
@@ -1167,6 +1183,9 @@ const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id)
case RC_CONSOLE_PSP:
return &rc_memory_regions_psp;
case RC_CONSOLE_PLAYSTATION_3:
return &rc_memory_regions_playstation3;
case RC_CONSOLE_POKEMON_MINI:
return &rc_memory_regions_pokemini;
+20 -4
View File
@@ -319,13 +319,30 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) {
case RC_OPERAND_FP:
return (left->value.dbl == right->value.dbl);
case RC_OPERAND_RECALL:
return (left->value.memref == right->value.memref);
if (left->memref_access_type != right->memref_access_type)
return 0;
switch (left->memref_access_type) {
case RC_OPERAND_CONST:
return (left->value.num == right->value.num);
case RC_OPERAND_FP:
return (left->value.dbl == right->value.dbl);
default:
if (!left->value.memref || !right->value.memref) /* could not find remember */
return 0;
break; /* fallthrough to memref comparison */
}
default:
break;
}
/* comparing two memrefs - look for exact matches on type and size */
if (left->size != right->size || left->value.memref->value.memref_type != right->value.memref->value.memref_type)
if (left->size != right->size)
return 0;
if (left->value.memref == right->value.memref) /* quick match if pointing to same memref */
return 1;
if (left->value.memref->value.memref_type != right->value.memref->value.memref_type)
return 0;
switch (left->value.memref->value.memref_type) {
@@ -336,8 +353,7 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) {
return (left_memref->modifier_type == right_memref->modifier_type &&
left_memref->depth == right_memref->depth &&
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier) &&
rc_operands_are_equal(&left_memref->parent, &right_memref->parent) &&
1 == 1
rc_operands_are_equal(&left_memref->parent, &right_memref->parent)
);
}
+4 -4
View File
@@ -127,7 +127,7 @@ RC_ALLOW_ALIGN(char)
/* force alignment to 4 bytes on 32-bit systems, or 8 bytes on 64-bit systems */
#define RC_ALIGN(n) (((n) + (sizeof(void*)-1)) & ~(sizeof(void*)-1))
typedef struct {
typedef struct rc_scratch_t {
rc_buffer_t buffer;
rc_scratch_string_t* strings;
@@ -188,7 +188,7 @@ enum {
RC_VALUE_TYPE_FLOAT
};
typedef struct {
typedef struct rc_typed_value_t {
union {
uint32_t u32;
int32_t i32;
@@ -208,7 +208,7 @@ enum {
#define RC_MEASURED_UNKNOWN 0xFFFFFFFF
#define RC_OPERAND_NONE 0xFF
typedef struct {
typedef struct rc_eval_state_t {
/* memory accessors */
rc_peek_t peek;
void* peek_userdata;
@@ -236,7 +236,7 @@ typedef struct {
}
rc_eval_state_t;
typedef struct {
typedef struct rc_parse_state_t {
int32_t offset;
void* buffer;
+3 -1
View File
@@ -1210,8 +1210,10 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co
/* combining field didn't match, or there's more unmatched combining fields. ignore this condition */
if (!chain_matches || rc_validate_is_combining_condition(compare_condition)) {
while (compare_condition->next && rc_validate_is_combining_condition(compare_condition))
while (compare_condition->next && rc_validate_is_combining_condition(compare_condition)) {
++state->cond_index;
compare_condition = compare_condition->next;
}
continue;
}
+1 -1
View File
@@ -802,7 +802,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part
value.value.u32 = '?';
tmp[chars++] = (char)value.value.u32;
if (chars == sizeof(tmp) || !part->next || part->next->display_type != RC_FORMAT_ASCIICHAR)
if (chars == sizeof(tmp) - 1 || !part->next || part->next->display_type != RC_FORMAT_ASCIICHAR)
break;
part = part->next;
+7 -1
View File
@@ -159,8 +159,14 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
/* if it looks like a floating point number, add the 'f' prefix */
while (isdigit((unsigned char)*buffer_ptr))
++buffer_ptr;
if (*buffer_ptr == '.')
if (*buffer_ptr == '.') {
if (ptr == &buffer[sizeof(buffer)]) {
parse->offset = RC_INVALID_VALUE;
return;
}
*ptr++ = 'f';
}
continue;
default:
+29 -8
View File
@@ -503,6 +503,19 @@ static int rc_hash_from_buffer(char hash[33], uint32_t console_id, const rc_hash
{
switch (console_id) {
default:
if (iterator->path) {
const char* ext = rc_path_get_extension(iterator->path);
if (strcasecmp(ext, "cue") == 0 ||
strcasecmp(ext, "m3u") == 0 ||
strcasecmp(ext, "iso") == 0 ||
strcasecmp(ext, "chd") == 0) {
/* These extensions are associated to CD media. If buffered data
* was provided, ignore it and try to open the media directly. */
if (rc_hash_from_file(hash, console_id, iterator))
return 1;
}
}
return rc_hash_iterator_error_formatted(iterator, "Unsupported console for buffer hash: %d", console_id);
case RC_CONSOLE_AMSTRAD_PC:
@@ -949,6 +962,12 @@ static int rc_hash_from_file(char hash[33], uint32_t console_id, const rc_hash_i
case RC_CONSOLE_PSP:
return rc_hash_psp(hash, iterator);
case RC_CONSOLE_PLAYSTATION_3:
if (rc_path_compare_extension(path, "m3u"))
return rc_hash_generate_from_playlist(hash, console_id, iterator);
return rc_hash_ps3(hash, iterator);
case RC_CONSOLE_SEGA_CD:
case RC_CONSOLE_SATURN:
if (rc_path_compare_extension(path, "m3u"))
@@ -1064,10 +1083,11 @@ static void rc_hash_initialize_iterator_chd(rc_hash_iterator_t* iterator, int da
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[4] = RC_CONSOLE_PSP;
iterator->consoles[5] = RC_CONSOLE_PC_ENGINE_CD;
iterator->consoles[6] = RC_CONSOLE_3DO;
iterator->consoles[7] = RC_CONSOLE_NEO_GEO_CD;
iterator->consoles[8] = RC_CONSOLE_PCFX;
iterator->consoles[5] = RC_CONSOLE_PLAYSTATION_3;
iterator->consoles[6] = RC_CONSOLE_PC_ENGINE_CD;
iterator->consoles[7] = RC_CONSOLE_3DO;
iterator->consoles[8] = RC_CONSOLE_NEO_GEO_CD;
iterator->consoles[9] = RC_CONSOLE_PCFX;
}
static void rc_hash_initialize_iterator_cue(rc_hash_iterator_t* iterator, int data) {
@@ -1137,10 +1157,11 @@ static void rc_hash_initialize_iterator_iso(rc_hash_iterator_t* iterator, int da
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[1] = RC_CONSOLE_PSP;
iterator->consoles[2] = RC_CONSOLE_3DO;
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[4] = RC_CONSOLE_GAMECUBE;
iterator->consoles[5] = RC_CONSOLE_WII;
iterator->consoles[2] = RC_CONSOLE_PLAYSTATION_3;
iterator->consoles[3] = RC_CONSOLE_3DO;
iterator->consoles[4] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[5] = RC_CONSOLE_GAMECUBE;
iterator->consoles[6] = RC_CONSOLE_WII;
}
static void rc_hash_initialize_iterator_m3u(rc_hash_iterator_t* iterator, int data) {
+1
View File
@@ -95,6 +95,7 @@ int rc_hash_buffered_file(char hash[33], uint32_t console_id, const rc_hash_iter
int rc_hash_psx(char hash[33], const rc_hash_iterator_t* iterator);
int rc_hash_ps2(char hash[33], const rc_hash_iterator_t* iterator);
int rc_hash_psp(char hash[33], const rc_hash_iterator_t* iterator);
int rc_hash_ps3(char hash[33], const rc_hash_iterator_t* iterator);
int rc_hash_sega_cd(char hash[33], const rc_hash_iterator_t* iterator);
int rc_hash_wii(char hash[33], const rc_hash_iterator_t* iterator);
#endif