diff --git a/Makefile.common b/Makefile.common index 58aa2c0240..71fb5c5f4f 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1165,7 +1165,8 @@ ifeq ($(HAVE_NETWORKING), 1) network/net_http_special.o \ tasks/task_http.o \ tasks/task_netplay_lan_scan.o \ - tasks/task_wifi.o + tasks/task_wifi.o \ + tasks/task_netplay_crc.o ifneq ($(HAVE_SOCKET_LEGACY),1) OBJ += $(LIBRETRO_COMM_DIR)/net/net_ifinfo.o diff --git a/command.c b/command.c index e3c39434d7..bbc61603a2 100644 --- a/command.c +++ b/command.c @@ -2449,6 +2449,24 @@ bool command_event(enum event_command cmd, void *data) } } break; + case CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED: + { + /* buf is expected to be address:port, there must be a better way + to do this but for now I'll just use a string list */ + char *buf = (char *) data; + static struct string_list *hostname = NULL; + hostname = string_split(buf, ":"); + + settings_t *settings = config_get_ptr(); + command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); + if (!init_netplay_deferred( + hostname->elems[0].data, atoi(hostname->elems[1].data))) + { + command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); + return false; + } + } + break; case CMD_EVENT_NETPLAY_FLIP_PLAYERS: netplay_driver_ctl(RARCH_NETPLAY_CTL_FLIP_PLAYERS, NULL); break; @@ -2461,6 +2479,7 @@ bool command_event(enum event_command cmd, void *data) case CMD_EVENT_NETWORK_INIT: case CMD_EVENT_NETPLAY_INIT: case CMD_EVENT_NETPLAY_INIT_DIRECT: + case CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED: case CMD_EVENT_NETPLAY_FLIP_PLAYERS: case CMD_EVENT_NETPLAY_GAME_WATCH: return false; diff --git a/command.h b/command.h index 19d8009c86..f15b108388 100644 --- a/command.h +++ b/command.h @@ -171,6 +171,8 @@ enum event_command CMD_EVENT_NETPLAY_INIT, /* Initializes netplay system with a direct host specified. */ CMD_EVENT_NETPLAY_INIT_DIRECT, + /* Initializes netplay system with a direct host specified after loading content. */ + CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, /* Deinitializes netplay system. */ CMD_EVENT_NETPLAY_DEINIT, /* Flip netplay players. */ diff --git a/file_path_special.h b/file_path_special.h index 521866143a..9a119dad7d 100644 --- a/file_path_special.h +++ b/file_path_special.h @@ -53,6 +53,7 @@ enum file_path_enum FILE_PATH_LAKKA_URL, FILE_PATH_CORE_THUMBNAILS_URL, FILE_PATH_INDEX_DIRS_URL, + FILE_PATH_NETPLAY_ROOM_LIST_URL, FILE_PATH_INDEX_URL, FILE_PATH_INDEX_EXTENDED_URL, FILE_PATH_CGP_EXTENSION, diff --git a/file_path_str.c b/file_path_str.c index fea26786c2..77009bcf46 100644 --- a/file_path_str.c +++ b/file_path_str.c @@ -101,6 +101,8 @@ const char *file_path_str(enum file_path_enum enum_idx) return ".index-dirs"; case FILE_PATH_INDEX_EXTENDED_URL: return ".index-extended"; + case FILE_PATH_NETPLAY_ROOM_LIST_URL: + return "registry.lpl"; case FILE_PATH_CORE_THUMBNAILS_URL: return "http://thumbnailpacks.libretro.com"; case FILE_PATH_LAKKA_URL: diff --git a/griffin/griffin.c b/griffin/griffin.c index 570b6eba30..70a1363f49 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -910,6 +910,7 @@ NETPLAY #include "../tasks/task_http.c" #include "../tasks/task_netplay_lan_scan.c" #include "../tasks/task_wifi.c" +#include "../tasks/task_netplay_crc.c" #endif /*============================================================ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index b223e9fa33..412e0148e3 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -14,6 +14,8 @@ MSG_HASH(MENU_ENUM_LABEL_CONFIGURATIONS_LIST, "configurations_list") MSG_HASH(MENU_ENUM_LABEL_ADD_TAB, "add_tab") +MSG_HASH(MENU_ENUM_LABEL_NETPLAY_TAB, + "netplay_tab") MSG_HASH(MENU_ENUM_LABEL_ARCHIVE_MODE, "archive_mode") MSG_HASH(MENU_ENUM_LABEL_ASSETS_DIRECTORY, @@ -155,6 +157,8 @@ MSG_HASH(MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT, "config_save_on_exit") MSG_HASH(MENU_ENUM_LABEL_CONNECT_WIFI, "connect_wifi") +MSG_HASH(MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM, + "connect_room") MSG_HASH(MENU_ENUM_LABEL_CONTENT_ACTIONS, "content_actions") MSG_HASH(MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST, @@ -837,6 +841,8 @@ MSG_HASH(MENU_ENUM_LABEL_SCAN_DIRECTORY, "scan_directory") MSG_HASH(MENU_ENUM_LABEL_SCAN_FILE, "scan_file") +MSG_HASH(MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS, + "refresh_rooms") MSG_HASH(MENU_ENUM_LABEL_SCAN_THIS_DIRECTORY, "scan_this_directory") MSG_HASH(MENU_ENUM_LABEL_SCREENSHOT_DIRECTORY, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 4301bdd150..ffcfb615bf 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -199,6 +199,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_ADD_TAB, "Import content" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, + "Netplay Rooms" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_ARCHIVE_MODE, "Archive File Association Action" @@ -967,13 +971,13 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Delay Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, - "Disconnect") + "Disconnect from netplay host") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE, "Netplay Enable") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_CLIENT, - "Connect to Netplay host") + "Connect to netplay host") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_HOST, - "Start hosting") + "Start netplay host") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_IP_ADDRESS, "Server Address") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LAN_SCAN_SETTINGS, diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 061b8c533c..57f2b9bf6b 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -487,6 +487,7 @@ static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs, string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)) || + string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)) || @@ -586,6 +587,7 @@ static int menu_cbs_init_bind_left_compare_type(menu_file_list_cbs_t *cbs, if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)) || + string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)) || @@ -625,6 +627,7 @@ int menu_cbs_init_bind_left(menu_file_list_cbs_t *cbs, if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)) || + string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)) || diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 83c76b60cb..1c19c249ac 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -3117,6 +3117,44 @@ static int action_ok_netplay_lan_scan_list(const char *path, entry_idx, ACTION_OK_DL_NETPLAY_LAN_SCAN_SETTINGS_LIST); } +static int action_ok_netplay_connect_room(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ +#ifdef HAVE_NETWORKING + settings_t *settings = config_get_ptr(); + + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) + command_event(CMD_EVENT_NETPLAY_DEINIT, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); + + char tmp_hostname[512]; + + /* For testing purposes + strlcpy(tmp_hostname, "192.168.1.241", sizeof(tmp_hostname));*/ + strlcpy(tmp_hostname, netplay_room_list[idx - 1].address, sizeof(tmp_hostname)); + snprintf(tmp_hostname, sizeof(tmp_hostname), "%s:%d", + netplay_room_list[idx - 1].address, netplay_room_list[idx - 1].port); + RARCH_LOG("%s %s %08x", netplay_room_list[idx - 1].address, netplay_room_list[idx - 1].gamename, netplay_room_list[idx - 1].gamecrc); + /* If we haven't yet started, this will load on its own */ + if (!content_is_inited()) + { + task_push_netplay_crc_scan(netplay_room_list[idx - 1].gamecrc, + tmp_hostname, netplay_room_list[idx - 1].corename); + } + else + { + /* Enable Netplay itself */ + if (!command_event(CMD_EVENT_NETPLAY_INIT, (void *) tmp_hostname)) + return -1; + } + +#else + return -1; + +#endif + return 0; +} + static int action_ok_lakka_services(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -3264,6 +3302,8 @@ static int action_ok_push_content_list(const char *path, entry_idx, ACTION_OK_DL_CONTENT_LIST); } + + static int action_ok_push_scan_file(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -3271,6 +3311,126 @@ static int action_ok_push_scan_file(const char *path, return action_ok_push_content_list(path, label, type, idx, entry_idx); } +static void netplay_refresh_rooms_cb(void *task_data, void *user_data, const char *err) +{ + char buf[PATH_MAX_LENGTH]; + + http_transfer_data_t *data = (http_transfer_data_t*)task_data; + menu_file_transfer_t *state = (menu_file_transfer_t*)user_data; + + buf[0] = '\0'; + + if (!data || err) + goto finish; + + memcpy(buf, data->data, data->len * sizeof(char)); + buf[data->len] = '\0'; + + +finish: + if (!err && !strstr(buf, file_path_str(FILE_PATH_NETPLAY_ROOM_LIST_URL))) + { + if (string_is_empty(buf)) + { + netplay_room_count = 0; + RARCH_LOG("Room list empty\n"); + } + else + { + int i, j = 0; + char tmp[PATH_MAX_LENGTH]; + static struct string_list *room_data = NULL; + file_list_t *file_list = NULL; + file_list = menu_entries_get_selection_buf_ptr(0); + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, file_list); + + room_data = string_split(buf, "\n"); + + if (netplay_room_list) + free(netplay_room_list); + + netplay_room_count = room_data->size / 8; + netplay_room_list = (struct netplay_room*)malloc(sizeof(struct netplay_room) * netplay_room_count); + + /*for (int i = 0; i < room_data->size; i++) + { + strlcpy(tmp, + room_data->elems[i].data, sizeof(tmp)); + RARCH_LOG("tmp %s\n", tmp); + + }*/ + menu_entries_append_enum(file_list, + "Refresh Room List", + msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS), + MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS, + MENU_SETTING_ACTION, 0, 0); + + RARCH_LOG ("Found %d rooms\n", netplay_room_count); + for (i = 0; i < netplay_room_count; i++) + { + strlcpy(netplay_room_list[i].nickname, room_data->elems[j + 0].data, sizeof(netplay_room_list[i].nickname)); + strlcpy(netplay_room_list[i].address, room_data->elems[j + 1].data, sizeof(netplay_room_list[i].address)); + strlcpy(netplay_room_list[i].corename, room_data->elems[j + 3].data, sizeof(netplay_room_list[i].corename)); + strlcpy(netplay_room_list[i].coreversion, room_data->elems[j + 4].data, sizeof(netplay_room_list[i].coreversion)); + strlcpy(netplay_room_list[i].gamename, room_data->elems[j + 5].data, sizeof(netplay_room_list[i].coreversion)); + netplay_room_list[i].port = atoi(room_data->elems[j + 2].data); + netplay_room_list[i].gamecrc = atoi(room_data->elems[j + 6].data); + netplay_room_list[i].timestamp = atoi(room_data->elems[j + 7].data); + + RARCH_LOG("Room Data: %d\n" + "Nickname: %s\n" + "Address: %s\n" + "Port: %d\n" + "Core: %s\n" + "Core Version: %s\n" + "Game: %s\n" + "Game CRC: %08x\n" + "Timestamp: %d\n", room_data->elems[j + 6].data, + netplay_room_list[i].nickname, + netplay_room_list[i].address, + netplay_room_list[i].port, + netplay_room_list[i].corename, + netplay_room_list[i].coreversion, + netplay_room_list[i].gamename, + netplay_room_list[i].gamecrc, + netplay_room_list[i].timestamp); + j+=8; + char s[PATH_MAX_LENGTH]; + snprintf(s, sizeof(s), "Nickname: %s", netplay_room_list[i].nickname); + menu_entries_append_enum(file_list, + s, + msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM), + MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM, + MENU_WIFI, 0, 0); + + } + } + } + + if (err) + RARCH_ERR("%s: %s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED), err); + + if (data) + { + if (data->data) + free(data->data); + free(data); + } + + if (user_data) + free(user_data); + +} + +static int action_ok_push_netplay_refresh_rooms(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + char url [2048] = "http://lobby.libretro.com/raw/"; + task_push_http_transfer(url, true, NULL, netplay_refresh_rooms_cb, NULL); + return 0; +} + + static int action_ok_scan_directory_list(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -3781,7 +3941,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, first_char = atoi(&str[0]); - if (first_char != (i+1)) + if (first_char != ((i+1))) continue; BIND_ACTION_OK(cbs, action_ok_push_user_binds_list); @@ -3994,6 +4154,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_SCAN_FILE: BIND_ACTION_OK(cbs, action_ok_push_scan_file); break; + case MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS: + BIND_ACTION_OK(cbs, action_ok_push_netplay_refresh_rooms); + break; case MENU_ENUM_LABEL_FAVORITES: BIND_ACTION_OK(cbs, action_ok_push_content_list); break; @@ -4098,6 +4261,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_NETPLAY_LAN_SCAN_SETTINGS: BIND_ACTION_OK(cbs, action_ok_netplay_lan_scan_list); break; + case MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM: + BIND_ACTION_OK(cbs, action_ok_netplay_connect_room); + break; case MENU_ENUM_LABEL_LAKKA_SERVICES: BIND_ACTION_OK(cbs, action_ok_lakka_services); break; diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 2cd25bf54a..fa8494ea14 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -474,6 +474,7 @@ static int menu_cbs_init_bind_right_compare_type(menu_file_list_cbs_t *cbs, if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)) || + string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)) || @@ -594,6 +595,7 @@ static int menu_cbs_init_bind_right_compare_label(menu_file_list_cbs_t *cbs, if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)) || + string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)) || @@ -632,6 +634,7 @@ int menu_cbs_init_bind_right(menu_file_list_cbs_t *cbs, if ( string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)) || + string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)) || string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)) || diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index ed4c0ee8e3..cc9cb3ac92 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -21,6 +21,7 @@ #ifdef HAVE_CHEEVOS #include "../../cheevos.h" #endif +#include "../../verbosity.h" #ifndef BIND_ACTION_SUBLABEL #define BIND_ACTION_SUBLABEL(cbs, name) \ @@ -28,6 +29,11 @@ cbs->action_sublabel_ident = #name; #endif +#ifdef HAVE_NETWORKING +#include "../../network/netplay/netplay.h" +#include "../../network/netplay/netplay_discovery.h" +#endif + #define default_sublabel_macro(func_name, lbl) \ static int (func_name)(file_list_t *list, unsigned type, unsigned i, const char *label, const char *path, char *s, size_t len) \ { \ @@ -117,6 +123,8 @@ default_sublabel_macro(action_bind_sublabel_content_history_size, MENU_ default_sublabel_macro(action_bind_sublabel_menu_input_unified_controls, MENU_ENUM_SUBLABEL_INPUT_UNIFIED_MENU_CONTROLS) default_sublabel_macro(action_bind_sublabel_onscreen_notifications_enable, MENU_ENUM_SUBLABEL_VIDEO_FONT_ENABLE) +/* MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM*/ + static int action_bind_sublabel_cheevos_entry( file_list_t *list, unsigned type, unsigned i, @@ -136,6 +144,22 @@ static int action_bind_sublabel_cheevos_entry( return 0; } +static int action_bind_sublabel_netplay_room( + file_list_t *list, + unsigned type, unsigned i, + const char *label, const char *path, + char *s, size_t len) +{ + if (i < 1) + return 0; + + snprintf(s,len, "%s (%s)\n%s (%08x)", + netplay_room_list[i - 1].corename, netplay_room_list[i - 1].coreversion, + netplay_room_list[i - 1].gamename, netplay_room_list[i - 1].gamecrc); + //strlcpy(s, netplay_room_list[i - 1].corename, len); + return 0; +} + static int action_bind_sublabel_generic( file_list_t *list, unsigned type, unsigned i, @@ -233,6 +257,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cheevos_entry); break; + case MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_room); + break; case MENU_ENUM_LABEL_CHEEVOS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cheevos_enable); break; diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 54c238eabd..9cfc7df7ac 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -257,6 +257,8 @@ static int action_get_title_group_settings(const char *path, const char *label, strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB), len); else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB))) strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ADD_TAB), len); + else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB))) + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_TAB), len); else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU))) strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HORIZONTAL_MENU), len); else diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 982e3a1215..932192fedf 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -96,6 +96,9 @@ enum XMB_TEXTURE_MUSICS, XMB_TEXTURE_MOVIES, #endif +#ifdef HAVE_NETWORKING + XMB_TEXTURE_NETPLAY, +#endif #ifdef HAVE_IMAGEVIEWER XMB_TEXTURE_IMAGES, #endif @@ -151,10 +154,15 @@ enum #ifdef HAVE_IMAGEVIEWER XMB_SYSTEM_TAB_IMAGES, #endif - XMB_SYSTEM_TAB_ADD + XMB_SYSTEM_TAB_ADD, +#ifdef HAVE_NETWORKING + XMB_SYSTEM_TAB_NETPLAY +#endif }; -#ifdef HAVE_LIBRETRODB +#ifdef HAVE_NETWORKING +#define XMB_SYSTEM_TAB_END XMB_SYSTEM_TAB_NETPLAY +#elif defined(HAVE_LIBRETRODB) #define XMB_SYSTEM_TAB_END XMB_SYSTEM_TAB_ADD #elif defined(HAVE_IMAGEVIEWER) #define XMB_SYSTEM_TAB_END XMB_SYSTEM_TAB_IMAGES @@ -164,6 +172,7 @@ enum #define XMB_SYSTEM_TAB_END XMB_SYSTEM_TAB_HISTORY #endif + typedef struct xmb_handle { file_list_t *menu_stack_old; @@ -300,6 +309,7 @@ typedef struct xmb_handle xmb_node_t settings_tab_node; xmb_node_t history_tab_node; xmb_node_t add_tab_node; + xmb_node_t netplay_tab_node; font_data_t *font; font_data_t *font2; @@ -1474,6 +1484,8 @@ static xmb_node_t* xmb_get_node(xmb_handle_t *xmb, unsigned i) return &xmb->history_tab_node; case XMB_SYSTEM_TAB_ADD: return &xmb->add_tab_node; + case XMB_SYSTEM_TAB_NETPLAY: + return &xmb->netplay_tab_node; default: if (i > xmb->system_tab_end) return xmb_get_userdata_from_horizontal_list( @@ -3217,6 +3229,9 @@ static void *xmb_init(void **userdata) if (settings->menu.xmb.show_add) xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_ADD; #endif +#ifdef HAVE_NETWORKING + xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_NETPLAY; +#endif menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); @@ -3432,6 +3447,8 @@ static const char *xmb_texture_path(unsigned id) return "off.png"; case XMB_TEXTURE_ADD: return "add.png"; + case XMB_TEXTURE_NETPLAY: + return "wifi.png"; case XMB_TEXTURE_KEY: return "key.png"; case XMB_TEXTURE_KEY_HOVER: @@ -3482,6 +3499,12 @@ static void xmb_context_reset_textures( xmb->add_tab_node.icon = xmb->textures.list[XMB_TEXTURE_ADD]; xmb->add_tab_node.alpha = xmb->categories.active.alpha; xmb->add_tab_node.zoom = xmb->categories.active.zoom; + +#ifdef HAVE_NETWORKING + xmb->netplay_tab_node.icon = xmb->textures.list[XMB_TEXTURE_NETPLAY]; + xmb->netplay_tab_node.alpha = xmb->categories.active.alpha; + xmb->netplay_tab_node.zoom = xmb->categories.active.zoom; +#endif } static void xmb_context_reset_background(const char *iconpath) @@ -3794,6 +3817,12 @@ static void xmb_list_cache(void *data, enum menu_list_type type, unsigned action menu_stack->list[stack_size - 1].type = MENU_ADD_TAB; break; + case XMB_SYSTEM_TAB_NETPLAY: + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)); + menu_stack->list[stack_size - 1].type = + MENU_NETPLAY_TAB; + break; default: menu_stack->list[stack_size - 1].label = strdup(msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU)); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 3ee45beecf..18ade8a5cf 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -244,11 +244,6 @@ static int menu_displaylist_parse_netplay( MENU_ENUM_LABEL_NETPLAY_DISCONNECT, MENU_SETTING_ACTION, 0, 0); - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS), - msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_SETTINGS), - MENU_ENUM_LABEL_NETWORK_SETTINGS, MENU_SETTING_GROUP, 0, 0); - menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_LAN_SCAN_SETTINGS), msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_LAN_SCAN_SETTINGS), @@ -2693,6 +2688,11 @@ static int menu_displaylist_parse_load_content_settings( MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS, MENU_SETTING_ACTION, 0, 0); +#ifdef HAVE_NETWORKING + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY, + PARSE_ACTION, false); +#endif menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_CHEAT_OPTIONS), @@ -2926,6 +2926,7 @@ static int menu_displaylist_parse_add_content_list( static int menu_displaylist_parse_scan_directory_list( menu_displaylist_info_t *info) { + #ifdef HAVE_LIBRETRODB menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY), @@ -2943,6 +2944,37 @@ static int menu_displaylist_parse_scan_directory_list( return 0; } +static int menu_displaylist_parse_netplay_room_list( + menu_displaylist_info_t *info) +{ + +#ifdef HAVE_NETWORKING + menu_entries_append_enum(info->list, + "Refresh Room List", + msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS), + MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS, + MENU_SETTING_ACTION, 0, 0); + + if (netplay_room_count > 0) + { + unsigned i; + for (i = 0; i < netplay_room_count; i++) + { + char s[PATH_MAX_LENGTH]; + snprintf(s, sizeof(s), "Nickname: %s", netplay_room_list[i].nickname); + const char *label = s; + menu_entries_append_enum(info->list, + label, + msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM), + MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM, + MENU_WIFI, 0, 0); + } + } +#endif + + return 0; +} + static int menu_displaylist_parse_options( menu_displaylist_info_t *info) { @@ -3777,6 +3809,12 @@ static bool menu_displaylist_push_internal( return false; return true; } + else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB))) + { + if (!menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info)) + return false; + return true; + } else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU))) { if (!menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info)) @@ -3905,6 +3943,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) case DISPLAYLIST_ADD_CONTENT_LIST: case DISPLAYLIST_CONFIGURATIONS_LIST: case DISPLAYLIST_SCAN_DIRECTORY_LIST: + case DISPLAYLIST_NETPLAY_ROOM_LIST: case DISPLAYLIST_LOAD_CONTENT_LIST: case DISPLAYLIST_USER_BINDS_LIST: case DISPLAYLIST_ACCOUNTS_LIST: @@ -5308,6 +5347,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) case DISPLAYLIST_SCAN_DIRECTORY_LIST: ret = menu_displaylist_parse_scan_directory_list(info); + info->need_push = true; + info->need_refresh = true; + break; + case DISPLAYLIST_NETPLAY_ROOM_LIST: + ret = menu_displaylist_parse_netplay_room_list(info); + info->need_push = true; info->need_refresh = true; break; diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 0c68697c87..043ff81356 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -151,6 +151,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_ADD_CONTENT_LIST, DISPLAYLIST_CONFIGURATIONS_LIST, DISPLAYLIST_SCAN_DIRECTORY_LIST, + DISPLAYLIST_NETPLAY_ROOM_LIST, DISPLAYLIST_ARCHIVE_ACTION, DISPLAYLIST_ARCHIVE_ACTION_DETECT_CORE, DISPLAYLIST_CORE_CONTENT, diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 7a0b1bc8a0..5eafc3f88b 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -50,6 +50,7 @@ RETRO_BEGIN_DECLS #define MENU_SETTINGS_CORE_OPTION_START 0x10000 #define MENU_SETTINGS_PLAYLIST_ASSOCIATION_START 0x20000 #define MENU_SETTINGS_CHEEVOS_START 0x40000 +#define MENU_SETTINGS_NETPLAY_ROOMS_START 0x80000 enum menu_image_type { @@ -154,6 +155,7 @@ enum menu_settings_type MENU_MUSIC_TAB, MENU_VIDEO_TAB, MENU_IMAGES_TAB, + MENU_NETPLAY_TAB, MENU_ADD_TAB, MENU_PLAYLISTS_TAB, MENU_SETTING_NO_ITEM, diff --git a/msg_hash.h b/msg_hash.h index 863c19246d..dffb8238ff 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -746,6 +746,7 @@ enum msg_hash_enums MENU_LABEL(NETPLAY_LAN_SCAN_SETTINGS), MENU_ENUM_LABEL_CONNECT_WIFI, + MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM, MENU_ENUM_LABEL_CONNECT_NETPLAY_LAN, MENU_LABEL(MENU_ENUM_LINEAR_FILTER), @@ -867,6 +868,7 @@ enum msg_hash_enums MENU_LABEL(SCAN_THIS_DIRECTORY), MENU_LABEL(SCAN_DIRECTORY), MENU_LABEL(SCAN_FILE), + MENU_LABEL(NETPLAY_REFRESH_ROOMS), MENU_LABEL(ADD_CONTENT_LIST), MENU_LABEL(CONFIGURATIONS_LIST), @@ -1146,6 +1148,7 @@ enum msg_hash_enums MENU_LABEL(SETTINGS_TAB), MENU_LABEL(HISTORY_TAB), MENU_LABEL(ADD_TAB), + MENU_LABEL(NETPLAY_TAB), MENU_LABEL(PLAYLISTS_TAB), MENU_LABEL(MAIN_MENU), MENU_LABEL(INPUT_SETTINGS), diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 13f3249e17..e6fd5f6488 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -57,6 +57,8 @@ void audio_sample_net(int16_t left, int16_t right); size_t audio_sample_batch_net(const int16_t *data, size_t frames); +bool init_netplay_deferred(const char* server, unsigned port); + /** * init_netplay * @direct_host : Host to connect to directly, if applicable (client only) diff --git a/network/netplay/netplay_discovery.h b/network/netplay/netplay_discovery.h index d593af6e64..2eec52aafe 100644 --- a/network/netplay/netplay_discovery.h +++ b/network/netplay/netplay_discovery.h @@ -18,6 +18,7 @@ #define __RARCH_NETPLAY_DISCOVERY_H #include +#include #define NETPLAY_HOST_STR_LEN 32 @@ -44,6 +45,24 @@ struct netplay_host_list { size_t size; }; +/* data is ordered like this on the server, I left it in this ordered + for reference */ +struct netplay_room +{ + char nickname [PATH_MAX_LENGTH]; + char address [PATH_MAX_LENGTH]; + int port; + char corename [PATH_MAX_LENGTH]; + char coreversion [PATH_MAX_LENGTH]; + char gamename [PATH_MAX_LENGTH]; + int gamecrc; + int timestamp; +}; + +struct netplay_room *netplay_room_list; + +int netplay_room_count; + /** Initialize Netplay discovery */ bool init_netplay_discovery(void); diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index ec0266bf52..fc5dc88c7a 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "netplay_private.h" @@ -28,6 +29,11 @@ #include "../../input/input_driver.h" #include "../../runloop.h" +#include "../../tasks/tasks_internal.h" +#include +#include "../../file_path_special.h" +#include "paths.h" + /* Only used before init_netplay */ static bool netplay_enabled = false; static bool netplay_is_client = false; @@ -38,6 +44,11 @@ static netplay_t *netplay_data = NULL; /* Used to avoid recursive netplay calls */ static bool in_netplay = false; +/* Used for deferred netplay initialization */ +static bool netplay_client_deferred = false; +static char server_address_deferred[512] = ""; +static unsigned server_port_deferred = 0; + /** * netplay_is_alive: * @netplay : pointer to netplay object @@ -151,6 +162,20 @@ static bool get_self_input_state(netplay_t *netplay) return true; } +bool init_netplay_deferred(const char* server, unsigned port) +{ + if (!string_is_empty(server) && port != 0) + { + strlcpy(server_address_deferred, server, sizeof(server_address_deferred)); + server_port_deferred = port; + netplay_client_deferred = true; + } + else + netplay_client_deferred = false; + return netplay_client_deferred; +} + + /** * netplay_poll: * @netplay : pointer to netplay object @@ -390,6 +415,36 @@ static int16_t netplay_input_state(netplay_t *netplay, } } +static int reannounce = 0; + +static void netplay_announce_cb(void *task_data, void *user_data, const char *error) +{ + RARCH_LOG("Announcing netplay game... \n"); + return; +} + +static void netplay_announce() +{ + rarch_system_info_t *system = NULL; + settings_t *settings = config_get_ptr(); + uint32_t *content_crc_ptr = NULL; + content_get_crc(&content_crc_ptr); + + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); + char url [2048] = "http://lobby.libretro.com/raw/?"; + char buf [2048]; + buf[0] = '\0'; + + snprintf(buf, sizeof(buf), "%susername=%s&corename=%s&coreversion=%s&" + "gamename=%s&gamecrc=%d&port=%d", + url, settings->username, system->info.library_name, + system->info.library_version, + path_basename(path_get(RARCH_PATH_BASENAME)),*content_crc_ptr, + settings->netplay.port); + + task_push_http_transfer(buf, true, NULL, netplay_announce_cb, NULL); +} + int16_t input_state_net(unsigned port, unsigned device, unsigned idx, unsigned id) { @@ -519,7 +574,9 @@ static void netplay_frontend_paused(netplay_t *netplay, bool paused) bool netplay_pre_frame(netplay_t *netplay) { bool sync_stalled; - + reannounce ++; + if (netplay->is_server && (reannounce % 3600 == 0)) + netplay_announce(); retro_assert(netplay); /* FIXME: This is an ugly way to learn we're not paused anymore */ @@ -883,12 +940,14 @@ bool init_netplay(void *direct_host, const char *server, unsigned port) runloop_msg_queue_push( msg_hash_to_str(MSG_WAITING_FOR_CLIENT), 0, 180, false); + + netplay_announce(); } netplay_data = (netplay_t*)netplay_new( netplay_is_client ? direct_host : NULL, - netplay_is_client ? server : NULL, - port ? port : RARCH_DEFAULT_PORT, + netplay_is_client ? (!netplay_client_deferred ? server : server_address_deferred) : NULL, + port ? ( !netplay_client_deferred ? port : server_port_deferred) : RARCH_DEFAULT_PORT, settings->netplay.stateless_mode, settings->netplay.check_frames, &cbs, settings->netplay.nat_traversal, settings->username, quirks); @@ -904,6 +963,8 @@ bool init_netplay(void *direct_host, const char *server, unsigned port) return false; } + + /** * netplay_driver_ctl * diff --git a/tasks/task_netplay_crc.c b/tasks/task_netplay_crc.c new file mode 100644 index 0000000000..16f7ba0da7 --- /dev/null +++ b/tasks/task_netplay_crc.c @@ -0,0 +1,184 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2016 - Jean-André Santoni + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tasks_internal.h" +#include "../file_path_special.h" +#include "../verbosity.h" +#include "../configuration.h" +#include "../playlist.h" +#include "../command.h" +#include "../core_info.h" + +typedef struct +{ + struct string_list *lpl_list; + char crc[PATH_MAX_LENGTH]; + char path[PATH_MAX_LENGTH]; + char hostname[512]; + char corename[PATH_MAX_LENGTH]; + bool found; +} netplay_crc_handle_t; + +static void netplay_crc_scan_callback(void *task_data, + void *user_data, const char *error) +{ + netplay_crc_handle_t *state = (netplay_crc_handle_t*)task_data; + core_info_list_t *info = NULL; + content_ctx_info_t content_info = {0}; + + int i; + core_info_get_list(&info); + + if (!state) + return; + + for (i=0; i < info->count; i++) + { + if(string_is_equal(info->list[i].core_name, state->corename)) + break; + } + + printf("Hostname: %s\n", state->hostname); + printf("Content: %s\n", state->path); + printf("Corename: %s\n", state->corename); + printf("Corepath: %s\n", info->list[i].path); + + command_event(CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, state->hostname); + task_push_content_load_default( + info->list[i].path, state->path, + &content_info, + CORE_TYPE_PLAIN, + CONTENT_MODE_LOAD_CONTENT_WITH_NEW_CORE_FROM_MENU, + NULL, NULL); + + free(state); +} + +static void task_netplay_crc_scan_handler(retro_task_t *task) +{ + netplay_crc_handle_t *state = (netplay_crc_handle_t*)task->state; + size_t i, j; + + task_set_progress(task, 0); + task_set_title(task, strdup("Checking for ROM presence.")); + task_set_finished(task, false); + + if (!state->lpl_list) + { + task_set_progress(task, 100); + task_set_title(task, strdup("Playlist directory not found.")); + task_set_finished(task, true); + free(state); + return; + } + + if (state->lpl_list->size == 0) + goto no_playlists; + + for (i = 0; i < state->lpl_list->size; i++) + { + const char *lpl_path = state->lpl_list->elems[i].data; + + if (!strstr(lpl_path, file_path_str(FILE_PATH_LPL_EXTENSION))) + continue; + + printf("%s\n", lpl_path); + + playlist_t *playlist = playlist_init(lpl_path, 99999); + + for (j = 0; j < playlist->size; j++) + { + printf("%s\n", playlist->entries[j].crc32); + if (string_is_equal(playlist->entries[j].crc32, state->crc)) + { + strlcpy(state->path, playlist->entries[j].path, sizeof(state->path)); + state->found = true; + task_set_data(task, state); + task_set_progress(task, 100); + task_set_title(task, strdup("Game found.")); + task_set_finished(task, true); + string_list_free(state->lpl_list); + return; + } + + task_set_progress(task, (int)(j/playlist->size*100.0)); + } + } + +no_playlists: + string_list_free(state->lpl_list); + task_set_progress(task, 100); + task_set_title(task, strdup("No game found.")); + task_set_finished(task, true); + return; +} + +bool task_push_netplay_crc_scan(uint32_t crc, + const char *hostname, const char *corename) +{ + settings_t *settings = config_get_ptr(); + retro_task_t *task = (retro_task_t *)calloc(1, sizeof(*task)); + netplay_crc_handle_t *state = (netplay_crc_handle_t*)calloc(1, sizeof(*state)); + + if (!task || !state) + goto error; + + state->crc[0] = '\0'; + snprintf(state->crc, sizeof(state->crc), "%08X|crc", crc); + + state->hostname[0] = '\0'; + snprintf(state->hostname, sizeof(state->hostname), "%s", hostname); + + state->corename[0] = '\0'; + snprintf(state->corename, sizeof(state->corename), "%s", corename); + + state->lpl_list = dir_list_new(settings->directory.playlist, + NULL, true, true, true, false); + + state->found = false; + + /* blocking means no other task can run while this one is running, + * which is the default */ + task->type = TASK_TYPE_BLOCKING; + task->state = state; + task->handler = task_netplay_crc_scan_handler; + task->callback = netplay_crc_scan_callback; + task->title = strdup("Checking for ROM presence."); + + task_queue_ctl(TASK_QUEUE_CTL_PUSH, task); + + return true; + +error: + if (state) + free(state); + if (task) + free(task); + + return false; +} diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 5c97486db5..958afb17ec 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -98,6 +98,9 @@ bool task_push_wifi_scan(void); bool task_push_netplay_lan_scan(void); +bool task_push_netplay_crc_scan(uint32_t crc, + const char *hostname, const char *corename); + #endif bool task_push_image_load(const char *fullpath,