Skip to content

Commit 85ebb0a

Browse files
committed
rg_utils: Added rg_json_fixup to fix json errors
In order to provide a better user experience, rg_json_fixup strips trailing commas. In the future it will also strip comments. NOTE: The way it finds trailing commas is certainly naive and could break things, so we should always try to parse the raw string before falling back to rg_json_fixup!
1 parent 88f2c0a commit 85ebb0a

File tree

4 files changed

+38
-7
lines changed

4 files changed

+38
-7
lines changed

components/retro-go/rg_gui.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,13 @@ bool rg_gui_set_theme(const char *theme_name)
118118
if (theme_name && theme_name[0])
119119
{
120120
snprintf(pathbuf, RG_PATH_MAX, "%s/%s/theme.json", RG_BASE_PATH_THEMES, theme_name);
121-
void *data;
121+
char *data;
122122
size_t data_len;
123-
if (rg_storage_read_file(pathbuf, &data, &data_len, 0))
123+
if (rg_storage_read_file(pathbuf, (void **)&data, &data_len, 0))
124124
{
125-
new_theme = cJSON_Parse((char *)data);
125+
new_theme = cJSON_Parse(data);
126+
if (!new_theme) // Parse failure, clean the markup and try again
127+
new_theme = cJSON_Parse(rg_json_fixup(data));
126128
free(data);
127129
}
128130
if (!new_theme)

components/retro-go/rg_settings.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ static cJSON *json_root(const char *name, bool set_dirty)
3131
cJSON_AddStringToObject(branch, "namespace", name);
3232
cJSON_AddNumberToObject(branch, "changed", 0);
3333

34-
void *data; size_t data_len;
34+
char *data; size_t data_len;
3535
char pathbuf[RG_PATH_MAX];
3636
snprintf(pathbuf, RG_PATH_MAX, "%s/%s.json", RG_BASE_PATH_CONFIG, name);
37-
if (rg_storage_read_file(pathbuf, &data, &data_len, 0))
37+
if (rg_storage_read_file(pathbuf, (void **)&data, &data_len, 0))
3838
{
39-
cJSON *values = cJSON_Parse((char *)data);
39+
cJSON *values = cJSON_Parse(data);
40+
if (!values) // Parse failure, clean the markup and try again
41+
values = cJSON_Parse(rg_json_fixup(data));
4042
if (values)
4143
{
4244
RG_LOGI("Config file loaded: '%s'", pathbuf);

components/retro-go/rg_utils.c

+19
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ char *rg_strtoupper(char *str)
2929
return str;
3030
}
3131

32+
char *rg_json_fixup(char *json)
33+
{
34+
// Strip trailing commas, eg [,1,2,3] {"a":1,}
35+
for (char *ptr = json, *prev = ptr; ptr && *ptr; ++ptr)
36+
{
37+
if ((*ptr == '}' || *ptr == ']' || *ptr == '{' || *ptr == '[' || *ptr == ',') && *prev == ',')
38+
{
39+
RG_LOGW("Found trailing comma at pos %d", (int)(ptr - json));
40+
*prev = ' ';
41+
}
42+
if (*ptr != '\t' && *ptr != '\n' && *ptr != ' ')
43+
prev = ptr;
44+
}
45+
46+
// TODO: We should also strip C-style comments!
47+
48+
return json;
49+
}
50+
3251
const char *rg_dirname(const char *path)
3352
{
3453
static char buffer[100];

components/retro-go/rg_utils.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,31 @@
4040
#define PRINTF_BINARY_32 PRINTF_BINARY_16 " " PRINTF_BINARY_16
4141
#define PRINTF_BINVAL_32(i) PRINTF_BINVAL_16((i) >> 16), PRINTF_BINVAL_16(i)
4242

43+
/* String functions */
44+
4345
/**
4446
* both functions give you an allocation of strlen(str) + 1 valid for the lifetime of the application
4547
* they cannot be freed. unique avoids keeping multiple copies of an identical string (eg a path)
4648
* Things like unique_string("abc") == unique_string("abc") are guaranteed to be true
4749
*/
4850
const char *rg_const_string(const char *str);
4951
const char *rg_unique_string(const char *str);
50-
5152
char *rg_strtolower(char *str);
5253
char *rg_strtoupper(char *str);
54+
char *rg_json_fixup(char *json);
55+
56+
/* Paths functions */
5357
const char *rg_dirname(const char *path);
5458
const char *rg_basename(const char *path);
5559
const char *rg_extension(const char *filename);
5660
bool rg_extension_match(const char *filename, const char *extensions);
5761
const char *rg_relpath(const char *path);
62+
63+
/* Hashing */
5864
uint32_t rg_crc32(uint32_t crc, const uint8_t *buf, size_t len);
5965
uint32_t rg_hash(const char *buf, size_t len);
66+
67+
/* Misc */
6068
void *rg_alloc(size_t size, uint32_t caps);
6169
// rg_usleep behaves like usleep in libc: it will sleep for *at least* `us` microseconds, but possibly more
6270
// due to scheduling. You should use rg_task_delay() if you don't need more than 10-15ms granularity.

0 commit comments

Comments
 (0)