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 }