Spellforce-Spell-framework
Loading...
Searching...
No Matches
sf_screens_loader.c
Go to the documentation of this file.
1
5
6#include <ctype.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "sf_screens_loader.h"
12#include "sf_building_loader.h" /* readfile() */
13#include "sf_wrappers.h"
14
15#define JSMN_STATIC
16#define JSMN_PARENT_LINKS
17#include "jsmn.h"
18
19static void str_to_lower(char *str)
20{
21 for (int i = 0; str[i] != '\0'; i++)
22 {
23 str[i] = (char)tolower((unsigned char)str[i]);
24 }
25}
26
28static int skip_token_tree(const jsmntok_t *tokens, int token_count, int index)
29{
30 if (index >= token_count)
31 {
32 return index;
33 }
34
35 if (tokens[index].type == JSMN_OBJECT || tokens[index].type == JSMN_ARRAY)
36 {
37 int children_count = tokens[index].size;
38 int next_index = index + 1;
39 for (int i = 0; i < children_count; i++)
40 {
41 if (next_index >= token_count)
42 {
43 return token_count;
44 }
45 if (tokens[index].type == JSMN_OBJECT)
46 {
47 next_index = skip_token_tree(tokens, token_count, next_index); // key
48 next_index = skip_token_tree(tokens, token_count, next_index); // value
49 }
50 else
51 {
52 next_index = skip_token_tree(tokens, token_count, next_index);
53 }
54 }
55 return next_index;
56 }
57
58 return index + 1;
59}
60
61// Copies a JSON string token's contents into a bounded buffer.
62static void json_str(const char *json, const jsmntok_t *token, char *out, size_t out_size)
63{
64 int len = token->end - token->start;
65 if (len >= (int)out_size)
66 {
67 len = (int)out_size - 1;
68 }
69 strncpy(out, json + token->start, len);
70 out[len] = '\0';
71}
72
73bool parse_screens_json_file(const char *path, ScreenEntry *out_entries,
74 int max_entries, int *out_count)
75{
76 *out_count = 0;
77
78 char *json_string = readfile(path);
79 if (json_string == NULL)
80 {
81 // readfile has already logged the reason.
82 return false;
83 }
84
85 jsmn_parser parser;
86 jsmn_init(&parser);
87
88 size_t json_length = strlen(json_string);
89 int token_count = jsmn_parse(&parser, json_string, json_length, NULL, 0);
90 if (token_count < 1)
91 {
92 log_error("screens.json is not valid JSON: %s", path);
93 log_debug(DEBUG_HIGH, json_string);
94 free(json_string);
95 return false;
96 }
97
98 jsmntok_t *tokens = (jsmntok_t *)calloc(token_count, sizeof(jsmntok_t));
99 if (tokens == NULL)
100 {
101 log_error("Unable to allocate %d JSON tokens for: %s", token_count, path);
102 free(json_string);
103 return false;
104 }
105
106 jsmn_init(&parser);
107 token_count = jsmn_parse(&parser, json_string, json_length, tokens, token_count);
108
109 bool success = false;
110 if (token_count < 1)
111 {
112 log_error("screens.json failed to tokenize: %s", path);
113 log_debug(DEBUG_HIGH, json_string);
114 }
115 else if (tokens[0].type != JSMN_OBJECT)
116 {
117 log_error("screens.json must be a single JSON object of map_name : msb_file pairs");
118 }
119 else
120 {
121 int pairs_to_process = tokens[0].size;
122 int current_token_index = 1;
123
124 for (int i = 0; i < pairs_to_process; i++)
125 {
126 if (current_token_index + 1 >= token_count)
127 {
128 break;
129 }
130
131 if (*out_count >= max_entries)
132 {
133 log_error("screens.json has more than %d entries, ignoring the rest", max_entries);
134 break;
135 }
136
137 const jsmntok_t *key_token = &tokens[current_token_index];
138 const jsmntok_t *value_token = &tokens[current_token_index + 1];
139
140 if (key_token->type != JSMN_STRING || value_token->type != JSMN_STRING)
141 {
142 log_error("screens.json entry %d is not a string:string pair, skipping", i);
143 }
144 else
145 {
146 ScreenEntry *entry = &out_entries[*out_count];
147 json_str(json_string, key_token, entry->map_name, sizeof(entry->map_name));
148 json_str(json_string, value_token, entry->msb_file, sizeof(entry->msb_file));
149 str_to_lower(entry->map_name);
150 (*out_count)++;
151 }
152
153 /* Using skip_token_tree for safer json parsing. */
154 current_token_index = skip_token_tree(tokens, token_count, current_token_index); // key
155 current_token_index = skip_token_tree(tokens, token_count, current_token_index); // value
156 }
157
158 success = true;
159 }
160
161 free(tokens);
162 free(json_string);
163 return success;
164}
165
bool parse_screens_json_file(const char *path, ScreenEntry *out_entries, int max_entries, int *out_count)
Reads a screens.json: a single object of map_name : msb_file pairs.
void log_error(const char *format,...)
void log_debug(DebugLevel level, const char *format,...)
@ JSMN_OBJECT
Definition jsmn.h:48
@ JSMN_ARRAY
Definition jsmn.h:49
@ JSMN_STRING
Definition jsmn.h:50
void jsmn_init(jsmn_parser *parser)
Definition jsmn.h:456
int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, jsmntok_t *tokens, const unsigned int num_tokens)
Definition jsmn.h:265
char * readfile(const char *path)
One "map name -> loading screen mesh" pair.
char msb_file[128]
char map_name[128]
int start
Definition jsmn.h:71
int size
Definition jsmn.h:73
int end
Definition jsmn.h:72
jsmntype_t type
Definition jsmn.h:70