1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *  Copyright (C) 2016-2017 - Gregor Richards
5  *  Copyright (C) 2016-2019 - Brad Parker
6  *
7  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
8  *  of the GNU General Public License as published by the Free Software Found-
9  *  ation, either version 3 of the License, or (at your option) any later version.
10  *
11  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13  *  PURPOSE.  See the GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along with RetroArch.
16  *  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <string/stdstring.h>
23 #include <compat/strl.h>
24 #include <formats/rjson.h>
25 #include "netplay_discovery.h"
26 #include "../../verbosity.h"
27 
28 enum netplay_parse_state
29 {
30    STATE_START = 0,
31    STATE_ARRAY_START,
32    STATE_OBJECT_START,
33    STATE_FIELDS_START,
34    STATE_FIELDS_OBJECT_START,
35    STATE_END
36 };
37 
38 struct netplay_rooms
39 {
40    struct netplay_room *head;
41    struct netplay_room *cur;
42 };
43 
44 struct netplay_json_context
45 {
46    bool *cur_member_bool;
47    int  *cur_member_int;
48    int  *cur_member_inthex;
49    char *cur_member_string;
50    size_t cur_member_size;
51    enum netplay_parse_state state;
52 };
53 
54 /* TODO/FIXME - static global variable */
55 static struct netplay_rooms *netplay_rooms_data;
56 
netplay_json_boolean(void * ctx,bool value)57 static bool netplay_json_boolean(void* ctx, bool value)
58 {
59    struct netplay_json_context* p_ctx = (struct netplay_json_context*)ctx;
60 
61    if (p_ctx->state == STATE_FIELDS_OBJECT_START)
62       if (p_ctx->cur_member_bool)
63          *p_ctx->cur_member_bool = value;
64 
65    return true;
66 }
67 
netplay_json_string(void * ctx,const char * p_value,size_t len)68 static bool netplay_json_string(void* ctx, const char *p_value, size_t len)
69 {
70    struct netplay_json_context* p_ctx = (struct netplay_json_context*)ctx;
71 
72    if (p_ctx->state == STATE_FIELDS_OBJECT_START)
73    {
74       if (p_value && len)
75       {
76          /* CRC comes in as a string but it is stored
77           * as an unsigned casted to int. */
78          if (p_ctx->cur_member_inthex)
79             *p_ctx->cur_member_inthex = (int)strtoul(p_value, NULL, 16);
80          if (p_ctx->cur_member_string)
81             strlcpy(p_ctx->cur_member_string, p_value, p_ctx->cur_member_size);
82       }
83    }
84 
85    return true;
86 }
87 
netplay_json_number(void * ctx,const char * p_value,size_t len)88 static bool netplay_json_number(void* ctx, const char *p_value, size_t len)
89 {
90    struct netplay_json_context *p_ctx = (struct netplay_json_context*)ctx;
91 
92    if (p_ctx->state == STATE_FIELDS_OBJECT_START)
93    {
94       if (p_value && len)
95          if (p_ctx->cur_member_int)
96             *p_ctx->cur_member_int = (int)strtol(p_value, NULL, 10);
97    }
98 
99    return true;
100 }
101 
netplay_json_start_object(void * ctx)102 static bool netplay_json_start_object(void* ctx)
103 {
104    struct netplay_json_context *p_ctx = (struct netplay_json_context*)ctx;
105 
106    if (p_ctx->state == STATE_FIELDS_START)
107    {
108       p_ctx->state = STATE_FIELDS_OBJECT_START;
109 
110       if (!netplay_rooms_data->head)
111       {
112          netplay_rooms_data->head      = (struct netplay_room*)calloc(1, sizeof(*netplay_rooms_data->head));
113          netplay_rooms_data->cur       = netplay_rooms_data->head;
114       }
115       else if (!netplay_rooms_data->cur->next)
116       {
117          netplay_rooms_data->cur->next = (struct netplay_room*)calloc(1, sizeof(*netplay_rooms_data->cur->next));
118          netplay_rooms_data->cur       = netplay_rooms_data->cur->next;
119       }
120    }
121    else if (p_ctx->state == STATE_ARRAY_START)
122       p_ctx->state = STATE_OBJECT_START;
123 
124    return true;
125 }
126 
netplay_json_end_object(void * ctx)127 static bool netplay_json_end_object(void* ctx)
128 {
129    struct netplay_json_context *p_ctx = (struct netplay_json_context*)ctx;
130 
131    if (p_ctx->state == STATE_FIELDS_OBJECT_START)
132       p_ctx->state = STATE_ARRAY_START;
133 
134    return true;
135 }
136 
netplay_json_object_member(void * ctx,const char * p_value,size_t len)137 static bool netplay_json_object_member(void *ctx, const char *p_value,
138       size_t len)
139 {
140    struct netplay_json_context* p_ctx = (struct netplay_json_context*)ctx;
141 
142    if (!p_value || !len)
143       return true;
144 
145    if (p_ctx->state == STATE_OBJECT_START && !string_is_empty(p_value)
146          && string_is_equal(p_value, "fields"))
147       p_ctx->state = STATE_FIELDS_START;
148 
149    if (p_ctx->state == STATE_FIELDS_OBJECT_START)
150    {
151       p_ctx->cur_member_bool   = NULL;
152       p_ctx->cur_member_int    = NULL;
153       p_ctx->cur_member_inthex = NULL;
154       p_ctx->cur_member_string = NULL;
155 
156       if (!string_is_empty(p_value))
157       {
158          if (string_is_equal(p_value, "username"))
159          {
160             p_ctx->cur_member_string = netplay_rooms_data->cur->nickname;
161             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->nickname);
162          }
163          else if (string_is_equal(p_value, "game_name"))
164          {
165             p_ctx->cur_member_string = netplay_rooms_data->cur->gamename;
166             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->gamename);
167          }
168          else if (string_is_equal(p_value, "core_name"))
169          {
170             p_ctx->cur_member_string = netplay_rooms_data->cur->corename;
171             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->corename);
172          }
173          else if (string_is_equal(p_value, "ip"))
174          {
175             p_ctx->cur_member_string = netplay_rooms_data->cur->address;
176             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->address);
177          }
178          else if (string_is_equal(p_value, "port"))
179          {
180             p_ctx->cur_member_int    = &netplay_rooms_data->cur->port;
181          }
182          else if (string_is_equal(p_value, "game_crc"))
183          {
184             p_ctx->cur_member_inthex = &netplay_rooms_data->cur->gamecrc;
185          }
186          else if (string_is_equal(p_value, "core_version"))
187          {
188             p_ctx->cur_member_string = netplay_rooms_data->cur->coreversion;
189             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->coreversion);
190          }
191          else if (string_is_equal(p_value, "has_password"))
192          {
193             p_ctx->cur_member_bool   = &netplay_rooms_data->cur->has_password;
194          }
195          else if (string_is_equal(p_value, "has_spectate_password"))
196          {
197             p_ctx->cur_member_bool   = &netplay_rooms_data->cur->has_spectate_password;
198          }
199          else if (string_is_equal(p_value, "fixed"))
200          {
201             p_ctx->cur_member_bool   = &netplay_rooms_data->cur->fixed;
202          }
203          else if (string_is_equal(p_value, "mitm_ip"))
204          {
205             p_ctx->cur_member_string = netplay_rooms_data->cur->mitm_address;
206             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->mitm_address);
207          }
208          else if (string_is_equal(p_value, "mitm_port"))
209          {
210             p_ctx->cur_member_int    = &netplay_rooms_data->cur->mitm_port;
211          }
212          else if (string_is_equal(p_value, "host_method"))
213          {
214             p_ctx->cur_member_int    = &netplay_rooms_data->cur->host_method;
215          }
216          else if (string_is_equal(p_value, "retroarch_version"))
217          {
218             p_ctx->cur_member_string = netplay_rooms_data->cur->retroarch_version;
219             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->retroarch_version);
220          }
221          else if (string_is_equal(p_value, "country"))
222          {
223             p_ctx->cur_member_string = netplay_rooms_data->cur->country;
224             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->country);
225          }
226          else if (string_is_equal(p_value, "frontend"))
227          {
228             p_ctx->cur_member_string = netplay_rooms_data->cur->frontend;
229             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->frontend);
230          }
231          else if (string_is_equal(p_value, "subsystem_name"))
232          {
233             p_ctx->cur_member_string = netplay_rooms_data->cur->subsystem_name;
234             p_ctx->cur_member_size   = sizeof(netplay_rooms_data->cur->subsystem_name);
235          }
236       }
237    }
238 
239    return true;
240 }
241 
netplay_json_start_array(void * ctx)242 static bool netplay_json_start_array(void* ctx)
243 {
244    struct netplay_json_context* p_ctx = (struct netplay_json_context*)ctx;
245 
246    if (p_ctx->state == STATE_START)
247       p_ctx->state = STATE_ARRAY_START;
248 
249    return true;
250 }
251 
netplay_rooms_error(void * context,int line,int col,const char * error)252 static void netplay_rooms_error(void *context,
253       int line, int col, const char* error)
254 {
255    RARCH_ERR("[netplay] Error: Invalid JSON at line %d, column %d - %s.\n",
256          line, col, error);
257 }
258 
netplay_rooms_free(void)259 void netplay_rooms_free(void)
260 {
261    if (netplay_rooms_data)
262    {
263       struct netplay_room *room = netplay_rooms_data->head;
264 
265       if (room)
266       {
267          while (room)
268          {
269             struct netplay_room *next = room->next;
270 
271             free(room);
272             room = next;
273          }
274       }
275 
276       free(netplay_rooms_data);
277    }
278    netplay_rooms_data = NULL;
279 }
280 
netplay_rooms_parse(const char * buf)281 int netplay_rooms_parse(const char *buf)
282 {
283    struct netplay_json_context ctx;
284 
285    memset(&ctx, 0, sizeof(ctx));
286 
287    ctx.state = STATE_START;
288 
289    /* delete any previous rooms */
290    netplay_rooms_free();
291 
292    netplay_rooms_data = (struct netplay_rooms*)
293       calloc(1, sizeof(*netplay_rooms_data));
294 
295    rjson_parse_quick(buf, &ctx, 0,
296          netplay_json_object_member,
297          netplay_json_string,
298          netplay_json_number,
299          netplay_json_start_object,
300          netplay_json_end_object,
301          netplay_json_start_array,
302          NULL /* end_array_handler */,
303          netplay_json_boolean,
304          NULL /* null handler */,
305          netplay_rooms_error);
306 
307    return 0;
308 }
309 
netplay_room_get(int index)310 struct netplay_room* netplay_room_get(int index)
311 {
312    int                   cur = 0;
313    struct netplay_room *room = netplay_rooms_data->head;
314 
315    if (index < 0)
316       return NULL;
317 
318    while (room)
319    {
320       if (cur == index)
321          break;
322 
323       room = room->next;
324       cur++;
325    }
326 
327    return room;
328 }
329 
netplay_rooms_get_count(void)330 int netplay_rooms_get_count(void)
331 {
332    int count = 0;
333    struct netplay_room *room;
334 
335    if (!netplay_rooms_data)
336       return count;
337 
338    room = netplay_rooms_data->head;
339 
340    if (!room)
341       return count;
342 
343    while (room)
344    {
345       count++;
346 
347       room = room->next;
348    }
349 
350    return count;
351 }
352