15#define JSMN_PARENT_LINKS
18static bool json_token_streq(
const char *json,
const jsmntok_t *token,
const char *s)
21 (int)strlen(s) == token->
end - token->
start &&
22 strncmp(json + token->
start, s, token->
end - token->
start) == 0;
25static void json_str(
const char *json,
const jsmntok_t *token,
char *out,
size_t out_size)
28 if (len >= (
int)out_size)
30 len = (int)out_size - 1;
32 strncpy(out, json + token->
start, len);
36static int jsonint(
const char *json,
const jsmntok_t *token)
40 if (len >= (
int)
sizeof(temp))
42 len = (int)
sizeof(temp) - 1;
44 strncpy(temp, json + token->
start, len);
49static int skip_token_tree(
const jsmntok_t *tokens,
int token_count,
int index)
51 if (index >= token_count)
58 int children_count = tokens[index].
size;
59 int next_index = index + 1;
60 for (
int i = 0; i < children_count; i++)
62 if (next_index >= token_count)
68 next_index = skip_token_tree(tokens, token_count, next_index);
69 next_index = skip_token_tree(tokens, token_count, next_index);
73 next_index = skip_token_tree(tokens, token_count, next_index);
88 if (json_token_streq(json, token,
"name"))
90 if (json_token_streq(json, token,
"campaign_name_id"))
92 if (json_token_streq(json, token,
"description"))
94 if (json_token_streq(json, token,
"author"))
96 if (json_token_streq(json, token,
"start_map"))
98 if (json_token_streq(json, token,
"tutorial_map"))
100 if (json_token_streq(json, token,
"campaign_folder"))
102 if (json_token_streq(json, token,
"starterkit"))
104 if (json_token_streq(json, token,
"intro_video"))
106 if (json_token_streq(json, token,
"avatar_type"))
111static bool parse_campaign_from_tokens(
const char *json,
const jsmntok_t *tokens,
114 if (token_count < 1 || tokens[0].type !=
JSMN_OBJECT)
124 int pairs_to_process = tokens[0].
size;
125 int current_token_index = 1;
127 for (
int i = 0; i < pairs_to_process; i++)
129 if (current_token_index + 1 >= token_count)
134 CampaignFieldKey key = parse_campaign_field_key(json, &tokens[current_token_index]);
135 const jsmntok_t *value_token = &tokens[current_token_index + 1];
141 json_str(json, value_token, def->
name,
sizeof(def->
name));
146 int campaign_name_id = jsonint(json, value_token);
156 json_str(json, value_token, def->
author,
sizeof(def->
author));
181 int avatar_type = jsonint(json, value_token);
191 current_token_index = skip_token_tree(tokens, token_count, current_token_index);
192 current_token_index = skip_token_tree(tokens, token_count, current_token_index);
201 if (json_string == NULL)
210 size_t json_length = strlen(json_string);
211 int token_count =
jsmn_parse(&parser, json_string, json_length, NULL, 0);
214 log_error(
"Campaign JSON is not valid JSON: %s", path);
223 log_error(
"Unable to allocate %d JSON tokens for: %s", token_count, path);
229 token_count =
jsmn_parse(&parser, json_string, json_length, tokens, token_count);
231 bool success =
false;
234 log_error(
"Campaign JSON failed to tokenize: %s", path);
239 log_error(
"Campaign JSON must be a single JSON object: %s", path);
243 success = parse_campaign_from_tokens(json_string, tokens, token_count, out_campaign);
246 log_error(
"Campaign JSON structure invalid: %s", path);
bool parse_campaign_json_file(const char *path, CampaignJson *out_campaign)
Reads and parses a campaign JSON file.
@ CAMPAIGN_FIELD_START_MAP
@ CAMPAIGN_FIELD_DESCRIPTION
@ CAMPAIGN_FIELD_CAMPAIGN_NAME_ID
@ CAMPAIGN_FIELD_STARTERKIT
@ CAMPAIGN_FIELD_INTRO_VIDEO
@ CAMPAIGN_FIELD_TUTORIAL_MAP
@ CAMPAIGN_FIELD_CAMPAIGN_FOLDER
@ CAMPAIGN_FIELD_AVATAR_TYPE
void log_error(const char *format,...)
void log_debug(DebugLevel level, const char *format,...)
void jsmn_init(jsmn_parser *parser)
int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, jsmntok_t *tokens, const unsigned int num_tokens)
char * readfile(const char *path)
Parse result: the definition plus which keys were actually present.
bool found_campaign_name_id
bool found_campaign_folder
uint16_t campaign_name_id