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