From 4b8f3ec5b5141a4a8f83f810b9fd35b04b7d56fe Mon Sep 17 00:00:00 2001 From: Jamiras Date: Fri, 14 Feb 2020 20:12:49 -0700 Subject: [PATCH] default rich presence on; better handling of special characters --- cheevos-new/cheevos.c | 164 +++++++++++++++-------------------------- cheevos-new/parser.c | 109 ++++++++++++++++++++++----- cheevos-new/parser.h | 1 + configuration.c | 2 +- menu/menu_setting.c | 2 +- tasks/task_http.c | 21 ++++++ tasks/tasks_internal.h | 3 + 7 files changed, 179 insertions(+), 123 deletions(-) diff --git a/cheevos-new/cheevos.c b/cheevos-new/cheevos.c index da3ca5511b..a96f5bd9f5 100644 --- a/cheevos-new/cheevos.c +++ b/cheevos-new/cheevos.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #ifdef HAVE_CONFIG_H @@ -572,94 +571,31 @@ static int rcheevos_parse(const char* json) if (rcheevos_locals.patchdata.richpresence_script) { - char *script = rcheevos_locals.patchdata.richpresence_script; - char *buffer_it = &script[0]; - const char *script_it = &script[0]; - unsigned buffer_size; - - while (*script_it != '\0') + int buffer_size = rc_richpresence_size(rcheevos_locals.patchdata.richpresence_script); + if (buffer_size <= 0) { - if (*script_it == '\\') - { - char escaped_char = *(script_it + 1); + snprintf(buffer, sizeof(buffer), "Error in rich presence: %s", rcheevos_rc_error(buffer_size)); - switch (escaped_char) - { - /* Ignore carriage return */ - case 'r': - script_it += 2; - break; + if (settings->bools.cheevos_verbose_enable) + runloop_msg_queue_push(buffer, 0, 4 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - /* Accept newlines */ - case 'n': - *buffer_it = '\n'; - buffer_it++; - script_it += 2; - break; - - /* Accept UTF-16 unicode characters */ - case 'u': - { - uint16_t *utf16; - char *utf8; - uint8_t i, j; - - for (i = 1; i < 16; i++) - if (strncmp((script_it + 6 * i), "\\u", 2)) - break; - - utf16 = (uint16_t*)calloc(i, sizeof(uint16_t)); - utf8 = (char*) calloc(i * 4, sizeof(char)); - - /* Get escaped hex values and add them to the string */ - for (j = 0; j < i; j++) - { - char temp[5]; - - script_it += 2; - memcpy(temp, script_it, 4); - temp[4] = '\0'; - utf16[j] = string_hex_to_unsigned(temp); - script_it += 4; - } - - if (utf16_to_char_string(utf16, utf8, i * 4)) - { - memcpy(buffer_it, utf8, strlen(utf8)); - buffer_it += strlen(utf8); - } - - free(utf16); - free(utf8); - } - break; - default: - *buffer_it = *script_it; - buffer_it++; - script_it++; - }; - } - else - { - *buffer_it = *script_it; - buffer_it++; - script_it++; - } - } - *buffer_it = '\0'; - - buffer_size = rc_richpresence_size(rcheevos_locals.patchdata.richpresence_script); - if (buffer_size == 0) - { + CHEEVOS_ERR(RCHEEVOS_TAG "%s\n", buffer); rcheevos_locals.richpresence.richpresence = NULL; - CHEEVOS_ERR(RCHEEVOS_TAG "Error reading rich presence"); } else { char *buffer = (char*)malloc(buffer_size); - rcheevos_locals.richpresence.richpresence = rc_parse_richpresence(buffer, script, NULL, 0); + rcheevos_locals.richpresence.richpresence = rc_parse_richpresence(buffer, rcheevos_locals.patchdata.richpresence_script, NULL, 0); rcheevos_locals.richpresence.last_update = cpu_features_get_time_usec(); } + + rcheevos_locals.richpresence.evaluation[0] = '\0'; + } + + if (!rcheevos_locals.richpresence.richpresence && rcheevos_locals.patchdata.title) + { + snprintf(rcheevos_locals.richpresence.evaluation, sizeof(rcheevos_locals.richpresence.evaluation), + "Playing %s", rcheevos_locals.patchdata.title); } return 0; @@ -1055,37 +991,56 @@ const char* rcheevos_get_richpresence(void) static void rcheevos_test_richpresence(void) { - if (!rcheevos_locals.richpresence.richpresence || - cpu_features_get_time_usec() < rcheevos_locals.richpresence.last_update + CHEEVOS_PING_FREQUENCY) - return; + settings_t* settings = config_get_ptr(); + retro_time_t now = cpu_features_get_time_usec(); - { - settings_t* settings = config_get_ptr(); - char url[256], post_data[1024]; + if (settings->bools.cheevos_richpresence_enable) + { + /* update rich presence every two minutes */ + if (now < rcheevos_locals.richpresence.last_update + CHEEVOS_PING_FREQUENCY) + return; - rcheevos_locals.richpresence.last_update = cpu_features_get_time_usec(); + if (rcheevos_locals.richpresence.richpresence) + { + rc_evaluate_richpresence(rcheevos_locals.richpresence.richpresence, + rcheevos_locals.richpresence.evaluation, + sizeof(rcheevos_locals.richpresence.evaluation), rcheevos_peek, NULL, NULL); + } + } + else + { + /* send ping every four minutes */ + if (now < rcheevos_locals.richpresence.last_update + CHEEVOS_PING_FREQUENCY * 2) + return; + } - rc_evaluate_richpresence(rcheevos_locals.richpresence.richpresence, - rcheevos_locals.richpresence.evaluation, - sizeof(rcheevos_locals.richpresence.evaluation), rcheevos_peek, NULL, NULL); + rcheevos_locals.richpresence.last_update = now; - /* Form URL */ - snprintf(url, 256, "http://retroachievements.org/dorequest.php?r=ping&u=%s&t=%s", - settings->arrays.cheevos_username, - rcheevos_locals.token); + { + char user_agent[256], url[256], post_data[1024]; - /* Form POST data */ - snprintf(post_data, 1024, "g=%u&m=%s", - rcheevos_locals.patchdata.game_id, - rcheevos_get_richpresence()); + snprintf(url, sizeof(url), "http://retroachievements.org/dorequest.php?r=ping&u=%s&t=%s", + settings->arrays.cheevos_username, rcheevos_locals.token); + + if (rcheevos_locals.richpresence.evaluation[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); + if (settings->bools.discord_enable) + discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS, false); #endif + } + else + { + snprintf(post_data, sizeof(post_data), "g=%u", rcheevos_locals.patchdata.game_id); + } - task_push_http_post_transfer(url, post_data, true, "POST", NULL, NULL); + rcheevos_get_user_agent(user_agent); + task_push_http_post_transfer_with_user_agent(url, post_data, true, "POST", user_agent, NULL, NULL); } } @@ -1370,8 +1325,7 @@ void rcheevos_test(void) !rcheevos_hardcore_paused) rcheevos_test_leaderboards(); - if (settings->bools.cheevos_richpresence_enable) - rcheevos_test_richpresence(); + rcheevos_test_richpresence(); } } @@ -2525,6 +2479,10 @@ found: if (!path_is_directory(coro->badge_fullpath)) path_mkdir(coro->badge_fullpath); CORO_YIELD(); + + if (!coro->cheevo->info->badge || !coro->cheevo->info->badge[0]) + continue; + if (coro->j == 0) snprintf(coro->badge_name, sizeof(coro->badge_name), diff --git a/cheevos-new/parser.c b/cheevos-new/parser.c index e27002991a..78af214108 100644 --- a/cheevos-new/parser.c +++ b/cheevos-new/parser.c @@ -3,6 +3,7 @@ #include "hash.h" #include "util.h" +#include #include #include #include @@ -263,6 +264,7 @@ typedef struct int in_cheevos; int in_lboards; int is_game_id; + int is_title; int is_console_id; int is_richpresence; unsigned core_count; @@ -276,16 +278,77 @@ typedef struct rcheevos_rapatchdata_t* patchdata; } rcheevos_readud_t; -static const char* rcheevos_dupstr(const rcheevos_field_t* field) +static char* rcheevos_unescape_string(const char* string, size_t length) { - char* string = (char*)malloc(field->length + 1); + const char* end = string + length; + char* buffer = (char*)malloc(length + 1); + char* buffer_it = buffer; - if (!string) + if (buffer == NULL) return NULL; - memcpy((void*)string, (void*)field->string, field->length); - string[field->length] = 0; - return string; + while (string < end) + { + if (*string == '\\') + { + char escaped_char = string[1]; + switch (escaped_char) + { + case 'r': /* Ignore carriage return */ + string += 2; + break; + + case 'n': /* Accept newlines */ + *buffer_it++ = '\n'; + string += 2; + break; + + case 'u': /* Accept UTF-16 unicode characters */ + { +#define MAX_SEQUENCES 16 + uint16_t utf16[MAX_SEQUENCES]; + char utf8[MAX_SEQUENCES * 4]; + uint8_t i, j; + + for (i = 1; i < MAX_SEQUENCES - 1; i++) + if (strncmp((string + 6 * i), "\\u", 2)) + break; + + /* Get escaped hex values and add them to the string */ + for (j = 0; j < i; j++) + { + char temp[5]; + + string += 2; + memcpy(temp, string, 4); + temp[4] = '\0'; + utf16[j] = string_hex_to_unsigned(temp); + string += 4; + } + utf16[j] = 0; + + if (utf16_to_char_string(utf16, utf8, sizeof(utf8))) + { + memcpy(buffer_it, utf8, strlen(utf8)); + buffer_it += strlen(utf8); + } + } + break; + + default: + *buffer_it++ = escaped_char; + string += 2; + break; + }; + } + else + { + *buffer_it++ = *string++; + } + } + *buffer_it = '\0'; + + return buffer; } static int rcheevos_new_cheevo(rcheevos_readud_t* ud) @@ -300,10 +363,10 @@ static int rcheevos_new_cheevo(rcheevos_readud_t* ud) else return 0; - cheevo->title = rcheevos_dupstr(&ud->title); - cheevo->description = rcheevos_dupstr(&ud->desc); - cheevo->badge = rcheevos_dupstr(&ud->badge); - cheevo->memaddr = rcheevos_dupstr(&ud->memaddr); + cheevo->title = rcheevos_unescape_string(ud->title.string, ud->title.length); + cheevo->description = rcheevos_unescape_string(ud->desc.string, ud->desc.length); + cheevo->badge = rcheevos_unescape_string(ud->badge.string, ud->badge.length); + cheevo->memaddr = rcheevos_unescape_string(ud->memaddr.string, ud->memaddr.length); cheevo->points = (unsigned)strtol(ud->points.string, NULL, 10); cheevo->id = (unsigned)strtol(ud->id.string, NULL, 10); @@ -326,11 +389,11 @@ static int rcheevos_new_lboard(rcheevos_readud_t* ud) { rcheevos_ralboard_t* lboard = ud->patchdata->lboards + ud->lboard_count++; - lboard->title = rcheevos_dupstr(&ud->title); - lboard->description = rcheevos_dupstr(&ud->desc); - lboard->format = rcheevos_dupstr(&ud->format); - lboard->mem = rcheevos_dupstr(&ud->memaddr); - lboard->id = (unsigned)strtol(ud->id.string, NULL, 10); + lboard->title = rcheevos_unescape_string(ud->title.string, ud->title.length); + lboard->description = rcheevos_unescape_string(ud->desc.string, ud->desc.length); + lboard->format = rcheevos_unescape_string(ud->format.string, ud->format.length); + lboard->mem = rcheevos_unescape_string(ud->memaddr.string, ud->memaddr.length); + lboard->id = (unsigned)strtol(ud->id.string, NULL, 10); if ( !lboard->title || !lboard->description @@ -409,6 +472,8 @@ static int rcheevos_read_key(void* userdata, case CHEEVOS_JSON_KEY_TITLE: if (common) ud->field = &ud->title; + else + ud->is_title = 1; break; case CHEEVOS_JSON_KEY_DESCRIPTION: if (common) @@ -459,11 +524,14 @@ static int rcheevos_read_string(void* userdata, ud->field->string = string; ud->field->length = length; } + else if (ud->is_title) + { + ud->patchdata->title = rcheevos_unescape_string(string, length); + ud->is_title = 0; + } else if (ud->is_richpresence) { - ud->patchdata->richpresence_script = (char*)malloc(length + 1); - memcpy(ud->patchdata->richpresence_script, string, length); - ud->patchdata->richpresence_script[length] = '\0'; + ud->patchdata->richpresence_script = rcheevos_unescape_string(string, length); ud->is_richpresence = 0; } @@ -544,10 +612,14 @@ int rcheevos_get_patchdata(const char* json, rcheevos_rapatchdata_t* patchdata) return -1; } + patchdata->richpresence_script = NULL; + patchdata->title = NULL; + /* Load the achievements. */ ud.in_cheevos = 0; ud.in_lboards = 0; ud.is_game_id = 0; + ud.is_title = 0; ud.is_console_id = 0; ud.is_richpresence = 0; ud.field = NULL; @@ -609,6 +681,7 @@ void rcheevos_free_patchdata(rcheevos_rapatchdata_t* patchdata) CHEEVOS_FREE(patchdata->unofficial); CHEEVOS_FREE(patchdata->lboards); CHEEVOS_FREE(patchdata->richpresence_script); + CHEEVOS_FREE(patchdata->title); patchdata->game_id = 0; patchdata->console_id = 0; diff --git a/cheevos-new/parser.h b/cheevos-new/parser.h index 2ce63d4b08..29aa0a3e3e 100644 --- a/cheevos-new/parser.h +++ b/cheevos-new/parser.h @@ -44,6 +44,7 @@ typedef struct { typedef struct { unsigned game_id; unsigned console_id; + char* title; rcheevos_racheevo_t* core; rcheevos_racheevo_t* unofficial; diff --git a/configuration.c b/configuration.c index 571b49ce2b..567ac1a2b2 100644 --- a/configuration.c +++ b/configuration.c @@ -1583,7 +1583,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("cheevos_test_unofficial", &settings->bools.cheevos_test_unofficial, true, false, false); SETTING_BOOL("cheevos_hardcore_mode_enable", &settings->bools.cheevos_hardcore_mode_enable, true, false, false); SETTING_BOOL("cheevos_leaderboards_enable", &settings->bools.cheevos_leaderboards_enable, true, false, false); - SETTING_BOOL("cheevos_richpresence_enable", &settings->bools.cheevos_richpresence_enable, true, false, false); + SETTING_BOOL("cheevos_richpresence_enable", &settings->bools.cheevos_richpresence_enable, true, true, false); SETTING_BOOL("cheevos_verbose_enable", &settings->bools.cheevos_verbose_enable, true, false, false); SETTING_BOOL("cheevos_auto_screenshot", &settings->bools.cheevos_auto_screenshot, true, false, false); #ifdef HAVE_XMB diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 07d8c415fd..20d5e13063 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -15419,7 +15419,7 @@ static bool setting_append_list( &settings->bools.cheevos_richpresence_enable, MENU_ENUM_LABEL_CHEEVOS_RICHPRESENCE_ENABLE, MENU_ENUM_LABEL_VALUE_CHEEVOS_RICHPRESENCE_ENABLE, - false, + true, MENU_ENUM_LABEL_VALUE_OFF, MENU_ENUM_LABEL_VALUE_ON, &group_info, diff --git a/tasks/task_http.c b/tasks/task_http.c index 17f7335709..3a76545963 100644 --- a/tasks/task_http.c +++ b/tasks/task_http.c @@ -393,6 +393,27 @@ void* task_push_http_post_transfer(const char *url, url, mute, type, cb, user_data); } +void* task_push_http_post_transfer_with_user_agent(const char *url, + const char *post_data, bool mute, + const char *type, const char* user_agent, + retro_task_callback_t cb, void *user_data) +{ + struct http_connection_t* conn; + + if (string_is_empty(url)) + return NULL; + + conn = net_http_connection_new(url, "POST", post_data); + if (!conn) + return NULL; + + if (user_agent != NULL) + net_http_connection_set_user_agent(conn, user_agent); + + /* assert: task_push_http_transfer_generic will free conn on failure */ + return task_push_http_transfer_generic(conn, url, mute, type, cb, user_data); +} + task_retriever_info_t *http_task_get_transfer_list(void) { task_retriever_data_t retrieve_data; diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 6e3d667922..15e698f51a 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -76,6 +76,9 @@ void *task_push_http_transfer_with_user_agent(const char *url, bool mute, const void *task_push_http_post_transfer(const char *url, const char *post_data, bool mute, const char *type, retro_task_callback_t cb, void *userdata); +void *task_push_http_post_transfer_with_user_agent(const char* url, const char* post_data, bool mute, + const char* type, const char* user_agent, retro_task_callback_t cb, void* user_data); + task_retriever_info_t *http_task_get_transfer_list(void); bool task_push_wifi_scan(retro_task_callback_t cb);