diff --git a/3rdparty/rcheevos/CHANGELOG.md b/3rdparty/rcheevos/CHANGELOG.md index cb7111580b..db443c3300 100644 --- a/3rdparty/rcheevos/CHANGELOG.md +++ b/3rdparty/rcheevos/CHANGELOG.md @@ -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 diff --git a/3rdparty/rcheevos/include/rc_consoles.h b/3rdparty/rcheevos/include/rc_consoles.h index 5f788855b1..1625cf2174 100644 --- a/3rdparty/rcheevos/include/rc_consoles.h +++ b/3rdparty/rcheevos/include/rc_consoles.h @@ -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, diff --git a/3rdparty/rcheevos/src/rapi/rc_api_common.c b/3rdparty/rcheevos/src/rapi/rc_api_common.c index 75db8f41f0..3b3528605b 100644 --- a/3rdparty/rcheevos/src/rapi/rc_api_common.c +++ b/3rdparty/rcheevos/src/rapi/rc_api_common.c @@ -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; diff --git a/3rdparty/rcheevos/src/rapi/rc_api_runtime.c b/3rdparty/rcheevos/src/rapi/rc_api_runtime.c index afecabbac9..261741d55a 100644 --- a/3rdparty/rcheevos/src/rapi/rc_api_runtime.c +++ b/3rdparty/rcheevos/src/rapi/rc_api_runtime.c @@ -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; diff --git a/3rdparty/rcheevos/src/rapi/rc_api_user.c b/3rdparty/rcheevos/src/rapi/rc_api_user.c index 9970e56c18..6943c98c44 100644 --- a/3rdparty/rcheevos/src/rapi/rc_api_user.c +++ b/3rdparty/rcheevos/src/rapi/rc_api_user.c @@ -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]), diff --git a/3rdparty/rcheevos/src/rc_client.c b/3rdparty/rcheevos/src/rc_client.c index 24de1bf14b..7a7399580e 100644 --- a/3rdparty/rcheevos/src/rc_client.c +++ b/3rdparty/rcheevos/src/rc_client.c @@ -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; diff --git a/3rdparty/rcheevos/src/rc_compat.c b/3rdparty/rcheevos/src/rc_compat.c index 0ab9e4dda5..4658c18d08 100644 --- a/3rdparty/rcheevos/src/rc_compat.c +++ b/3rdparty/rcheevos/src/rc_compat.c @@ -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) { diff --git a/3rdparty/rcheevos/src/rc_compat.h b/3rdparty/rcheevos/src/rc_compat.h index 3f24df2efd..3b788e2e77 100644 --- a/3rdparty/rcheevos/src/rc_compat.h +++ b/3rdparty/rcheevos/src/rc_compat.h @@ -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 diff --git a/3rdparty/rcheevos/src/rc_libretro.c b/3rdparty/rcheevos/src/rc_libretro.c index e04c8d2f4f..bb681d928b 100644 --- a/3rdparty/rcheevos/src/rc_libretro.c +++ b/3rdparty/rcheevos/src/rc_libretro.c @@ -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)); } diff --git a/3rdparty/rcheevos/src/rc_libretro.h b/3rdparty/rcheevos/src/rc_libretro.h index 8821303218..fe96a40d41 100644 --- a/3rdparty/rcheevos/src/rc_libretro.h +++ b/3rdparty/rcheevos/src/rc_libretro.h @@ -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 | diff --git a/3rdparty/rcheevos/src/rc_util.c b/3rdparty/rcheevos/src/rc_util.c index 95de413dcb..3efdf04da4 100644 --- a/3rdparty/rcheevos/src/rc_util.c +++ b/3rdparty/rcheevos/src/rc_util.c @@ -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); diff --git a/3rdparty/rcheevos/src/rc_version.h b/3rdparty/rcheevos/src/rc_version.h index 7eaa32a862..a2c0544d92 100644 --- a/3rdparty/rcheevos/src/rc_version.h +++ b/3rdparty/rcheevos/src/rc_version.h @@ -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) diff --git a/3rdparty/rcheevos/src/rcheevos/condition.c b/3rdparty/rcheevos/src/rcheevos/condition.c index 63986812e3..eb57a1cba3 100644 --- a/3rdparty/rcheevos/src/rcheevos/condition.c +++ b/3rdparty/rcheevos/src/rcheevos/condition.c @@ -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 */ diff --git a/3rdparty/rcheevos/src/rcheevos/condset.c b/3rdparty/rcheevos/src/rcheevos/condset.c index b19d3fd227..c9f984bfff 100644 --- a/3rdparty/rcheevos/src/rcheevos/condset.c +++ b/3rdparty/rcheevos/src/rcheevos/condset.c @@ -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; } diff --git a/3rdparty/rcheevos/src/rcheevos/consoleinfo.c b/3rdparty/rcheevos/src/rcheevos/consoleinfo.c index a7ad9afff4..2acedfdec6 100644 --- a/3rdparty/rcheevos/src/rcheevos/consoleinfo.c +++ b/3rdparty/rcheevos/src/rcheevos/consoleinfo.c @@ -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; diff --git a/3rdparty/rcheevos/src/rcheevos/operand.c b/3rdparty/rcheevos/src/rcheevos/operand.c index ae18d93155..5d9c81a5cf 100644 --- a/3rdparty/rcheevos/src/rcheevos/operand.c +++ b/3rdparty/rcheevos/src/rcheevos/operand.c @@ -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) ); } diff --git a/3rdparty/rcheevos/src/rcheevos/rc_internal.h b/3rdparty/rcheevos/src/rcheevos/rc_internal.h index f5c3b19b33..51e2e234c9 100644 --- a/3rdparty/rcheevos/src/rcheevos/rc_internal.h +++ b/3rdparty/rcheevos/src/rcheevos/rc_internal.h @@ -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; diff --git a/3rdparty/rcheevos/src/rcheevos/rc_validate.c b/3rdparty/rcheevos/src/rcheevos/rc_validate.c index 2d55c2f467..843c2ad30c 100644 --- a/3rdparty/rcheevos/src/rcheevos/rc_validate.c +++ b/3rdparty/rcheevos/src/rcheevos/rc_validate.c @@ -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; } diff --git a/3rdparty/rcheevos/src/rcheevos/richpresence.c b/3rdparty/rcheevos/src/rcheevos/richpresence.c index 2292667301..c6a3393e2a 100644 --- a/3rdparty/rcheevos/src/rcheevos/richpresence.c +++ b/3rdparty/rcheevos/src/rcheevos/richpresence.c @@ -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; diff --git a/3rdparty/rcheevos/src/rcheevos/value.c b/3rdparty/rcheevos/src/rcheevos/value.c index 8876f5bced..9a451c63c8 100644 --- a/3rdparty/rcheevos/src/rcheevos/value.c +++ b/3rdparty/rcheevos/src/rcheevos/value.c @@ -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: diff --git a/3rdparty/rcheevos/src/rhash/hash.c b/3rdparty/rcheevos/src/rhash/hash.c index 451a9a8945..2df5d00b88 100644 --- a/3rdparty/rcheevos/src/rhash/hash.c +++ b/3rdparty/rcheevos/src/rhash/hash.c @@ -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) { diff --git a/3rdparty/rcheevos/src/rhash/rc_hash_internal.h b/3rdparty/rcheevos/src/rhash/rc_hash_internal.h index c1ea6d0c77..76c62b6df8 100644 --- a/3rdparty/rcheevos/src/rhash/rc_hash_internal.h +++ b/3rdparty/rcheevos/src/rhash/rc_hash_internal.h @@ -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