Mercurial > molko
comparison tools/molko-map.c @ 16:621c815c9509
tools: implement basic molko-map stub, continue #2448
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 07 Jan 2020 21:45:03 +0100 |
parents | |
children | 0d5ecefcccd3 |
comparison
equal
deleted
inserted
replaced
15:98e091b9251e | 16:621c815c9509 |
---|---|
1 /* | |
2 * molko-map.c -- convert tiled .tmx into custom files | |
3 * | |
4 * Copyright (c) 2020 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <assert.h> | |
20 #include <stdarg.h> | |
21 #include <stdbool.h> | |
22 #include <stdio.h> | |
23 #include <stdlib.h> | |
24 #include <string.h> | |
25 | |
26 #include <expat.h> | |
27 | |
28 enum state { | |
29 STATE_ROOT, /* no tag read yet */ | |
30 STATE_MAP, /* top <map> */ | |
31 STATE_MAP_PROPERTIES, /* <map><properties> */ | |
32 STATE_TILESET, /* <tileset> */ | |
33 STATE_TILESET_IMAGE, /* <tileset><image> */ | |
34 STATE_LAYER, /* <layer> */ | |
35 STATE_LAYER_DATA, /* <layer><data> */ | |
36 }; | |
37 | |
38 enum layer { | |
39 LAYER_NONE, | |
40 LAYER_BACKGROUND, | |
41 LAYER_FOREGROUND | |
42 }; | |
43 | |
44 struct context { | |
45 enum state state; | |
46 enum layer layer; | |
47 }; | |
48 | |
49 static const int layer_indices[] = { | |
50 [LAYER_NONE] = -1, | |
51 [LAYER_BACKGROUND] = 0, | |
52 [LAYER_FOREGROUND] = 1 | |
53 }; | |
54 | |
55 static void on_data(struct context *, const char *, const char **); | |
56 static void on_image(struct context *, const char *, const char **); | |
57 static void on_layer(struct context *, const char *, const char **); | |
58 static void on_map(struct context *, const char *, const char **); | |
59 static void on_properties(struct context *, const char *, const char **); | |
60 static void on_property(struct context *, const char *, const char **); | |
61 static void on_tile(struct context *, const char *, const char **); | |
62 static void on_tileset(struct context *, const char *, const char **); | |
63 static void die(const char *, ...); | |
64 | |
65 static const struct { | |
66 const char *name; | |
67 void (*begin)(struct context *, const char *, const char **); | |
68 void (*end)(struct context *, const char *); | |
69 } handlers[] = { | |
70 { "data", on_data, NULL }, | |
71 { "image", on_image, NULL }, | |
72 { "layer", on_layer, NULL }, | |
73 { "map", on_map, NULL }, | |
74 { "properties", on_properties, NULL }, | |
75 { "property", on_property, NULL }, | |
76 { "tile", on_tile, NULL }, | |
77 { "tileset", on_tileset, NULL }, | |
78 { NULL, NULL, NULL }, | |
79 }; | |
80 | |
81 static bool | |
82 is_known_map_property(const char *name) | |
83 { | |
84 static const char *known[] = { | |
85 "title", | |
86 NULL | |
87 }; | |
88 | |
89 for (size_t i = 0; known[i]; ++i) | |
90 if (strcmp(known[i], name) == 0) | |
91 return true; | |
92 | |
93 return false; | |
94 } | |
95 | |
96 static void | |
97 write_map_property(const char **attrs) | |
98 { | |
99 /* | |
100 * This is how <map><properties> are defined: | |
101 * | |
102 * 0 1 2 3 | |
103 * <property name="PROPERTY NAME" value="PROPERTY VALUE"> | |
104 */ | |
105 for (size_t i = 0; attrs[i]; i += 4) { | |
106 if (is_known_map_property(attrs[i + 1])) | |
107 printf("%s|%s\n", attrs[i + 1], attrs[i + 3]); | |
108 else | |
109 die("unsupported map property: %s\n", attrs[i + 1]); | |
110 } | |
111 } | |
112 | |
113 static const char * | |
114 find_attr(const char **attrs, const char *key) | |
115 { | |
116 for (size_t i = 0; attrs[i]; i += 2) | |
117 if (strcmp(attrs[i], key) == 0) | |
118 return attrs[i + 1]; | |
119 | |
120 return NULL; | |
121 } | |
122 | |
123 static void | |
124 on_map(struct context *ctx, const char *name, const char **attrs) | |
125 { | |
126 (void)ctx; | |
127 (void)name; | |
128 (void)attrs; | |
129 | |
130 ctx->state = STATE_MAP; | |
131 } | |
132 | |
133 static void | |
134 on_properties(struct context *ctx, const char *name, const char **attrs) | |
135 { | |
136 (void)name; | |
137 (void)attrs; | |
138 | |
139 /* | |
140 * <properties> exist in different parent tags | |
141 */ | |
142 switch (ctx->state) { | |
143 case STATE_MAP: | |
144 ctx->state = STATE_MAP_PROPERTIES; | |
145 break; | |
146 default: | |
147 fprintf(stderr, "warning: unsupported <properties>\n"); | |
148 break; | |
149 } | |
150 } | |
151 | |
152 static void | |
153 on_property(struct context *ctx, const char *name, const char **attrs) | |
154 { | |
155 (void)name; | |
156 (void)attrs; | |
157 | |
158 switch (ctx->state) { | |
159 case STATE_MAP_PROPERTIES: | |
160 write_map_property(attrs); | |
161 break; | |
162 default: | |
163 break; | |
164 } | |
165 } | |
166 | |
167 static void | |
168 on_layer(struct context *ctx, const char *name, const char **attrs) | |
169 { | |
170 (void)name; | |
171 | |
172 /* | |
173 * The layer is defined like this: | |
174 * | |
175 * Values of attrs: | |
176 * 0 1 IGNORED | |
177 * <layer name="background" width="10" height="10"> | |
178 */ | |
179 const char *layername = find_attr(attrs, "name"); | |
180 | |
181 if (!layername) | |
182 die("layer is missing 'name' attribute\n"); | |
183 | |
184 if (strcmp(layername, "background") == 0) | |
185 ctx->layer = LAYER_BACKGROUND; | |
186 else | |
187 die("invalid '%s' value for 'name' property\n", layername); | |
188 | |
189 ctx->state = STATE_LAYER; | |
190 } | |
191 | |
192 static void | |
193 on_data(struct context *ctx, const char *name, const char **attrs) | |
194 { | |
195 (void)name; | |
196 (void)attrs; | |
197 | |
198 if (ctx->state != STATE_LAYER) | |
199 die("reading <data> outside a <layer> node\n"); | |
200 | |
201 ctx->state = STATE_LAYER_DATA; | |
202 } | |
203 | |
204 static void | |
205 on_tile(struct context *ctx, const char *name, const char **attrs) | |
206 { | |
207 (void)name; | |
208 | |
209 /* TODO: add more layer */ | |
210 if (ctx->state != STATE_LAYER_DATA) | |
211 die("reading <tile> outside a <data> node\n"); | |
212 | |
213 /* | |
214 * The tile is defined as following: | |
215 * | |
216 * Values of attrs: | |
217 * 0 1 | |
218 * <tile gid="1" /> | |
219 */ | |
220 const char *gid = find_attr(attrs, "gid"); | |
221 | |
222 if (!gid) | |
223 die("missing 'gid' attribute in <tile> node\n"); | |
224 | |
225 printf("tile|%d|%s\n", layer_indices[ctx->layer], gid); | |
226 } | |
227 | |
228 static void | |
229 on_tileset(struct context *ctx, const char *name, const char **attrs) | |
230 { | |
231 (void)ctx; | |
232 (void)name; | |
233 (void)attrs; | |
234 | |
235 ctx->state = STATE_TILESET; | |
236 } | |
237 | |
238 static void | |
239 on_image(struct context *ctx, const char *name, const char **attrs) | |
240 { | |
241 (void)name; | |
242 | |
243 if (ctx->state != STATE_TILESET) | |
244 die("reading <image> outside a <tileset> node\n"); | |
245 | |
246 /** | |
247 * The tile is defined as following: | |
248 * | |
249 * Values of attrs: | |
250 * 0 1 | |
251 * <image source="name.png" ... /> | |
252 */ | |
253 const char *src = find_attr(attrs, "source"); | |
254 | |
255 if (!src) | |
256 die("missing 'source' attribute in <image> node\n"); | |
257 | |
258 printf("tileset|%s\n", src); | |
259 } | |
260 | |
261 static void | |
262 on_start_element(struct context *ctx, const char *name, const char **attrs) | |
263 { | |
264 assert(ctx); | |
265 assert(name); | |
266 assert(attrs); | |
267 | |
268 size_t i; | |
269 | |
270 for (i = 0; handlers[i].name; ++i) { | |
271 if (strcmp(name, handlers[i].name) == 0) { | |
272 handlers[i].begin(ctx, name, attrs); | |
273 break; | |
274 } | |
275 } | |
276 | |
277 if (!handlers[i].name) | |
278 fprintf(stderr, "unsupported <%s> tag\n", name); | |
279 } | |
280 | |
281 static void | |
282 on_end_element(void *data, const char *name) | |
283 { | |
284 (void)data; | |
285 (void)name; | |
286 #if 0 | |
287 size_t i; | |
288 | |
289 for (i = 0; handlers[i].name; ++i) { | |
290 if (strcmp(name, handlers[i].name) == 0) { | |
291 handlers[i].end(ctx, name, attrs); | |
292 break; | |
293 } | |
294 } | |
295 | |
296 if (!handlers[i].name) | |
297 fprintf(stderr, "unsupported <%s> tag\n", name); | |
298 #endif | |
299 } | |
300 | |
301 static void | |
302 run(XML_Parser parser) | |
303 { | |
304 char buffer[BUFSIZ]; | |
305 size_t nbread; | |
306 struct context ctx = {0}; | |
307 | |
308 XML_SetUserData(parser, &ctx); | |
309 | |
310 while ((nbread = fread(buffer, 1, sizeof (buffer), stdin)) > 0) { | |
311 bool done = nbread < sizeof (buffer); | |
312 | |
313 if (XML_Parse(parser, buffer, nbread, done) == XML_STATUS_ERROR) { | |
314 fprintf(stderr, "%lu: %s\n", | |
315 XML_GetCurrentLineNumber(parser), | |
316 XML_ErrorString(XML_GetErrorCode(parser))); | |
317 exit(1); | |
318 } | |
319 } | |
320 } | |
321 | |
322 static void | |
323 die(const char *fmt, ...) | |
324 { | |
325 assert(fmt); | |
326 | |
327 va_list ap; | |
328 | |
329 va_start(ap, fmt); | |
330 vfprintf(stderr, fmt, ap); | |
331 va_end(ap); | |
332 exit(1); | |
333 } | |
334 | |
335 int | |
336 main(void) | |
337 { | |
338 XML_Parser parser; | |
339 | |
340 parser = XML_ParserCreate(NULL); | |
341 XML_SetElementHandler(parser, | |
342 (XML_StartElementHandler)on_start_element, on_end_element); | |
343 | |
344 run(parser); | |
345 | |
346 XML_ParserFree(parser); | |
347 } |