diff --git a/Makefile.common b/Makefile.common
index 1c433f5c04..16964a7af7 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1195,7 +1195,8 @@ ifeq ($(HAVE_NETWORKING), 1)
network/netplay/netplay_io.o \
network/netplay/netplay_sync.o \
network/netplay/netplay_discovery.o \
- network/netplay/netplay_buf.o
+ network/netplay/netplay_buf.o \
+ network/netplay/netplay_room_parse.o
# Retro Achievements (also depends on threads)
diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c
index fd812b6403..24ba225ce9 100644
--- a/menu/cbs/menu_cbs_ok.c
+++ b/menu/cbs/menu_cbs_ok.c
@@ -3418,7 +3418,6 @@ finish:
int j = 0;
int k = 0;
int lan_room_count = 0;
- static struct string_list *room_data = NULL;
struct netplay_host_list *lan_hosts = NULL;
file_list_t *file_list = menu_entries_get_selection_buf_ptr(0);
@@ -3428,7 +3427,7 @@ finish:
if (lan_hosts)
lan_room_count = lan_hosts->size;
- room_data = string_split(buf, "\n");
+ netplay_rooms_parse(buf);
if (netplay_room_list)
free(netplay_room_list);
@@ -3437,20 +3436,11 @@ finish:
* in the same list. If both entries are available, we want to show only
* the LAN one. */
- netplay_room_count = (int)(room_data->size / 8);
+ netplay_room_count = netplay_rooms_get_count();
netplay_room_list = (struct netplay_room*)
calloc(netplay_room_count + lan_room_count,
sizeof(struct netplay_room));
-#if 0
- for (int i = 0; i < room_data->size; i++)
- {
- strlcpy(tmp,
- room_data->elems[i].data, sizeof(tmp));
- RARCH_LOG("tmp %s\n", tmp);
-
- }
-#endif
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, file_list);
menu_entries_append_enum(file_list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_HOST),
@@ -3469,7 +3459,9 @@ finish:
for (i = 0; i < netplay_room_count; i++)
{
- strlcpy(netplay_room_list[i].nickname,
+ memcpy(&netplay_room_list[i], netplay_room_get(i), sizeof(netplay_room_list[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,
@@ -3487,7 +3479,7 @@ finish:
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);
+ netplay_room_list[i].timestamp = atoi(room_data->elems[j + 7].data);*/
/* Uncomment this to debug mismatched room parameters*/
#if 0
@@ -3521,6 +3513,8 @@ finish:
MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM,
MENU_ROOM, 0, 0);
}
+
+ netplay_rooms_free();
}
if (lan_room_count != 0)
@@ -3587,7 +3581,7 @@ finish:
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/";
+ char url [2048] = "http://newlobby.libretro.com/list/";
task_push_netplay_lan_scan();
task_push_http_transfer(url, true, NULL, netplay_refresh_rooms_cb, NULL);
return 0;
diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h
index 8b72bfc50b..8a22bc12dd 100644
--- a/network/netplay/netplay.h
+++ b/network/netplay/netplay.h
@@ -79,4 +79,12 @@ void deinit_netplay(void);
bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data);
+int netplay_rooms_parse(const char *buf);
+
+struct netplay_room* netplay_room_get(int index);
+
+int netplay_rooms_get_count();
+
+void netplay_rooms_free();
+
#endif
diff --git a/network/netplay/netplay_discovery.h b/network/netplay/netplay_discovery.h
index 68119cd020..bc177b01a7 100644
--- a/network/netplay/netplay_discovery.h
+++ b/network/netplay/netplay_discovery.h
@@ -60,6 +60,10 @@ struct netplay_room
char gamename [PATH_MAX_LENGTH];
int gamecrc;
int timestamp;
+ bool has_password;
+ bool has_spectate_password;
+ bool fixed;
+ struct netplay_room *next;
};
extern struct netplay_room *netplay_room_list;
diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c
index a28736874b..3136e6ba2b 100644
--- a/network/netplay/netplay_frontend.c
+++ b/network/netplay/netplay_frontend.c
@@ -521,7 +521,7 @@ static void netplay_announce_cb(void *task_data, void *user_data, const char *er
static void netplay_announce(void)
{
char buf [2048];
- char url [2048] = "http://lobby.libretro.com/raw/?";
+ char url [2048] = "http://newlobby.libretro.com/add/";
rarch_system_info_t *system = NULL;
settings_t *settings = config_get_ptr();
uint32_t *content_crc_ptr = NULL;
@@ -532,14 +532,13 @@ static void netplay_announce(void)
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,
- !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A",*content_crc_ptr,
- settings->netplay.port);
+ snprintf(buf, sizeof(buf), "username=%s&core_name=%s&core_version=%s&"
+ "game_name=%s&game_crc=%d&port=%d&has_password=%d&has_spectate_password=%d",
+ settings->username, system->info.library_name, system->info.library_version,
+ !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A", *content_crc_ptr,
+ settings->netplay.port, settings->netplay.password ? 1 : 0, settings->netplay.spectate_password ? 1 : 0);
- task_push_http_transfer(buf, true, NULL, netplay_announce_cb, NULL);
+ task_push_http_post_transfer(url, buf, true, NULL, netplay_announce_cb, NULL);
}
int16_t input_state_net(unsigned port, unsigned device,
diff --git a/network/netplay/netplay_room_parse.c b/network/netplay/netplay_room_parse.c
new file mode 100644
index 0000000000..9ae5565cd2
--- /dev/null
+++ b/network/netplay/netplay_room_parse.c
@@ -0,0 +1,466 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2016-2017 - Gregor Richards
+ * Copyright (C) 2017 - Brad Parker
+ *
+ * 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 "netplay_discovery.h"
+#include "../../verbosity.h"
+
+enum parse_state
+{
+ STATE_START = 0,
+ STATE_ARRAY_START,
+ STATE_OBJECT_START,
+ STATE_FIELDS_START,
+ STATE_FIELDS_OBJECT_START,
+ STATE_END
+};
+
+struct netplay_rooms
+{
+ struct netplay_room *head;
+ struct netplay_room *cur;
+};
+
+typedef struct tag_Context
+{
+ JSON_Parser parser;
+ int inEmptyContainer;
+ enum parse_state state;
+ char *cur_field;
+ void *cur_member;
+ size_t cur_member_size;
+} Context;
+
+static struct netplay_rooms *rooms;
+
+static void parse_context_init(Context* pCtx)
+{
+ pCtx->parser = NULL;
+ pCtx->inEmptyContainer = 0;
+}
+
+static void parse_context_free(Context* pCtx)
+{
+ if (pCtx->cur_field)
+ free(pCtx->cur_field);
+
+ pCtx->cur_field = NULL;
+
+ JSON_Parser_Free(pCtx->parser);
+}
+
+static JSON_Parser_HandlerResult JSON_CALL EncodingDetectedHandler(JSON_Parser parser)
+{
+ (void)parser;
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL NullHandler(JSON_Parser parser)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ pCtx->inEmptyContainer = 0;
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL BooleanHandler(JSON_Parser parser, JSON_Boolean value)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ pCtx->inEmptyContainer = 0;
+
+ if (pCtx->state == STATE_FIELDS_OBJECT_START)
+ {
+ RARCH_LOG("found boolean: %d\n", value);
+
+ if (pCtx->cur_field)
+ *((bool*)pCtx->cur_member) = value;
+ }
+
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL StringHandler(JSON_Parser parser, char* pValue, size_t length, JSON_StringAttributes attributes)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ (void)attributes;
+ pCtx->inEmptyContainer = 0;
+
+ if (pCtx->state == STATE_FIELDS_OBJECT_START)
+ {
+ RARCH_LOG("found string: %s\n", pValue);
+
+ if (pValue && length)
+ {
+ if (pCtx->cur_field && string_is_equal(pCtx->cur_field, "game_crc"))
+ {
+ /* CRC comes in as a string but it is stored as an int */
+ *((int*)pCtx->cur_member) = strtoul(pValue, NULL, 16);
+ }
+ else if (pCtx->cur_field)
+ strlcpy((char*)pCtx->cur_member, pValue, PATH_MAX_LENGTH);
+ }
+ }
+
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL NumberHandler(JSON_Parser parser, char* pValue, size_t length, JSON_NumberAttributes attributes)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ (void)attributes;
+ pCtx->inEmptyContainer = 0;
+
+ if (pCtx->state == STATE_FIELDS_OBJECT_START)
+ {
+ RARCH_LOG("found number string: %s\n", pValue);
+
+ if (pValue && length)
+ if (pCtx->cur_field)
+ *((int*)pCtx->cur_member) = strtol(pValue, NULL, 10);
+ }
+
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL SpecialNumberHandler(JSON_Parser parser, JSON_SpecialNumber value)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ pCtx->inEmptyContainer = 0;
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL StartObjectHandler(JSON_Parser parser)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ pCtx->inEmptyContainer = 1;
+ RARCH_LOG("object start\n");
+
+ if (pCtx->state == STATE_FIELDS_START)
+ {
+ pCtx->state = STATE_FIELDS_OBJECT_START;
+
+ if (!rooms->head)
+ {
+ rooms->head = (struct netplay_room*)calloc(1, sizeof(*rooms->head));
+ rooms->cur = rooms->head;
+ }
+ else if (!rooms->cur->next)
+ {
+ rooms->cur->next = (struct netplay_room*)calloc(1, sizeof(*rooms->cur->next));
+ rooms->cur = rooms->cur->next;
+ }
+ }
+ else if (pCtx->state == STATE_ARRAY_START)
+ pCtx->state = STATE_OBJECT_START;
+
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL EndObjectHandler(JSON_Parser parser)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ if (!pCtx->inEmptyContainer)
+ {
+ /* indent */
+ }
+ pCtx->inEmptyContainer = 0;
+ RARCH_LOG("object end\n");
+
+ if (pCtx->state == STATE_FIELDS_OBJECT_START)
+ pCtx->state = STATE_ARRAY_START;
+
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL ObjectMemberHandler(JSON_Parser parser, char* pValue, size_t length, JSON_StringAttributes attributes)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ (void)attributes;
+ if (!pCtx->inEmptyContainer)
+ RARCH_LOG("object member comma\n");
+ RARCH_LOG("object member %I64u: %s\n", length, pValue);
+
+ if (!pValue || !length)
+ return JSON_Parser_Continue;
+
+ if (pCtx->state == STATE_OBJECT_START && string_is_equal(pValue, "fields"))
+ pCtx->state = STATE_FIELDS_START;
+
+ if (pCtx->state == STATE_FIELDS_OBJECT_START)
+ {
+ RARCH_LOG("got field %s\n", pValue);
+
+ if (pCtx->cur_field)
+ free(pCtx->cur_field);
+
+ pCtx->cur_field = strdup(pValue);
+
+ if (string_is_equal(pValue, "username"))
+ {
+ pCtx->cur_member = &rooms->cur->nickname;
+ pCtx->cur_member_size = sizeof(rooms->cur->nickname);
+ }
+ else if (string_is_equal(pValue, "game_name"))
+ {
+ pCtx->cur_member = &rooms->cur->gamename;
+ pCtx->cur_member_size = sizeof(rooms->cur->gamename);
+ }
+ else if (string_is_equal(pValue, "core_name"))
+ {
+ pCtx->cur_member = &rooms->cur->corename;
+ pCtx->cur_member_size = sizeof(rooms->cur->corename);
+ }
+ else if (string_is_equal(pValue, "ip"))
+ {
+ pCtx->cur_member = &rooms->cur->address;
+ pCtx->cur_member_size = sizeof(rooms->cur->address);
+ }
+ else if (string_is_equal(pValue, "port"))
+ pCtx->cur_member = &rooms->cur->port;
+ else if (string_is_equal(pValue, "game_crc"))
+ pCtx->cur_member = &rooms->cur->gamecrc;
+ else if (string_is_equal(pValue, "core_version"))
+ {
+ pCtx->cur_member = &rooms->cur->coreversion;
+ pCtx->cur_member_size = sizeof(rooms->cur->coreversion);
+ }
+ else if (string_is_equal(pValue, "has_password"))
+ pCtx->cur_member = &rooms->cur->has_password;
+ else if (string_is_equal(pValue, "has_spectate_password"))
+ pCtx->cur_member = &rooms->cur->has_spectate_password;
+ else if (string_is_equal(pValue, "fixed"))
+ pCtx->cur_member = &rooms->cur->fixed;
+ else
+ {
+ /* unknown field, ignore it */
+ free(pCtx->cur_field);
+ pCtx->cur_field = NULL;
+ }
+ }
+
+ pCtx->inEmptyContainer = 0;
+ /* indent, string, space, colon, space */
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL StartArrayHandler(JSON_Parser parser)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ pCtx->inEmptyContainer = 1;
+ RARCH_LOG("array start\n");
+
+ if (pCtx->state == STATE_START)
+ pCtx->state = STATE_ARRAY_START;
+
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL EndArrayHandler(JSON_Parser parser)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ if (!pCtx->inEmptyContainer)
+ {
+ /* indent */
+ }
+ pCtx->inEmptyContainer = 0;
+ RARCH_LOG("array end\n");
+ return JSON_Parser_Continue;
+}
+
+static JSON_Parser_HandlerResult JSON_CALL ArrayItemHandler(JSON_Parser parser)
+{
+ Context* pCtx = (Context*)JSON_Parser_GetUserData(parser);
+ (void)parser;
+ if (!pCtx->inEmptyContainer)
+ {
+ RARCH_LOG("array comma\n");
+ }
+ pCtx->inEmptyContainer = 0;
+ /* indent */
+ return JSON_Parser_Continue;
+}
+
+static int parse_context_setup(Context* pCtx)
+{
+ /*JSON_Parser_SetTrackObjectMembers(pCtx->parser, JSON_True);
+ JSON_Parser_SetAllowBOM(pCtx->parser, JSON_True);
+ JSON_Parser_SetAllowComments(pCtx->parser, JSON_True);
+ JSON_Parser_SetAllowSpecialNumbers(pCtx->parser, JSON_True);
+ JSON_Parser_SetAllowHexNumbers(pCtx->parser, JSON_True);
+ JSON_Parser_SetAllowUnescapedControlCharacters(pCtx->parser, JSON_True);
+ JSON_Parser_SetReplaceInvalidEncodingSequences(pCtx->parser, JSON_True);
+ JSON_Parser_SetTrackObjectMembers(pCtx->parser, JSON_False);*/
+
+ if (JSON_Parser_GetInputEncoding(pCtx->parser) == JSON_UnknownEncoding)
+ {
+ JSON_Parser_SetEncodingDetectedHandler(pCtx->parser, &EncodingDetectedHandler);
+ }
+
+ JSON_Parser_SetNullHandler(pCtx->parser, &NullHandler);
+ JSON_Parser_SetBooleanHandler(pCtx->parser, &BooleanHandler);
+ JSON_Parser_SetStringHandler(pCtx->parser, &StringHandler);
+ JSON_Parser_SetNumberHandler(pCtx->parser, &NumberHandler);
+ JSON_Parser_SetSpecialNumberHandler(pCtx->parser, &SpecialNumberHandler);
+ JSON_Parser_SetStartObjectHandler(pCtx->parser, &StartObjectHandler);
+ JSON_Parser_SetEndObjectHandler(pCtx->parser, &EndObjectHandler);
+ JSON_Parser_SetObjectMemberHandler(pCtx->parser, &ObjectMemberHandler);
+ JSON_Parser_SetStartArrayHandler(pCtx->parser, &StartArrayHandler);
+ JSON_Parser_SetEndArrayHandler(pCtx->parser, &EndArrayHandler);
+ JSON_Parser_SetArrayItemHandler(pCtx->parser, &ArrayItemHandler);
+ JSON_Parser_SetUserData(pCtx->parser, pCtx);
+
+ return 1;
+}
+
+static void parse_context_error(Context* pCtx)
+{
+ if (JSON_Parser_GetError(pCtx->parser) != JSON_Error_AbortedByHandler)
+ {
+ JSON_Error error = JSON_Parser_GetError(pCtx->parser);
+ JSON_Location errorLocation = {0, 0, 0};
+ (void)JSON_Parser_GetErrorLocation(pCtx->parser, &errorLocation);
+ RARCH_ERR("invalid JSON at line %d, column %d (input byte %d) - %s.\n",
+ (int)errorLocation.line + 1,
+ (int)errorLocation.column + 1,
+ (int)errorLocation.byte,
+ JSON_ErrorString(error));
+ }
+}
+
+static int json_parse(Context* pCtx, const char *buf)
+{
+ if (!JSON_Parser_Parse(pCtx->parser, buf, strlen(buf), JSON_True))
+ {
+ parse_context_error(pCtx);
+ return 0;
+ }
+
+ return 1;
+}
+
+void netplay_rooms_free()
+{
+ if (rooms)
+ {
+ struct netplay_room *room = rooms->head;
+
+ if (room)
+ {
+ while (room != NULL)
+ {
+ struct netplay_room *next = room->next;
+
+ free(room);
+
+ room = next;
+ }
+
+ free(rooms);
+ }
+ else
+ free(rooms);
+
+ rooms = NULL;
+ }
+}
+
+int netplay_rooms_parse(const char *buf)
+{
+ Context ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ ctx.state = STATE_START;
+
+ /* delete any previous rooms */
+ netplay_rooms_free();
+
+ rooms = (struct netplay_rooms*)calloc(1, sizeof(*rooms));
+
+ parse_context_init(&ctx);
+
+ ctx.parser = JSON_Parser_Create(NULL);
+
+ if (!ctx.parser)
+ {
+ RARCH_ERR("could not allocate memory for JSON parser.\n");
+ return 1;
+ }
+
+ parse_context_setup(&ctx);
+ json_parse(&ctx, buf);
+ parse_context_free(&ctx);
+
+ return 0;
+}
+
+struct netplay_room* netplay_room_get(int index)
+{
+ int cur = 0;
+ struct netplay_room *room = rooms->head;
+
+ if (index < 0)
+ return NULL;
+
+ while (room != NULL)
+ {
+ if (cur == index)
+ break;
+
+ room = room->next;
+ cur++;
+ }
+
+ return room;
+}
+
+int netplay_rooms_get_count()
+{
+ int count = 0;
+ struct netplay_room *room;
+
+ if (!rooms)
+ return count;
+
+ room = rooms->head;
+
+ if (!room)
+ return count;
+
+ while(room != NULL)
+ {
+ count++;
+
+ room = room->next;
+ }
+
+ return count;
+}