parse new lobby room listing with JSON

This commit is contained in:
Brad Parker 2017-03-04 01:31:24 -05:00
parent 21fe0c8499
commit 0d81b2005f
6 changed files with 496 additions and 24 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string/stdstring.h>
#include <compat/strl.h>
#include <formats/jsonsax_full.h>
#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;
}