From c276577b5251d1bf524d43a3042e17d7a70782e2 Mon Sep 17 00:00:00 2001 From: MrHuu Date: Wed, 22 Jan 2025 20:42:23 +0100 Subject: [PATCH 01/12] (3DS) Add unique ID's (#17459) - Anarch - Ardens - Dice - DoubleCherryGB - Fbneocps12 - Fbneoneogeo - Numero - Tamalibretro - Uw8 --- pkg/ctr/Makefile.cores | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/pkg/ctr/Makefile.cores b/pkg/ctr/Makefile.cores index bfb215b0c90f..12863f1eec58 100644 --- a/pkg/ctr/Makefile.cores +++ b/pkg/ctr/Makefile.cores @@ -21,6 +21,20 @@ else ifeq ($(LIBRETRO), a5200) APP_ICON = pkg/ctr/assets/a5200.png APP_BANNER = pkg/ctr/assets/a5200_banner.png +else ifeq ($(LIBRETRO), anarch) + APP_TITLE = Anarch + APP_PRODUCT_CODE = RARCH-ANARCH + APP_UNIQUE_ID = 0xBACE5 + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + +else ifeq ($(LIBRETRO), ardens) + APP_TITLE = Ardens + APP_PRODUCT_CODE = RARCH-ARDENS + APP_UNIQUE_ID = 0xBACE6 + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), arduous) APP_TITLE = Arduous APP_PRODUCT_CODE = RARCH-ARDUOUS @@ -71,6 +85,13 @@ else ifeq ($(LIBRETRO), crocods) APP_ICON = pkg/ctr/assets/default.png APP_BANNER = pkg/ctr/assets/libretro_banner.png +else ifeq ($(LIBRETRO), dice) + APP_TITLE = Dice + APP_PRODUCT_CODE = RARCH-DICE + APP_UNIQUE_ID = 0xBACE7 + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), dosbox) APP_TITLE = DosBox APP_AUTHOR = various @@ -87,6 +108,13 @@ else ifeq ($(LIBRETRO), dosbox_svn) APP_ICON = pkg/ctr/assets/dosbox.png APP_BANNER = pkg/ctr/assets/dosbox_banner.png +else ifeq ($(LIBRETRO), doublecherrygb) + APP_TITLE = DoubleCherryGB + APP_PRODUCT_CODE = RARCH-DCGB + APP_UNIQUE_ID = 0xBACE8 + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), ecwolf) APP_TITLE = ECWolf APP_PRODUCT_CODE = RARCH-ECWOLF @@ -146,6 +174,20 @@ else ifeq ($(LIBRETRO), fbneo) APP_ICON = pkg/ctr/assets/FBNEO.png APP_BANNER = pkg/ctr/assets/FBNEO_banner.png +else ifeq ($(LIBRETRO), fbneo_cps12) + APP_TITLE = FinalBurn Neo CPS-1 / CPS-2 + APP_PRODUCT_CODE = RARCH-FBNEOCPS12 + APP_UNIQUE_ID = 0xBACE9 + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + +else ifeq ($(LIBRETRO), fbneo_neogeo) + APP_TITLE = FinalBurn Neo Neo-Geo + APP_PRODUCT_CODE = RARCH-FBNEONEOGEO + APP_UNIQUE_ID = 0xBACEA + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), fceumm) APP_TITLE = FCeumm Libretro APP_PRODUCT_CODE = RARCH-FCEUMM @@ -423,6 +465,14 @@ else ifeq ($(LIBRETRO), np2kai) APP_ICON = pkg/ctr/assets/np2kai.png APP_BANNER = pkg/ctr/assets/np2kai_banner.png +else ifeq ($(LIBRETRO), numero) + APP_TITLE = Numero + APP_AUTHOR = nbarkhina + APP_PRODUCT_CODE = RARCH-NUMERO + APP_UNIQUE_ID = 0xBACEB + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), nxengine) APP_TITLE = NXengine Libretro APP_PRODUCT_CODE = RARCH-NXENGINE @@ -611,6 +661,14 @@ else ifeq ($(LIBRETRO), superbroswar) APP_ICON = pkg/ctr/assets/default.png APP_BANNER = pkg/ctr/assets/libretro_banner.png +else ifeq ($(LIBRETRO), tamalibretro) + APP_TITLE = TamaLIBretro + APP_AUTHOR = celerizer + APP_PRODUCT_CODE = RARCH-TAMALIBRETRO + APP_UNIQUE_ID = 0xBACEC + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), tgbdual) APP_TITLE = TGB-Dual APP_PRODUCT_CODE = RARCH-TGBDUAL @@ -646,6 +704,13 @@ else ifeq ($(LIBRETRO), tyrquake) APP_ICON = pkg/ctr/assets/tyrquake.png APP_BANNER = pkg/ctr/assets/tyrquake_banner.png +else ifeq ($(LIBRETRO), uw8) + APP_TITLE = MicroW8 + APP_PRODUCT_CODE = RARCH-UW8 + APP_UNIQUE_ID = 0xBACED + APP_ICON = pkg/ctr/assets/default.png + APP_BANNER = pkg/ctr/assets/libretro_banner.png + else ifeq ($(LIBRETRO), uzem) APP_TITLE = Uzem APP_PRODUCT_CODE = RARCH-UZEM From 0903eead06ddf7c032791690fce3e7055d72150b Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Wed, 22 Jan 2025 14:43:02 -0500 Subject: [PATCH 02/12] tvos: fix fetching refresh rate on tvos 13/14 (#17455) --- gfx/display_servers/dispserv_apple.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/display_servers/dispserv_apple.m b/gfx/display_servers/dispserv_apple.m index 67652f8e1448..64314cd0d46b 100644 --- a/gfx/display_servers/dispserv_apple.m +++ b/gfx/display_servers/dispserv_apple.m @@ -140,7 +140,7 @@ static bool apple_display_server_set_resolution(void *data, UIScreen *mainScreen = [UIScreen mainScreen]; #if (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000) || (TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED >= 150000) - if (@available(iOS 15, *)) + if (@available(iOS 15, tvOS 15, *)) currentRate = [CocoaView get].displayLink.preferredFrameRateRange.preferred; else #endif From aa49c055240aa87d2273b65c3366537bbee3adf9 Mon Sep 17 00:00:00 2001 From: Mathieu Poliquin Date: Thu, 23 Jan 2025 03:43:11 +0800 Subject: [PATCH 03/12] Fix game_ai_lib.so loading path in game_ai.c (#17456) --- ai/game_ai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai/game_ai.c b/ai/game_ai.c index 93298cc6dcc3..e8f01968a913 100644 --- a/ai/game_ai.c +++ b/ai/game_ai.c @@ -106,7 +106,7 @@ extern void game_ai_init() retro_assert(game_ai_lib_set_debug_log); } #else - g_lib_handle = dlopen("libgame_ai.so", RTLD_NOW); + g_lib_handle = dlopen("./libgame_ai.so", RTLD_NOW); retro_assert(g_lib_handle); if(g_lib_handle != NULL) From 14a28b81af0bd339bbf521cf53d6468fb3d1be7f Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 23 Jan 2025 00:14:36 +0000 Subject: [PATCH 04/12] Fetch translations from Crowdin --- intl/msg_hash_fr.h | 42 +++++++++++++++++++++++++++++++++++++++++- intl/msg_hash_uk.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ intl/progress.h | 6 +++--- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 490d9b5f181e..1592cfbb2bf5 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -4888,7 +4888,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_LABEL_VALUE_LOG_VERBOSITY_DEBUG, - "0 (debug)" + "0 (diagnostic)" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_LOG_VERBOSITY_INFO, @@ -15765,13 +15765,53 @@ MSG_HASH( "arrêté." ) #ifdef HAVE_GAME_AI +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_MENU_OPTION, + "Remplacement du joueur IA" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_MENU_OPTION, + "Sous-label remplacement du joueur IA" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_CORE_GAME_AI_OPTIONS, "IA du jeu" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P1, + "Remplacer j1" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P1, + "Remplace le joueur 01" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P2, + "Remplacer j2" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P2, + "Remplace le joueur 02" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_SHOW_DEBUG, + "Afficher le diagnostic" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_SHOW_DEBUG, + "Afficher le diagnostic" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_GAME_AI, + "Afficher 'IA du jeu'" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_GAME_AI, + "Afficher l'option 'IA du jeu'." + ) #endif \ No newline at end of file diff --git a/intl/msg_hash_uk.h b/intl/msg_hash_uk.h index e15d9dd4fe32..6b4bcb989226 100644 --- a/intl/msg_hash_uk.h +++ b/intl/msg_hash_uk.h @@ -15909,9 +15909,53 @@ MSG_HASH( "зупинено." ) #ifdef HAVE_GAME_AI +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_MENU_OPTION, + "Перевизначити гравця по ШІ" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_MENU_OPTION, + "Перевизначити підмітку оператора AI" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_GAME_AI_OPTIONS, + "ШІ гри" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P1, + "Перевизначити p1" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P1, + "Перевизначити гравця 01" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P2, + "Перевизначити p2" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P2, + "Перевизначити гравця 02" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_SHOW_DEBUG, + "Показати налагодження" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_SHOW_DEBUG, + "Показати налагодження" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_GAME_AI, + "Показати 'Кінець гри'" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_GAME_AI, + "Показувати опцію \"Кінець гри\"." + ) #endif \ No newline at end of file diff --git a/intl/progress.h b/intl/progress.h index 1f231552d2df..e0a6a233cfac 100644 --- a/intl/progress.h +++ b/intl/progress.h @@ -59,8 +59,8 @@ #define LANGUAGE_PROGRESS_FINNISH_APPROVED 46 /* French */ -#define LANGUAGE_PROGRESS_FRENCH_TRANSLATED 99 -#define LANGUAGE_PROGRESS_FRENCH_APPROVED 99 +#define LANGUAGE_PROGRESS_FRENCH_TRANSLATED 100 +#define LANGUAGE_PROGRESS_FRENCH_APPROVED 100 /* Galician */ #define LANGUAGE_PROGRESS_GALICIAN_TRANSLATED 99 @@ -143,7 +143,7 @@ #define LANGUAGE_PROGRESS_TATAR_APPROVED 0 /* Ukrainian */ -#define LANGUAGE_PROGRESS_UKRAINIAN_TRANSLATED 99 +#define LANGUAGE_PROGRESS_UKRAINIAN_TRANSLATED 100 #define LANGUAGE_PROGRESS_UKRAINIAN_APPROVED 7 /* Valencian */ From d2eb49ccbc5b7291d5fa9d1c73427d79b4da4387 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Wed, 22 Jan 2025 19:29:47 -0500 Subject: [PATCH 05/12] net_http refactor (#17460) * net_http: refactor net_http_new The goal is to move calls to getaddrinfo() and connect() into net_http_update(). This will make it possible for them to be replaced with non-blocking alternatives later. The net_http calling pattern right now allows callers to create the http_connection_t, call net_http_new() which creates the http_t from the http_connection_t, free the http_connection_t, and then start calling net_http_update(). In order to preserve that, the http_t needs to copy the values out of the http_connection_t on create. This also preserves the http_connection_t values instead of freeing them, so the connection would be able to be used later. * net_http: implement dns cache * net_http: separate out address resolution, connect, and request send * net_http: perform getaddrinfo on separate thread * net_http: implement basic connection pool * net_http: refactor receive calls to read faster, do fewer reallocs * net_http: build fix for platforms without SSL * net_http: build fix for non-griffin builds * net_http: build fix for non-threaded platforms --- libretro-common/net/net_http.c | 1450 ++++++++++++++++++++------------ 1 file changed, 922 insertions(+), 528 deletions(-) diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index 486459af9b16..35834cce6608 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -31,66 +31,116 @@ #include #endif #include +#include #include #include #include #include #include +#ifdef HAVE_THREADS +#include +#endif -enum +enum response_part { P_HEADER_TOP = 0, P_HEADER, P_BODY, P_BODY_CHUNKLEN, - P_DONE, - P_ERROR + P_DONE }; -enum +enum bodytype { T_FULL = 0, T_LEN, T_CHUNK }; -struct http_socket_state_t +struct conn_pool_entry { - void *ssl_ctx; + char *domain; + int port; int fd; + void *ssl_ctx; bool ssl; + bool connected; + bool in_use; + struct conn_pool_entry *next; }; +static struct conn_pool_entry *conn_pool = NULL; +#ifdef HAVE_THREADS +static slock_t *conn_pool_lock = NULL; +#endif + struct http_t { - char *data; - struct string_list *headers; - struct http_socket_state_t sock_state; /* ptr alignment */ - size_t pos; - size_t len; - size_t buflen; - int status; - char part; - char bodytype; bool error; + + struct conn_pool_entry *conn; + bool ssl; + bool request_sent; + + struct request + { + char *domain; + char *path; + char *method; + char *contenttype; + void *postdata; + char *useragent; + char *headers; + size_t contentlength; + int port; + } request; + + struct response + { + char *data; + struct string_list *headers; + size_t pos; + size_t len; + size_t buflen; + int status; + enum response_part part; + enum bodytype bodytype; + } response; }; struct http_connection_t { char *domain; - char *location; - char *urlcopy; + char *path; + char *url; char *scan; - char *methodcopy; - char *contenttypecopy; - void *postdatacopy; - char *useragentcopy; - char *headerscopy; - struct http_socket_state_t sock_state; /* ptr alignment */ - size_t contentlength; + char *method; + char *contenttype; + void *postdata; + char *useragent; + char *headers; + size_t contentlength; /* ptr alignment */ int port; + bool ssl; }; +struct dns_cache_entry +{ + char *domain; + int port; + struct addrinfo *addr; + retro_time_t timestamp; + bool valid; + struct dns_cache_entry *next; +}; + +static struct dns_cache_entry *dns_cache = NULL; +/* 5 min timeout, in usec */ +static const retro_time_t dns_cache_timeout = 1000 /* usec/ms */ * 1000 /* ms/s */ * 60 /* s/min */ * 5 /* min */; +#ifdef HAVE_THREADS +static slock_t *dns_cache_lock = NULL; +#endif + /** * net_http_urlencode: * @@ -424,85 +474,6 @@ void net_http_urlencode_full(char *s, const char *source, size_t len) free(tmp); } -static int net_http_new_socket(struct http_connection_t *conn) -{ - struct addrinfo *addr = NULL, *next_addr = NULL; - int fd = socket_init( - (void**)&addr, conn->port, conn->domain, SOCKET_TYPE_STREAM, 0); -#ifdef HAVE_SSL - if (conn->sock_state.ssl) - { - if (fd < 0) - goto done; - - if (!(conn->sock_state.ssl_ctx = ssl_socket_init(fd, conn->domain))) - { - socket_close(fd); - fd = -1; - goto done; - } - - /* TODO: Properly figure out what's going wrong when the newer - timeout/poll code interacts with mbed and winsock - https://github.com/libretro/RetroArch/issues/14742 */ - - /* Temp fix, don't use new timeout/poll code for cheevos http requests */ - bool timeout = true; -#ifdef __WIN32 - if (!strcmp(conn->domain, "retroachievements.org")) - timeout = false; -#endif - - if (ssl_socket_connect(conn->sock_state.ssl_ctx, addr, timeout, true) - < 0) - { - fd = -1; - goto done; - } - } - else -#endif - for (next_addr = addr; fd >= 0; fd = socket_next((void**)&next_addr)) - { - if (socket_connect_with_timeout(fd, next_addr, 5000)) - break; - - socket_close(fd); - } - -#ifdef HAVE_SSL -done: -#endif - if (addr) - freeaddrinfo_retro(addr); - - conn->sock_state.fd = fd; - - return fd; -} - -static void net_http_send_str( - struct http_socket_state_t *sock_state, bool *error, - const char *text, size_t text_size) -{ - if (*error) - return; -#ifdef HAVE_SSL - if (sock_state->ssl) - { - if (!ssl_socket_send_all_blocking( - sock_state->ssl_ctx, text, text_size, true)) - *error = true; - } - else -#endif - { - if (!socket_send_all_blocking( - sock_state->fd, text, text_size, true)) - *error = true; - } -} - struct http_connection_t *net_http_connection_new(const char *url, const char *method, const char *data) { @@ -510,42 +481,27 @@ struct http_connection_t *net_http_connection_new(const char *url, if (!url) return NULL; - if (!(conn = (struct http_connection_t*)malloc( - sizeof(*conn)))) + if (!(conn = (struct http_connection_t*)calloc(1, sizeof(*conn)))) return NULL; - conn->domain = NULL; - conn->location = NULL; - conn->urlcopy = NULL; - conn->scan = NULL; - conn->methodcopy = NULL; - conn->contenttypecopy = NULL; - conn->postdatacopy = NULL; - conn->useragentcopy = NULL; - conn->headerscopy = NULL; - conn->port = 0; - conn->sock_state.fd = 0; - conn->sock_state.ssl = false; - conn->sock_state.ssl_ctx= NULL; - if (method) - conn->methodcopy = strdup(method); + conn->method = strdup(method); if (data) { - conn->postdatacopy = strdup(data); + conn->postdata = strdup(data); conn->contentlength = strlen(data); } - if (!(conn->urlcopy = strdup(url))) + if (!(conn->url = strdup(url))) goto error; if (!strncmp(url, "http://", STRLEN_CONST("http://"))) - conn->scan = conn->urlcopy + STRLEN_CONST("http://"); + conn->scan = conn->url + STRLEN_CONST("http://"); else if (!strncmp(url, "https://", STRLEN_CONST("https://"))) { - conn->scan = conn->urlcopy + STRLEN_CONST("https://"); - conn->sock_state.ssl = true; + conn->scan = conn->url + STRLEN_CONST("https://"); + conn->ssl = true; } else goto error; @@ -558,15 +514,12 @@ struct http_connection_t *net_http_connection_new(const char *url, return conn; error: - if (conn->urlcopy) - free(conn->urlcopy); - if (conn->methodcopy) - free(conn->methodcopy); - if (conn->postdatacopy) - free(conn->postdatacopy); - conn->urlcopy = NULL; - conn->methodcopy = NULL; - conn->postdatacopy = NULL; + if (conn->url) + free(conn->url); + if (conn->method) + free(conn->method); + if (conn->postdata) + free(conn->postdata); free(conn); return NULL; } @@ -608,7 +561,7 @@ bool net_http_connection_done(struct http_connection_t *conn) else if (conn->port == 0) { /* port not specified, default to standard HTTP or HTTPS port */ - if (conn->sock_state.ssl) + if (conn->ssl) conn->port = 443; else conn->port = 80; @@ -616,39 +569,39 @@ bool net_http_connection_done(struct http_connection_t *conn) if (*conn->scan == '/') { - /* domain followed by location - split off the location */ + /* domain followed by path - split off the path */ /* site.com/path.html or site.com:80/path.html */ *conn->scan = '\0'; - conn->location = conn->scan + 1; + conn->path = conn->scan + 1; return true; } else if (!*conn->scan) { - /* domain with no location - point location at empty string */ + /* domain with no path - point path at empty string */ /* site.com or site.com:80 */ - conn->location = conn->scan; + conn->path = conn->scan; return true; } else if (*conn->scan == '?') { - /* domain with no location, but still has query parms - point location at the query parms */ + /* domain with no path, but still has query parms - point path at the query parms */ /* site.com?param=3 or site.com:80?param=3 */ if (!has_port) { /* if there wasn't a port, we have to expand the urlcopy so we can separate the two parts */ size_t domain_len = strlen(conn->domain); - size_t location_len = strlen(conn->scan); - char* urlcopy = (char*)malloc(domain_len + location_len + 2); + size_t path_len = strlen(conn->scan); + char* urlcopy = (char*)malloc(domain_len + path_len + 2); memcpy(urlcopy, conn->domain, domain_len); urlcopy[domain_len] = '\0'; - memcpy(urlcopy + domain_len + 1, conn->scan, location_len + 1); + memcpy(urlcopy + domain_len + 1, conn->scan, path_len + 1); - free(conn->urlcopy); - conn->domain = conn->urlcopy = urlcopy; - conn->location = conn->scan = urlcopy + domain_len + 1; + free(conn->url); + conn->domain = conn->url = urlcopy; + conn->path = conn->scan = urlcopy + domain_len + 1; } else /* There was a port, so overwriting the : will terminate the domain and we can just point at the ? */ - conn->location = conn->scan; + conn->path = conn->scan; return true; } @@ -662,30 +615,23 @@ void net_http_connection_free(struct http_connection_t *conn) if (!conn) return; - if (conn->urlcopy) - free(conn->urlcopy); - - if (conn->methodcopy) - free(conn->methodcopy); + if (conn->url) + free(conn->url); - if (conn->contenttypecopy) - free(conn->contenttypecopy); + if (conn->method) + free(conn->method); - if (conn->postdatacopy) - free(conn->postdatacopy); + if (conn->contenttype) + free(conn->contenttype); - if (conn->useragentcopy) - free(conn->useragentcopy); + if (conn->postdata) + free(conn->postdata); - if (conn->headerscopy) - free(conn->headerscopy); + if (conn->useragent) + free(conn->useragent); - conn->urlcopy = NULL; - conn->methodcopy = NULL; - conn->contenttypecopy = NULL; - conn->postdatacopy = NULL; - conn->useragentcopy = NULL; - conn->headerscopy = NULL; + if (conn->headers) + free(conn->headers); free(conn); } @@ -693,19 +639,19 @@ void net_http_connection_free(struct http_connection_t *conn) void net_http_connection_set_user_agent( struct http_connection_t *conn, const char *user_agent) { - if (conn->useragentcopy) - free(conn->useragentcopy); + if (conn->useragent) + free(conn->useragent); - conn->useragentcopy = user_agent ? strdup(user_agent) : NULL; + conn->useragent = user_agent ? strdup(user_agent) : NULL; } void net_http_connection_set_headers( struct http_connection_t *conn, const char *headers) { - if (conn->headerscopy) - free(conn->headerscopy); + if (conn->headers) + free(conn->headers); - conn->headerscopy = headers ? strdup(headers) : NULL; + conn->headers = headers ? strdup(headers) : NULL; } void net_http_connection_set_content( @@ -713,116 +659,582 @@ void net_http_connection_set_content( size_t content_length, const void *content) { - if (conn->contenttypecopy) - free(conn->contenttypecopy); - if (conn->postdatacopy) - free(conn->postdatacopy); + if (conn->contenttype) + free(conn->contenttype); + if (conn->postdata) + free(conn->postdata); - conn->contenttypecopy = content_type ? strdup(content_type) : NULL; + conn->contenttype = content_type ? strdup(content_type) : NULL; conn->contentlength = content_length; if (content_length) { - conn->postdatacopy = malloc(content_length); - memcpy(conn->postdatacopy, content, content_length); + conn->postdata = malloc(content_length); + memcpy(conn->postdata, content, content_length); } } const char *net_http_connection_url(struct http_connection_t *conn) { - return conn->urlcopy; + return conn->url; } const char* net_http_connection_method(struct http_connection_t* conn) { - return conn->methodcopy; + return conn->method; } -struct http_t *net_http_new(struct http_connection_t *conn) +static void net_http_dns_cache_remove_expired(void) +{ + struct dns_cache_entry *entry = dns_cache; + struct dns_cache_entry *prev = NULL; + while (entry) + { + if (entry->timestamp + dns_cache_timeout < cpu_features_get_time_usec()) + { + if (prev) + prev->next = entry->next; + else + dns_cache = entry->next; + freeaddrinfo_retro(entry->addr); + free(entry->domain); + free(entry); + entry = prev ? prev->next : dns_cache; + } + else + { + prev = entry; + entry = entry->next; + } + } +} + +static struct dns_cache_entry *net_http_dns_cache_find(const char *domain, int port) +{ + struct dns_cache_entry *entry; + + net_http_dns_cache_remove_expired(); + + entry = dns_cache; + while (entry) + { + if (port == entry->port && string_is_equal(entry->domain, domain)) + { + entry->timestamp = cpu_features_get_time_usec(); + return entry; + } + entry = entry->next; + } + return NULL; +} + +static struct dns_cache_entry *net_http_dns_cache_add(const char *domain, int port, struct addrinfo *addr) +{ + struct dns_cache_entry *entry = (struct dns_cache_entry*)calloc(1, sizeof(*entry)); + if (!entry) + return NULL; + entry->domain = strdup(domain); + entry->port = port; + entry->addr = addr; + entry->timestamp = cpu_features_get_time_usec(); + entry->valid = (addr != NULL); + entry->next = dns_cache; + dns_cache = entry; + return entry; +} + +static void net_http_conn_pool_free(struct conn_pool_entry *entry) +{ +#ifdef HAVE_SSL + if (entry->ssl && entry->ssl_ctx) + { + ssl_socket_close(entry->ssl_ctx); + ssl_socket_free(entry->ssl_ctx); + } +#endif + if (entry->fd >= 0) + socket_close(entry->fd); + free(entry->domain); + free(entry); +} + +static void net_http_conn_pool_remove(struct conn_pool_entry *entry) +{ + struct conn_pool_entry *prev = NULL; + struct conn_pool_entry *current; + if (!entry) + return; +#ifdef HAVE_THREADS + slock_lock(conn_pool_lock); +#endif + current = conn_pool; + while (current) + { + if (current == entry) + { + if (prev) + prev->next = current->next; + else + conn_pool = current->next; + net_http_conn_pool_free(current); +#ifdef HAVE_THREADS + slock_unlock(conn_pool_lock); +#endif + return; + } + prev = current; + current = current->next; + } +#ifdef HAVE_THREADS + slock_unlock(conn_pool_lock); +#endif +} + +/* *NOT* thread safe, caller must lock */ +static void net_http_conn_pool_remove_expired(void) { - bool error = false; + struct conn_pool_entry *entry = conn_pool; + struct conn_pool_entry *prev = NULL; + struct timeval tv = { 0 }; + int max = 0; + fd_set fds; + FD_ZERO(&fds); + entry = conn_pool; + while (entry) + { + if (!entry->in_use) + { + FD_SET(entry->fd, &fds); + if (entry->fd >= max) + max = entry->fd + 1; + } + entry = entry->next; + } + if (select(max, &fds, NULL, NULL, &tv) <= 0) + return; + entry = conn_pool; + while (entry) + { + if (!entry->in_use && FD_ISSET(entry->fd, &fds)) + { + char buf[4096]; + bool error = false; #ifdef HAVE_SSL - if (!conn || (net_http_new_socket(conn)) < 0) + if (entry->ssl && entry->ssl_ctx) + ssl_socket_receive_all_nonblocking(entry->ssl_ctx, &error, buf, sizeof(buf)); + else +#endif + socket_receive_all_nonblocking(entry->fd, &error, buf, sizeof(buf)); + + if (!error) + continue; + + if (prev) + prev->next = entry->next; + else + conn_pool = entry->next; + /* if it's not in use and it's reaadable we assume that means it's closed without checking recv */ + net_http_conn_pool_free(entry); + entry = prev ? prev->next : conn_pool; + } + else + { + prev = entry; + entry = entry->next; + } + } +} + +/* if it's not already in the pool, will add to end. + *NOT* thread safe, caller must lock */ +static void net_http_conn_pool_move_to_end(struct conn_pool_entry *entry) +{ + struct conn_pool_entry *prev = NULL; + struct conn_pool_entry *current = conn_pool; + /* 0 items in pool */ + if (!conn_pool) + { + conn_pool = entry; + entry->next = NULL; + return; + } + /* already only item in pool */ + if (conn_pool == entry && !conn_pool->next) + return; + while (current) + { + if (current != entry) + prev = current; + else + { + /* need to remove current */ + if (prev) + prev->next = current->next; + else + conn_pool = current->next; + } + current = current->next; + } + prev->next = entry; + entry->next = NULL; +} + +static struct conn_pool_entry *net_http_conn_pool_find(const char *domain, int port) +{ + struct conn_pool_entry *entry; + +#ifdef HAVE_THREADS + slock_lock(conn_pool_lock); +#endif + + net_http_conn_pool_remove_expired(); + + entry = conn_pool; + while (entry) + { + if (!entry->in_use && port == entry->port && string_is_equal(entry->domain, domain)) + { + entry->in_use = true; + net_http_conn_pool_move_to_end(entry); +#ifdef HAVE_THREADS + slock_unlock(conn_pool_lock); +#endif + return entry; + } + entry = entry->next; + } +#ifdef HAVE_THREADS + slock_unlock(conn_pool_lock); +#endif + return NULL; +} + +static struct conn_pool_entry *net_http_conn_pool_add(const char *domain, int port, int fd, bool ssl) +{ + struct conn_pool_entry *entry = (struct conn_pool_entry*)calloc(1, sizeof(*entry)); + if (!entry) return NULL; -#else - int fd = -1; - if (!conn || (fd = net_http_new_socket(conn)) < 0) + entry->domain = strdup(domain); + entry->port = port; + entry->fd = fd; + entry->in_use = true; + entry->ssl = ssl; + entry->connected = false; +#ifdef HAVE_THREADS + slock_lock(conn_pool_lock); +#endif + net_http_conn_pool_move_to_end(entry); +#ifdef HAVE_THREADS + slock_unlock(conn_pool_lock); +#endif + return entry; +} + +struct http_t *net_http_new(struct http_connection_t *conn) +{ + struct http_t *state; + + if (!conn) return NULL; + + state = (struct http_t*)calloc(1, sizeof(struct http_t)); + if (!state) + return NULL; + + state->ssl = conn->ssl; + state->conn = NULL; + + state->request.domain = strdup(conn->domain); + state->request.path = strdup(conn->path); + state->request.method = strdup(conn->method); + state->request.contenttype = conn->contenttype ? strdup(conn->contenttype) : NULL; + state->request.contentlength = conn->contentlength; + if (conn->postdata && conn->contentlength) + { + state->request.postdata = malloc(conn->contentlength); + memcpy(state->request.postdata, conn->postdata, conn->contentlength); + } + state->request.useragent = conn->useragent ? strdup(conn->useragent) : NULL; + state->request.headers = conn->headers ? strdup(conn->headers) : NULL; + state->request.port = conn->port; + + state->response.status = -1; + state->response.buflen = 16 * 1024; + state->response.data = (char*)malloc(state->response.buflen); + state->response.headers = string_list_new(); + string_list_initialize(state->response.headers); + + return state; +} + +static void net_http_resolve(void *data) +{ + struct dns_cache_entry *entry = (struct dns_cache_entry*)data; + struct addrinfo hints = {0}; + struct addrinfo *addr = NULL; + char *domain; + int port; + char port_buf[6]; +#if defined(HAVE_SOCKET_LEGACY) || defined(WIIU) + int family = AF_INET; +#else + int family = AF_UNSPEC; #endif - /* This is a bit lazy, but it works. */ - if (conn->methodcopy) + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_NUMERICSERV; + +#ifdef HAVE_THREADS + slock_lock(dns_cache_lock); +#endif + domain = strdup(entry->domain); + port = entry->port; +#ifdef HAVE_THREADS + slock_unlock(dns_cache_lock); +#endif + + if (!network_init()) { - net_http_send_str(&conn->sock_state, &error, conn->methodcopy, - strlen(conn->methodcopy)); - net_http_send_str(&conn->sock_state, &error, " /", - STRLEN_CONST(" /")); +#ifdef HAVE_THREADS + slock_lock(dns_cache_lock); +#endif + entry->valid = true; + entry->addr = NULL; +#ifdef HAVE_THREADS + slock_unlock(dns_cache_lock); +#endif + free(domain); + return; + } + + snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port); + + getaddrinfo_retro(domain, port_buf, &hints, &addr); + free(domain); + +#ifdef HAVE_THREADS + slock_lock(dns_cache_lock); +#endif + entry->valid = true; + entry->addr = addr; +#ifdef HAVE_THREADS + slock_unlock(dns_cache_lock); +#endif +} + +static bool net_http_new_socket(struct http_t *state) +{ + struct addrinfo *addr = NULL; + struct dns_cache_entry *entry; + +#ifdef HAVE_THREADS + sthread_t *thread; + + if (!dns_cache_lock) + dns_cache_lock = slock_new(); + slock_lock(dns_cache_lock); +#endif + + entry = net_http_dns_cache_find(state->request.domain, state->request.port); + if (entry) + { + if (entry->valid) + { + int fd; + if (!entry->addr) + { +#ifdef HAVE_THREADS + slock_unlock(dns_cache_lock); +#endif + return false; + } + addr = entry->addr; + fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (fd >= 0) + state->conn = net_http_conn_pool_add(state->request.domain, state->request.port, fd, state->ssl); +#ifdef HAVE_THREADS + /* still waiting on thread */ + slock_unlock(dns_cache_lock); +#endif + return (fd >= 0); + } + else + { +#ifdef HAVE_THREADS + /* still waiting on thread */ + slock_unlock(dns_cache_lock); +#endif + return true; + } } else { - net_http_send_str(&conn->sock_state, &error, "GET /", - STRLEN_CONST("GET /")); + entry = net_http_dns_cache_add(state->request.domain, state->request.port, NULL); +#ifdef HAVE_THREADS + /* create the entry for it as an indicator that the request is underway */ + thread = sthread_create(net_http_resolve, entry); + sthread_detach(thread); +#else + net_http_resolve(state); +#endif } - net_http_send_str(&conn->sock_state, &error, conn->location, - strlen(conn->location)); - net_http_send_str(&conn->sock_state, &error, " HTTP/1.1\r\n", - STRLEN_CONST(" HTTP/1.1\r\n")); +#ifdef HAVE_THREADS + slock_unlock(dns_cache_lock); +#endif + + return true; +} - net_http_send_str(&conn->sock_state, &error, "Host: ", - STRLEN_CONST("Host: ")); - net_http_send_str(&conn->sock_state, &error, conn->domain, - strlen(conn->domain)); +static bool net_http_connect(struct http_t *state) +{ + struct addrinfo *addr = NULL, *next_addr = NULL; + struct conn_pool_entry *conn = state->conn; + struct dns_cache_entry *dns_entry = net_http_dns_cache_find(state->request.domain, state->request.port); + /* we just used/added this in _new_socket above, if it's not there it's a big bug */ + addr = dns_entry->addr; - if (conn->port) +#ifdef HAVE_SSL + if (state->ssl) + { + if (!conn || conn->fd < 0) + return false; + + if (!(conn->ssl_ctx = ssl_socket_init(conn->fd, state->request.domain))) + { + net_http_conn_pool_remove(conn); + state->conn = NULL; + state->error = true; + return false; + } + + /* TODO: Properly figure out what's going wrong when the newer + timeout/poll code interacts with mbed and winsock + https://github.com/libretro/RetroArch/issues/14742 */ + + /* Temp fix, don't use new timeout/poll code for cheevos http requests */ + bool timeout = true; +#ifdef __WIN32 + if (!strcmp(state->request.domain, "retroachievements.org")) + timeout = false; +#endif + + if (ssl_socket_connect(conn->ssl_ctx, addr, timeout, true) < 0) + { + net_http_conn_pool_remove(conn); + state->conn = NULL; + state->error = true; + return false; + } + else + { + conn->connected = true; + return true; + } + } + else +#endif + { + for (next_addr = addr; conn->fd >= 0; conn->fd = socket_next((void**)&next_addr)) + { + if (socket_connect_with_timeout(conn->fd, next_addr, 5000)) + { + conn->connected = true; + return true; + } + + socket_close(conn->fd); + } + conn->fd = -1; /* already closed */ + net_http_conn_pool_remove(conn); + state->conn = NULL; + state->error = true; + return false; + } +} + +static void net_http_send_str( + struct http_t *state, const char *text, size_t text_size) +{ + if (state->error) + return; +#ifdef HAVE_SSL + if (state->ssl) + { + if (!ssl_socket_send_all_blocking( + state->conn->ssl_ctx, text, text_size, true)) + state->error = true; + } + else +#endif + { + if (!socket_send_all_blocking( + state->conn->fd, text, text_size, true)) + state->error = true; + } +} + +static bool net_http_send_request(struct http_t *state) +{ + struct request *request = &state->request; + + /* This is a bit lazy, but it works. */ + if (request->method) + { + net_http_send_str(state, request->method, strlen(request->method)); + net_http_send_str(state, " /", STRLEN_CONST(" /")); + } + else + net_http_send_str(state, "GET /", STRLEN_CONST("GET /")); + + net_http_send_str(state, request->path, strlen(request->path)); + net_http_send_str(state, " HTTP/1.1\r\n", STRLEN_CONST(" HTTP/1.1\r\n")); + + net_http_send_str(state, "Host: ", STRLEN_CONST("Host: ")); + net_http_send_str(state, request->domain, strlen(request->domain)); + + if (request->port) { char portstr[16]; size_t _len = 0; portstr[ _len] = ':'; portstr[++_len] = '\0'; _len += snprintf(portstr + _len, sizeof(portstr) - _len, - "%i", conn->port); - net_http_send_str(&conn->sock_state, &error, portstr, _len); + "%i", request->port); + net_http_send_str(state, portstr, _len); } - net_http_send_str(&conn->sock_state, &error, "\r\n", - STRLEN_CONST("\r\n")); + net_http_send_str(state, "\r\n", STRLEN_CONST("\r\n")); /* Pre-formatted headers */ - if (conn->headerscopy) - net_http_send_str(&conn->sock_state, &error, conn->headerscopy, - strlen(conn->headerscopy)); - if (conn->contenttypecopy) + if (request->headers) + net_http_send_str(state, request->headers, strlen(request->headers)); + if (request->contenttype) { - net_http_send_str(&conn->sock_state, &error, "Content-Type: ", - STRLEN_CONST("Content-Type: ")); - net_http_send_str(&conn->sock_state, &error, - conn->contenttypecopy, strlen(conn->contenttypecopy)); - net_http_send_str(&conn->sock_state, &error, "\r\n", - STRLEN_CONST("\r\n")); + net_http_send_str(state, "Content-Type: ", STRLEN_CONST("Content-Type: ")); + net_http_send_str(state, request->contenttype, strlen(request->contenttype)); + net_http_send_str(state, "\r\n", STRLEN_CONST("\r\n")); } - if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST") || string_is_equal(conn->methodcopy, "PUT"))) + if (request->method && (string_is_equal(request->method, "POST") || string_is_equal(request->method, "PUT"))) { size_t post_len, len; char *len_str = NULL; - if (!conn->postdatacopy && !string_is_equal(conn->methodcopy, "PUT")) - goto err; - - if (!conn->headerscopy) + if (!request->postdata && !string_is_equal(request->method, "PUT")) { - if (!conn->contenttypecopy) - net_http_send_str(&conn->sock_state, &error, - "Content-Type: application/x-www-form-urlencoded\r\n", - STRLEN_CONST( - "Content-Type: application/x-www-form-urlencoded\r\n" - )); + state->error = true; + return true; } - net_http_send_str(&conn->sock_state, &error, "Content-Length: ", - STRLEN_CONST("Content-Length: ")); + if (!request->headers && !request->contenttype) + net_http_send_str(state, + "Content-Type: application/x-www-form-urlencoded\r\n", + STRLEN_CONST("Content-Type: application/x-www-form-urlencoded\r\n")); - post_len = conn->contentlength; + net_http_send_str(state, "Content-Length: ", STRLEN_CONST("Content-Length: ")); + + post_len = request->contentlength; #ifdef _WIN32 len = snprintf(NULL, 0, "%" PRIuPTR, post_len); len_str = (char*)malloc(len + 1); @@ -835,82 +1247,26 @@ struct http_t *net_http_new(struct http_connection_t *conn) len_str[len] = '\0'; - net_http_send_str(&conn->sock_state, &error, len_str, - strlen(len_str)); - net_http_send_str(&conn->sock_state, &error, "\r\n", - STRLEN_CONST("\r\n")); + net_http_send_str(state, len_str, strlen(len_str)); + net_http_send_str(state, "\r\n", STRLEN_CONST("\r\n")); free(len_str); } - net_http_send_str(&conn->sock_state, &error, "User-Agent: ", - STRLEN_CONST("User-Agent: ")); - if (conn->useragentcopy) - net_http_send_str(&conn->sock_state, &error, - conn->useragentcopy, strlen(conn->useragentcopy)); + net_http_send_str(state, "User-Agent: ", STRLEN_CONST("User-Agent: ")); + if (request->useragent) + net_http_send_str(state, request->useragent, strlen(request->useragent)); else - net_http_send_str(&conn->sock_state, &error, "libretro", - STRLEN_CONST("libretro")); - net_http_send_str(&conn->sock_state, &error, "\r\n", - STRLEN_CONST("\r\n")); - - net_http_send_str(&conn->sock_state, &error, - "Connection: close\r\n", STRLEN_CONST("Connection: close\r\n")); - net_http_send_str(&conn->sock_state, &error, "\r\n", - STRLEN_CONST("\r\n")); + net_http_send_str(state, "libretro", STRLEN_CONST("libretro")); + net_http_send_str(state, "\r\n", STRLEN_CONST("\r\n")); - if (conn->postdatacopy && conn->contentlength) - net_http_send_str(&conn->sock_state, &error, conn->postdatacopy, - conn->contentlength); - - if (!error) - { - struct http_t *state = (struct http_t*)malloc(sizeof(struct http_t)); - state->sock_state = conn->sock_state; - state->status = -1; - state->data = NULL; - state->part = P_HEADER_TOP; - state->bodytype = T_FULL; - state->error = false; - state->pos = 0; - state->len = 0; - state->buflen = 512; - - if ((state->data = (char*)malloc(state->buflen))) - { - if ((state->headers = string_list_new()) != NULL) - return state; - } - free(state); - } + net_http_send_str(state, "\r\n", STRLEN_CONST("\r\n")); -err: - if (conn) - { - if (conn->methodcopy) - free(conn->methodcopy); - if (conn->contenttypecopy) - free(conn->contenttypecopy); - if (conn->postdatacopy) - free(conn->postdatacopy); - conn->methodcopy = NULL; - conn->contenttypecopy = NULL; - conn->postdatacopy = NULL; + if (request->postdata && request->contentlength) + net_http_send_str(state, request->postdata, request->contentlength); -#ifdef HAVE_SSL - if (conn->sock_state.ssl_ctx) - { - ssl_socket_close(conn->sock_state.ssl_ctx); - ssl_socket_free(conn->sock_state.ssl_ctx); - conn->sock_state.ssl_ctx = NULL; - } -#endif - } - -#ifndef HAVE_SSL - socket_close(fd); -#endif - return NULL; + state->request_sent = true; + return state->error; } /** @@ -923,262 +1279,297 @@ struct http_t *net_http_new(struct http_connection_t *conn) **/ int net_http_fd(struct http_t *state) { - if (!state) + if (!state || !state->conn) return -1; - return state->sock_state.fd; + return state->conn->fd; } -/** - * net_http_update: - * - * @return true if it's done, or if something broke. - * @total will be 0 if it's not known. - **/ -bool net_http_update(struct http_t *state, size_t* progress, size_t* total) +static ssize_t net_http_receive_header(struct http_t *state, ssize_t newlen) { - if (state) + struct response *response = &state->response; + + response->pos += newlen; + + while (response->part < P_BODY) { - ssize_t newlen = 0; - if (state->error) - goto error; + char *dataend = response->data + response->pos; + char *lineend = (char*)memchr(response->data, '\n', response->pos); - if (state->part < P_BODY) - { - if (state->error) - goto error; + if (!lineend) + break; -#ifdef HAVE_SSL - if (state->sock_state.ssl && state->sock_state.ssl_ctx) - newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error, - (uint8_t*)state->data + state->pos, - state->buflen - state->pos); - else -#endif - newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error, - (uint8_t*)state->data + state->pos, - state->buflen - state->pos); + *lineend = '\0'; - if (newlen < 0) - { - state->error = true; - goto error; - } + if (lineend != response->data && lineend[-1]=='\r') + lineend[-1] = '\0'; - if (state->pos + newlen >= state->buflen - 64) + if (response->part == P_HEADER_TOP) + { + if (strncmp(response->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0) { - state->buflen *= 2; - state->data = (char*)realloc(state->data, state->buflen); + response->part = P_DONE; + state->error = true; + return -1; } - state->pos += newlen; - - while (state->part < P_BODY) + response->status = (int)strtoul(response->data + + STRLEN_CONST("HTTP/1.1 "), NULL, 10); + response->part = P_HEADER; + } + else + { + if (string_starts_with_case_insensitive(response->data, "Content-Length:")) { - char *dataend = state->data + state->pos; - char *lineend = (char*)memchr(state->data, '\n', state->pos); - - if (!lineend) - break; + char* ptr = response->data + STRLEN_CONST("Content-Length:"); + while (ISSPACE(*ptr)) + ++ptr; - *lineend = '\0'; - - if (lineend != state->data && lineend[-1]=='\r') - lineend[-1] = '\0'; + response->bodytype = T_LEN; + response->len = strtol(ptr, NULL, 10); + } + else if (string_is_equal_case_insensitive(response->data, "Transfer-Encoding: chunked")) + response->bodytype = T_CHUNK; - if (state->part == P_HEADER_TOP) + if (response->data[0]=='\0') + { + if (response->status == 100) { - if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0) - { - state->error = true; - goto error; - } - state->status = (int)strtoul(state->data - + STRLEN_CONST("HTTP/1.1 "), NULL, 10); - state->part = P_HEADER; + response->part = P_HEADER_TOP; } else { - if (string_starts_with_case_insensitive(state->data, "Content-Length:")) - { - char* ptr = state->data + STRLEN_CONST("Content-Length:"); - while (ISSPACE(*ptr)) - ++ptr; - - state->bodytype = T_LEN; - state->len = strtol(ptr, NULL, 10); - } - if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked")) - state->bodytype = T_CHUNK; - - if (state->data[0]=='\0') - { - if (state->status == 100) - { - state->part = P_HEADER_TOP; - } - else - { - state->part = P_BODY; - if (state->bodytype == T_CHUNK) - state->part = P_BODY_CHUNKLEN; - } - } - else - { - union string_list_elem_attr attr; - attr.i = 0; - string_list_append(state->headers, state->data, attr); - } + response->part = P_BODY; + if (response->bodytype == T_CHUNK) + response->part = P_BODY_CHUNKLEN; } - - memmove(state->data, lineend + 1, dataend-(lineend+1)); - state->pos = (dataend-(lineend + 1)); } - - if (state->part >= P_BODY) + else { - newlen = state->pos; - state->pos = 0; + union string_list_elem_attr attr; + attr.i = 0; + string_list_append(response->headers, response->data, attr); } } - if (state->part >= P_BODY && state->part < P_DONE) + memmove(response->data, lineend + 1, dataend-(lineend+1)); + response->pos = (dataend-(lineend + 1)); + } + + if (response->part >= P_BODY) + { + newlen = response->pos; + response->pos = 0; + if (response->bodytype == T_LEN) { - if (!newlen) - { - if (state->error) - newlen = -1; - else if (state->len) - { -#ifdef HAVE_SSL - if (state->sock_state.ssl && state->sock_state.ssl_ctx) - newlen = ssl_socket_receive_all_nonblocking( - state->sock_state.ssl_ctx, - &state->error, - (uint8_t*)state->data + state->pos, - state->buflen - state->pos); - else -#endif - newlen = socket_receive_all_nonblocking( - state->sock_state.fd, - &state->error, - (uint8_t*)state->data + state->pos, - state->buflen - state->pos); - } + response->buflen = response->len; + response->data = (char*)realloc(response->data, response->buflen); + } + } + else + { + if (response->pos >= response->buflen - 64) + { + response->buflen *= 2; + response->data = (char*)realloc(response->data, response->buflen); + } + } + return newlen; +} - if (newlen < 0) - { - if (state->bodytype != T_FULL) - { - state->error = true; - goto error; - } - state->part = P_DONE; - state->data = (char*)realloc(state->data, state->len); - newlen = 0; - } +static bool net_http_receive_body(struct http_t *state, ssize_t newlen) +{ + struct response *response = &state->response; - if (state->pos + newlen >= state->buflen - 64) - { - state->buflen *= 2; - state->data = (char*)realloc(state->data, state->buflen); - } - } + if (newlen < 0 || state->error) + { + if (response->bodytype != T_FULL) + return false; + response->part = P_DONE; + if (response->buflen != response->len) + response->data = (char*)realloc(response->data, response->len); + return true; + } parse_again: - if (state->bodytype == T_CHUNK) + if (response->bodytype == T_CHUNK) + { + if (response->part == P_BODY_CHUNKLEN) + { + response->pos += newlen; + + if (response->pos - response->len >= 2) { - if (state->part == P_BODY_CHUNKLEN) - { - state->pos += newlen; + /* + * len=start of chunk including \r\n + * pos=end of data + */ - if (state->pos - state->len >= 2) - { - /* - * len=start of chunk including \r\n - * pos=end of data - */ - - char *fullend = state->data + state->pos; - char *end = (char*)memchr(state->data + state->len + 2, '\n', - state->pos - state->len - 2); - - if (end) - { - size_t chunklen = strtoul(state->data+state->len, NULL, 16); - state->pos = state->len; - end++; - - memmove(state->data+state->len, end, fullend-end); - - state->len = chunklen; - newlen = (fullend - end); - - /* - len=num bytes - newlen=unparsed bytes after \n - pos=start of chunk including \r\n - */ - - state->part = P_BODY; - if (state->len == 0) - { - state->part = P_DONE; - state->len = state->pos; - state->data = (char*)realloc(state->data, state->len); - } - goto parse_again; - } - } - } - else if (state->part == P_BODY) + char *fullend = response->data + response->pos; + char *end = (char*)memchr(response->data + response->len + 2, '\n', + response->pos - response->len - 2); + + if (end) { - if ((size_t)newlen >= state->len) + size_t chunklen = strtoul(response->data + response->len, NULL, 16); + response->pos = response->len; + end++; + + memmove(response->data + response->len, end, fullend-end); + + response->len = chunklen; + newlen = (fullend - end); + + /* + len=num bytes + newlen=unparsed bytes after \n + pos=start of chunk including \r\n + */ + + response->part = P_BODY; + if (response->len == 0) { - state->pos += state->len; - newlen -= state->len; - state->len = state->pos; - state->part = P_BODY_CHUNKLEN; - goto parse_again; + response->part = P_DONE; + response->len = response->pos; + response->data = (char*)realloc(response->data, response->len); + return true; } - state->pos += newlen; - state->len -= newlen; + goto parse_again; } } - else + } + else if (response->part == P_BODY) + { + if ((size_t)newlen >= response->len) { - state->pos += newlen; - - if (state->pos > state->len) - { - state->error = true; - goto error; - } - else if (state->pos == state->len) - { - state->part = P_DONE; - state->data = (char*)realloc(state->data, state->len); - } + response->pos += response->len; + newlen -= response->len; + response->len = response->pos; + response->part = P_BODY_CHUNKLEN; + goto parse_again; } + response->pos += newlen; + response->len -= newlen; + } + } + else + { + response->pos += newlen; + + if (response->pos > response->len) + return false; + else if (response->pos == response->len) + { + response->part = P_DONE; + if (response->buflen != response->len) + response->data = (char*)realloc(response->data, response->len); + return true; } + } + + if (response->pos >= response->buflen) + { + response->buflen *= 2; + response->data = (char*)realloc(response->data, response->buflen); + } + return true; +} - if (progress) - *progress = state->pos; +/** + * net_http_update: + * + * @return true if it's done, or if something broke. + * @total will be 0 if it's not known. + **/ +bool net_http_update(struct http_t *state, size_t* progress, size_t* total) +{ + struct response *response; + ssize_t newlen = 0; - if (total) + if (!state) + return true; + + if (state->error) + return true; + + if (!state->conn) + { + state->conn = net_http_conn_pool_find(state->request.domain, state->request.port); + if (!state->conn) { - if (state->bodytype == T_LEN) - *total = state->len; - else - *total = 0; + if (!net_http_new_socket(state)) + state->error = true; + return state->error; } + } - if (state->part != P_DONE) - return false; + if (!state->conn->connected) + { + if (!net_http_connect(state)) + state->error = true; + return state->error; } + + if (!state->request_sent) + return net_http_send_request(state); + + response = &state->response; + +#ifdef HAVE_SSL + if (state->ssl && state->conn->ssl_ctx) + newlen = ssl_socket_receive_all_nonblocking(state->conn->ssl_ctx, &state->error, + (uint8_t*)response->data + response->pos, + response->buflen - response->pos); + else +#endif + newlen = socket_receive_all_nonblocking(state->conn->fd, &state->error, + (uint8_t*)response->data + response->pos, + response->buflen - response->pos); + + if (response->part < P_BODY) + { + if (newlen < 0 || state->error) + goto error; + newlen = net_http_receive_header(state, newlen); + } + + if (response->part >= P_BODY && response->part < P_DONE) + { + if (!net_http_receive_body(state, newlen)) + goto error; + } + + if (progress) + *progress = response->pos; + + if (total) + { + if (response->bodytype == T_LEN) + *total = response->len; + else + *total = 0; + } + + if (response->part != P_DONE) + return false; + + for (newlen = 0; (size_t)newlen < response->headers->size; newlen++) + { + if (string_is_equal_case_insensitive(response->headers->elems[newlen].data, "connection: close")) + { + net_http_conn_pool_remove(state->conn); + state->conn = NULL; + return true; + } + } + + state->conn->in_use = false; + state->conn = NULL; return true; + error: - state->part = P_ERROR; - state->status = -1; + net_http_conn_pool_remove(state->conn); + state->error = true; + response->part = P_DONE; + response->status = -1; return true; } @@ -1195,7 +1586,7 @@ int net_http_status(struct http_t *state) { if (!state) return -1; - return state->status; + return state->response.status; } /** @@ -1215,7 +1606,7 @@ struct string_list *net_http_headers(struct http_t *state) if (state->error) return NULL; - return state->headers; + return state->response.headers; } /** @@ -1232,7 +1623,7 @@ uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error) if (!state) return NULL; - if (!accept_error && (state->error || state->status < 200 || state->status > 299)) + if (!accept_error && (state->error || state->response.status < 200 || state->response.status > 299)) { if (len) *len = 0; @@ -1240,9 +1631,9 @@ uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error) } if (len) - *len = state->len; + *len = state->response.len; - return (uint8_t*)state->data; + return (uint8_t*)state->response.data; } /** @@ -1255,19 +1646,22 @@ void net_http_delete(struct http_t *state) if (!state) return; - if (state->sock_state.fd >= 0) - { -#ifdef HAVE_SSL - if (state->sock_state.ssl && state->sock_state.ssl_ctx) - { - ssl_socket_close(state->sock_state.ssl_ctx); - ssl_socket_free(state->sock_state.ssl_ctx); - state->sock_state.ssl_ctx = NULL; - } - else -#endif - socket_close(state->sock_state.fd); - } + if (state->conn) + net_http_conn_pool_remove(state->conn); + if (state->request.domain) + free(state->request.domain); + if (state->request.path) + free(state->request.path); + if (state->request.method) + free(state->request.method); + if (state->request.contenttype) + free(state->request.contenttype); + if (state->request.postdata) + free(state->request.postdata); + if (state->request.useragent) + free(state->request.useragent); + if (state->request.headers) + free(state->request.headers); free(state); } @@ -1278,5 +1672,5 @@ void net_http_delete(struct http_t *state) **/ bool net_http_error(struct http_t *state) { - return (state->error || state->status < 200 || state->status > 299); + return (state->error || state->response.status < 200 || state->response.status > 299); } From 72df261ed0ef8a177618edec8b9fe0dba2e3b3ce Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Thu, 23 Jan 2025 12:56:11 -0500 Subject: [PATCH 06/12] Fix opening file inside archive with core detection (#17461) --- menu/cbs/menu_cbs_ok.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 6149f4682ddb..f0d86ce2132f 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -9676,7 +9676,7 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, } else { - if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_ARCHIVE_ACTION_DETECT_CORE)) + if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_ARCHIVE_OPEN_DETECT_CORE)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES)) ) From 81e443c4cfcd97dcfbccf42705d248561c7e4483 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 24 Jan 2025 00:14:32 +0000 Subject: [PATCH 07/12] Fetch translations from Crowdin --- intl/msg_hash_ast.h | 44 +++++++++++++++++++++++++++++++++++++++++ intl/msg_hash_ko.h | 48 +++++++++++++++++++++++++++++++++++++++++++++ intl/msg_hash_ru.h | 32 ++++++++++++++++++++++++++++++ intl/progress.h | 2 +- 4 files changed, 125 insertions(+), 1 deletion(-) diff --git a/intl/msg_hash_ast.h b/intl/msg_hash_ast.h index 175e70e37b1b..e6e56d1c2028 100644 --- a/intl/msg_hash_ast.h +++ b/intl/msg_hash_ast.h @@ -109,6 +109,18 @@ MSG_HASH( MENU_ENUM_SUBLABEL_ADD_CONTENT_LIST, "Crea y anueva llistes pente la busca de conteníu." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_SHOW_WIMP, + "Amosar el menú pa escritorios" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_SHOW_WIMP, + "Abre'l menú tradicional pa escritorios." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MENU_DISABLE_KIOSK_MODE, + "Amuesa toles opciones de configuración rellacionaes." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_ONLINE_UPDATER, "Anovador" @@ -1479,10 +1491,22 @@ MSG_HASH( /* Settings > Core */ +MSG_HASH( + MENU_ENUM_LABEL_VALUE_DRIVER_SWITCH_ENABLE, + "Permitir que los nucleos cambien el controlador de videu" + ) MSG_HASH( MENU_ENUM_SUBLABEL_DUMMY_ON_CORE_SHUTDOWN, "Dalgunos nucleos tienen una función d'apagáu, la carga d'un nucleu maniquín impide que RetroArch cole." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CHECK_FOR_MISSING_FIRMWARE, + "Comprobar el firmware que falte enantes de cargar" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_INFO_CACHE_ENABLE, + "Atroxar na cache los ficheros d'información de los núcleos" + ) #ifndef HAVE_DYNAMIC #endif MSG_HASH( @@ -1512,6 +1536,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_CONFIG_SAVE_ON_EXIT, "Guardar la configuración al colar" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUTO_OVERRIDES_ENABLE, + "Cargar automáticamente los ficheros d'anulación" + ) /* Settings > Saving */ @@ -1964,6 +1992,14 @@ MSG_HASH( /* Settings > AI Service */ +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AI_SERVICE_MODE, + "Salida del serviciu d'IA" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AI_SERVICE_URL, + "URL del serviciu d'IA" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AI_SERVICE_SOURCE_LANG, "Llingua d'orixe" @@ -2887,6 +2923,10 @@ MSG_HASH( MENU_ENUM_LABEL_CHEAT_HANDLER_TYPE_EMU, "Emulador" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_BOXARTS, + "Carátula" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_ON, "Sí" @@ -3411,6 +3451,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER, "Restolador de conteníu" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART, + "Carátula" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS, "Toles llistes" diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index 4dc8b05d0a12..2dc512569741 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -10332,6 +10332,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_CYCLE_THUMBNAILS, "미리보기 전환" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RANDOM_SELECT, + "랜덤 선택" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK, "이전" @@ -15937,9 +15941,53 @@ MSG_HASH( "중지됨." ) #ifdef HAVE_GAME_AI +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_MENU_OPTION, + "AI 플레이어 할당" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_MENU_OPTION, + "AI 플레이어 할당 설명" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_GAME_AI_OPTIONS, + "게임 AI" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P1, + "p1 할당" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P1, + "플레이어 1에 할당" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P2, + "p2 할당" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P2, + "플레이어 2에 할당" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_SHOW_DEBUG, + "디버그 표시" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_SHOW_DEBUG, + "디버그 표시" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_GAME_AI, + "'게임 AI' 표시" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_GAME_AI, + "'게임 AI' 옵션을 표시합니다." + ) #endif \ No newline at end of file diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index a43b4a2a1ed6..7673349a2e6f 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -15865,9 +15865,41 @@ MSG_HASH( "остановлен." ) #ifdef HAVE_GAME_AI +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_MENU_OPTION, + "Замещение игрока ИИ" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_GAME_AI_OPTIONS, + "Игровой ИИ" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P1, + "Заменить Игрока 1" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P2, + "Заменить Игрока 2" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_SHOW_DEBUG, + "Показать отладку" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_SHOW_DEBUG, + "Показать отладку" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QUICK_MENU_SHOW_GAME_AI, + "Показывать 'Игровой ИИ'" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_QUICK_MENU_SHOW_GAME_AI, + "Показывать опцию 'Игровой ИИ'." + ) #endif \ No newline at end of file diff --git a/intl/progress.h b/intl/progress.h index e0a6a233cfac..01ae477496c3 100644 --- a/intl/progress.h +++ b/intl/progress.h @@ -91,7 +91,7 @@ #define LANGUAGE_PROGRESS_JAPANESE_APPROVED 0 /* Korean */ -#define LANGUAGE_PROGRESS_KOREAN_TRANSLATED 99 +#define LANGUAGE_PROGRESS_KOREAN_TRANSLATED 100 #define LANGUAGE_PROGRESS_KOREAN_APPROVED 0 /* Dutch */ From 04cdf0aa4d2b3cab759951ace579aff5cabb6e92 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Fri, 24 Jan 2025 12:05:32 -0500 Subject: [PATCH 08/12] apple: acceleration should include gravity (#17467) --- input/drivers/cocoa_input.m | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/input/drivers/cocoa_input.m b/input/drivers/cocoa_input.m index 9195fabdf224..1470fe549f33 100644 --- a/input/drivers/cocoa_input.m +++ b/input/drivers/cocoa_input.m @@ -742,7 +742,6 @@ static int16_t cocoa_input_state( static void cocoa_input_free(void *data) { - unsigned i; cocoa_input_data_t *apple = (cocoa_input_data_t*)data; if (!apple || !data) @@ -782,6 +781,10 @@ static bool cocoa_input_set_sensor_state(void *data, unsigned port, continue; if (!controller.motion) break; + if (action == RETRO_SENSOR_ACCELEROMETER_ENABLE && !controller.motion.hasGravityAndUserAcceleration) + break; + if (action == RETRO_SENSOR_GYROSCOPE_ENABLE && !controller.motion.hasAttitudeAndRotationRate) + break; if (controller.motion.sensorsRequireManualActivation) { /* This is a bug, we assume if you turn on/off either @@ -827,7 +830,7 @@ static bool cocoa_input_set_sensor_state(void *data, unsigned port, static float cocoa_input_get_sensor_input(void *data, unsigned port, unsigned id) { #ifdef HAVE_MFI - if (@available(iOS 14.0, *)) + if (@available(iOS 14.0, macOS 11.0, tvOS 14.0, *)) { for (GCController *controller in [GCController controllers]) { @@ -838,11 +841,11 @@ static float cocoa_input_get_sensor_input(void *data, unsigned port, unsigned id switch (id) { case RETRO_SENSOR_ACCELEROMETER_X: - return controller.motion.userAcceleration.x; + return controller.motion.acceleration.x; case RETRO_SENSOR_ACCELEROMETER_Y: - return controller.motion.userAcceleration.y; + return controller.motion.acceleration.y; case RETRO_SENSOR_ACCELEROMETER_Z: - return controller.motion.userAcceleration.z; + return controller.motion.acceleration.z; case RETRO_SENSOR_GYROSCOPE_X: return controller.motion.rotationRate.x; case RETRO_SENSOR_GYROSCOPE_Y: @@ -860,11 +863,11 @@ static float cocoa_input_get_sensor_input(void *data, unsigned port, unsigned id switch (id) { case RETRO_SENSOR_ACCELEROMETER_X: - return motionManager.deviceMotion.userAcceleration.x; + return motionManager.deviceMotion.gravity.x + motionManager.deviceMotion.userAcceleration.x; case RETRO_SENSOR_ACCELEROMETER_Y: - return motionManager.deviceMotion.userAcceleration.y; + return motionManager.deviceMotion.gravity.y + motionManager.deviceMotion.userAcceleration.y; case RETRO_SENSOR_ACCELEROMETER_Z: - return motionManager.deviceMotion.userAcceleration.z; + return motionManager.deviceMotion.gravity.z + motionManager.deviceMotion.userAcceleration.z; case RETRO_SENSOR_GYROSCOPE_X: return motionManager.deviceMotion.rotationRate.x; case RETRO_SENSOR_GYROSCOPE_Y: From 120465d676afe4ef6dae8a0c48fb7af359a7067f Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Fri, 24 Jan 2025 19:37:28 +0100 Subject: [PATCH 09/12] Fix runahead.c --- runahead.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runahead.c b/runahead.c index 0f26be66321f..473320cd6286 100644 --- a/runahead.c +++ b/runahead.c @@ -150,7 +150,7 @@ static void strcat_alloc(char **dst, const char *s) { size_t __len = strlen(s); if (__len != 0) - src = strldup(s, __len); + src = strldup(s, __len + 1); else src = NULL; } @@ -239,7 +239,7 @@ static char *get_tmpdir_alloc(const char *override_dir) { size_t _len = strlen(src); if (_len != 0) - path = strldup(src, _len); + path = strldup(src, _len + 1); } else path = (char*)calloc(1,1); @@ -264,7 +264,7 @@ static bool write_file_with_random_name(char **temp_dll_path, { size_t _len = strlen(src); if (_len != 0) - ext = strldup(src, _len); + ext = strldup(src, _len + 1); } else ext = (char*)calloc(1,1); From 8814eb3eb63c7c8383bd55421639a02c349fb178 Mon Sep 17 00:00:00 2001 From: zoltanvb <101990835+zoltanvb@users.noreply.github.com> Date: Fri, 24 Jan 2025 23:49:41 +0100 Subject: [PATCH 10/12] Changes up to 2025-01-24 (#17469) --- CHANGES.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 5e9189bc7aae..20b9d20a4bde 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,41 @@ # Future +- 3DS: Fix unique IDs for newer cores +- AUTOCONF: Enable alternative display name in autoconfig files - AUDIO/PIPEWIRE: Fix app launch when pipewire service is stopped +- AUDIO/PIPEWIRE: Fix speedup with threaded video mode +- AUDIO/PIPEWIRE: Fix latency setting and microphone handling +- CHEEVOS: Include achievement state in netplay states +- CLOUDSYNC: Fix Windows path issues +- CLOUDSYNC: Workaround for duplicated requests bug - GENERAL: Fix save state auto increment - GENERAL: Fix softpatching with periods/dots in the file name - GENERAL: Fix compilation with --enable-videocore -- MENU: Add SSL support to the information list +- GENERAL: Allow asset directory redefinition and other directory overrides via environment variables +- GENERAL: Allow override of player 1/2 input with machine learning models (needs recompilation and external library) +- INPUT: Fix a crash when initializing illuminance sensor on Linux +- INPUT: Analog-to-digital refactor, fixing behavior when analogs are assigned to keys +- INPUT/COCOA: Include gravity in acceleration sensor values +- INPUT/WINRAW: Invert mouse index order - IOS: Ensure webserver notice can be dismissed +- IOS: Fix clean playlist function +- MACOS: Fix some sandbox handling in App Store builds +- MACOS: Reset keyboard state when focus is lost +- MENU: Add SSL support to the information list +- MENU: Add warning to BFI and related menu items +- MENU: Fix latency statistics when using runahead +- MENU: Fix opening file inside archive with core selection +- MENU/PLAYLISTS: Random selection/shuffle function +- MENU/QT: Fix desktop menu crash with Cheevos disabled +- MENU/RGUI: Cleanups of certain menu items +- NETWORK: Refactor of net_http, improvements for task blocking and performance +- OVERLAY: Preferred overlay loading is now default only on mobile platforms +- TVOS: Fix 720p display +- TVOS: Fix refresh rate fetching on tvOS 13/14 +- SAVESTATES: Reset state index when loading new content +- UWP: Fix slang shader compilation +- VIDEO: Enable BFI setting for mobile platforms (mind the warnings) - VIDEO/SHADERS: Allow exact refresh rate sync with shader subframes +- WEBPLAYER: Update core list for 1.20.0 # 1.20.0 - AUDIO: Fix audio handling in case of RARCH_NETPLAY_CTL_USE_CORE_PACKET_INTERFACE From c11d9bee2e4e3abbae5a23b82add2553ddd8e8cd Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 25 Jan 2025 00:13:19 +0000 Subject: [PATCH 11/12] Fetch translations from Crowdin --- intl/msg_hash_de.h | 36 ++++++++++++++++++++++++++++++++++++ intl/msg_hash_en.h | 8 ++++++++ intl/progress.h | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index f219617d8e1d..64aa61e575f5 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -15485,9 +15485,45 @@ MSG_HASH( "gestoppt." ) #ifdef HAVE_GAME_AI +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_MENU_OPTION, + "KI-Player überschreiben" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_MENU_OPTION, + "KI-Spieler überschreiben Unterlabel" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CORE_GAME_AI_OPTIONS, + "Spiel-KI" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P1, + "P1 überschreiben" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P1, + "Spieler 01 überschreiben" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_OVERRIDE_P2, + "P2 überschreiben" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_OVERRIDE_P2, + "Spieler 02 überschreiben" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_GAME_AI_SHOW_DEBUG, + "Fehlerinformation anzeigen" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_GAME_AI_SHOW_DEBUG, + "Fehlerinformation anzeigen" + ) #endif \ No newline at end of file diff --git a/intl/msg_hash_en.h b/intl/msg_hash_en.h index 5c06d0b400fd..197dcc1373db 100644 --- a/intl/msg_hash_en.h +++ b/intl/msg_hash_en.h @@ -838,6 +838,14 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_SHOW_MOUSE_CURSOR, "Show the Mouse Cursor With Overlay" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_OVERLAY_ANALOG_RECENTER_ZONE, + "Analogue Recentring Zone" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_OVERLAY_ANALOG_RECENTER_ZONE, + "Analogue stick input will be relative to first touch if pressed within this zone." + ) /* Settings > On-Screen Display > On-Screen Overlay > Keyboard Overlay */ diff --git a/intl/progress.h b/intl/progress.h index 01ae477496c3..81a52a323e2b 100644 --- a/intl/progress.h +++ b/intl/progress.h @@ -39,7 +39,7 @@ #define LANGUAGE_PROGRESS_GREEK_APPROVED 0 /* English, United Kingdom */ -#define LANGUAGE_PROGRESS_ENGLISH_UNITED_KINGDOM_TRANSLATED 99 +#define LANGUAGE_PROGRESS_ENGLISH_UNITED_KINGDOM_TRANSLATED 100 #define LANGUAGE_PROGRESS_ENGLISH_UNITED_KINGDOM_APPROVED 0 /* Esperanto */ From b982ff64c2725f872b532ebbbac158db00885bf5 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Sat, 25 Jan 2025 22:10:25 -0500 Subject: [PATCH 12/12] cheevos: fix crash entering achievements in quick menu when no client (#17472) --- cheevos/cheevos_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cheevos/cheevos_menu.c b/cheevos/cheevos_menu.c index a9cb09423b98..a182d909a8e2 100644 --- a/cheevos/cheevos_menu.c +++ b/cheevos/cheevos_menu.c @@ -303,7 +303,7 @@ void rcheevos_menu_populate(void* data) rcheevos_menu_reset_badges(); rcheevos_locals->menuitem_count = 0; - if (rcheevos_locals->client->state.disconnect) + if (rcheevos_locals->client && rcheevos_locals->client->state.disconnect) { menu_entries_append(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_SERVER_UNREACHABLE),