comparison src/libmlk-rpg/rpg/map-file.c @ 320:8f9937403749

misc: improve loading of data
author David Demelier <markand@malikania.fr>
date Fri, 01 Oct 2021 20:30:00 +0200
parents libmlk-rpg/rpg/map-file.c@3bfaaf5342a9
children 7d7991f97acf
comparison
equal deleted inserted replaced
319:b843eef4cc35 320:8f9937403749
1 /*
2 * map-file.c -- map file loader
3 *
4 * Copyright (c) 2020-2021 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 <errno.h>
21 #include <libgen.h>
22 #include <limits.h>
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <port/port.h>
29
30 #include <core/alloc.h>
31 #include <core/error.h>
32 #include <core/image.h>
33 #include <core/trace.h>
34 #include <core/zfile.h>
35
36 #include "map-file.h"
37 #include "rpg_p.h"
38
39 #define MAX_F(v) MAX_F_(v)
40 #define MAX_F_(v) "%" #v "[^\n|]"
41
42 struct context {
43 struct map_file *mf; /* Map loader. */
44 struct map *map; /* Map object to fill. */
45 FILE *fp; /* Map file pointer. */
46 char basedir[PATH_MAX]; /* Parent map directory */
47 };
48
49 static int
50 parse_layer_tiles(struct context *ctx, const char *layer_name)
51 {
52 enum map_layer_type layer_type;
53 size_t amount, current;
54
55 if (strcmp(layer_name, "background") == 0)
56 layer_type = MAP_LAYER_TYPE_BACKGROUND;
57 else if (strcmp(layer_name, "foreground") == 0)
58 layer_type = MAP_LAYER_TYPE_FOREGROUND;
59 else if (strcmp(layer_name, "above") == 0)
60 layer_type = MAP_LAYER_TYPE_ABOVE;
61 else
62 return errorf(_("invalid layer type: %s"), layer_name);
63
64 amount = ctx->map->columns * ctx->map->rows;
65 current = 0;
66
67 /*
68 * The next line after a layer declaration is a list of plain integer
69 * that fill the layer tiles.
70 */
71 if (!(ctx->mf->layers[layer_type].tiles = alloc_array0(amount, sizeof (unsigned short))))
72 return -1;
73
74 for (int tile; fscanf(ctx->fp, "%d\n", &tile) && current < amount; ++current)
75 ctx->mf->layers[layer_type].tiles[current] = tile;
76
77 ctx->map->layers[layer_type].tiles = ctx->mf->layers[layer_type].tiles;
78
79 return 0;
80 }
81
82 static int
83 parse_actions(struct context *ctx)
84 {
85 char exec[128 + 1];
86 int x = 0, y = 0, block = 0;
87 unsigned int w = 0, h = 0;
88
89 while (fscanf(ctx->fp, "%d|%d|%u|%u|%d|%128[^\n]\n", &x, &y, &w, &h, &block, exec) >= 5) {
90 struct map_block *reg;
91
92 if (!ctx->mf->load_action) {
93 tracef(_("ignoring action %d,%d,%u,%u,%d,%s"), x, y, w, h, block, exec);
94 continue;
95 }
96
97 ctx->mf->load_action(ctx->map, x, y, w, h, exec);
98
99 /*
100 * Actions do not have concept of collisions because they are
101 * not only used on maps. The map structure has its very own
102 * object to manage collisions but the .map file use the same
103 * directive for simplicity. So create a block region if the
104 * directive has one.
105 */
106 if (block) {
107 if (!(reg = alloc_pool_new(&ctx->mf->blocks)))
108 return -1;
109
110 reg->x = x;
111 reg->y = y;
112 reg->w = w;
113 reg->h = h;
114 }
115 }
116
117 /* Reference the blocks array from map_file. */
118 ctx->map->blocks = ctx->mf->blocks.data;
119 ctx->map->blocksz = ctx->mf->blocks.size;
120
121 return 0;
122 }
123
124 static int
125 parse_layer(struct context *ctx, const char *line)
126 {
127 char layer_name[32 + 1] = {0};
128
129 /* Check if weight/height has been specified. */
130 if (ctx->map->columns == 0 || ctx->map->rows == 0)
131 return errorf(_("missing map dimensions before layer"));
132
133 /* Determine layer type. */
134 if (sscanf(line, "layer|%32s", layer_name) <= 0)
135 return errorf(_("missing layer type definition"));
136
137 if (strcmp(layer_name, "actions") == 0)
138 return parse_actions(ctx);
139
140 return parse_layer_tiles(ctx, layer_name);
141 }
142
143 static int
144 parse_tileset(struct context *ctx, const char *line)
145 {
146 char path[PATH_MAX] = {0}, *p;
147 struct map_file *mf = ctx->mf;
148 struct tileset_file *tf = &mf->tileset_file;
149
150 if (!(p = strchr(line, '|')))
151 return errorf(_("could not parse tileset"));
152
153 snprintf(path, sizeof (path), "%s/%s", ctx->basedir, p + 1);
154
155 if (tileset_file_open(tf, &mf->tileset, path) < 0)
156 return -1;
157
158 ctx->map->tileset = &mf->tileset;
159
160 return 0;
161 }
162
163 static int
164 parse_title(struct context *ctx, const char *line)
165 {
166 if (sscanf(line, "title|" MAX_F(MAP_FILE_TITLE_MAX), ctx->mf->title) != 1 || strlen(ctx->mf->title) == 0)
167 return errorf(_("null map title"));
168
169 ctx->map->title = ctx->mf->title;
170
171 return 0;
172 }
173
174 static int
175 parse_columns(struct context *ctx, const char *line)
176 {
177 if (sscanf(line, "columns|%u", &ctx->map->columns) != 1 || ctx->map->columns == 0)
178 return errorf(_("null map columns"));
179
180 return 0;
181 }
182
183 static int
184 parse_rows(struct context *ctx, const char *line)
185 {
186 if (sscanf(line, "rows|%u", &ctx->map->rows) != 1 || ctx->map->rows == 0)
187 return errorf(_("null map rows"));
188
189 return 0;
190 }
191
192 static int
193 parse_origin(struct context *ctx, const char *line)
194 {
195 if (sscanf(line, "origin|%d|%d", &ctx->map->player_x, &ctx->map->player_y) != 2)
196 return errorf(_("invalid origin"));
197
198 return 0;
199 }
200
201 static int
202 parse_line(struct context *ctx, const char *line)
203 {
204 static const struct {
205 const char *property;
206 int (*read)(struct context *, const char *);
207 } props[] = {
208 { "title", parse_title },
209 { "columns", parse_columns },
210 { "rows", parse_rows },
211 { "tileset", parse_tileset },
212 { "origin", parse_origin },
213 { "layer", parse_layer },
214 };
215
216 for (size_t i = 0; i < UTIL_SIZE(props); ++i)
217 if (strncmp(line, props[i].property, strlen(props[i].property)) == 0)
218 return props[i].read(ctx, line);
219
220 return 0;
221 }
222
223 static int
224 parse(struct context *ctx, const char *path)
225 {
226 char line[1024];
227 char basedir[PATH_MAX];
228
229 strlcpy(basedir, path, sizeof (basedir));
230 strlcpy(ctx->basedir, dirname(basedir), sizeof (ctx->basedir));
231
232 while (fgets(line, sizeof (line), ctx->fp)) {
233 /* Remove \n if any */
234 line[strcspn(line, "\r\n")] = '\0';
235
236 if (parse_line(ctx, line) < 0)
237 return -1;
238 }
239
240 return 0;
241 }
242
243 static int
244 check(struct map *map)
245 {
246 /*
247 * Check that we have parsed every required components.
248 */
249 if (!map->title)
250 return errorf(_("missing title"));
251
252 /*
253 * We don't need to check width/height because parsing layers and
254 * tilesets already check for their presence, so only check layers.
255 */
256 if (!map->layers[0].tiles)
257 return errorf(_("missing background layer"));
258 if (!map->layers[1].tiles)
259 return errorf(_("missing foreground layer"));
260 if (!tileset_ok(map->tileset))
261 return errorf(_("missing tileset"));
262
263 return 0;
264 }
265
266 int
267 map_file_open(struct map_file *file, struct map *map, const char *path)
268 {
269 assert(file);
270 assert(path);
271 assert(map);
272
273 struct context ctx = {
274 .mf = file,
275 .map = map,
276 };
277 struct zfile zf;
278 int ret = 0;
279
280 memset(map, 0, sizeof (*map));
281
282 if (alloc_pool_init(&file->blocks, sizeof (*map->blocks), NULL) < 0)
283 goto fail;
284 if (zfile_open(&zf, path) < 0)
285 goto fail;
286
287 ctx.fp = zf.fp;
288
289 if ((ret = parse(&ctx, path)) < 0 || (ret = check(map)) < 0)
290 goto fail;
291
292 zfile_close(&zf);
293
294 return 0;
295
296 fail:
297 errorf("%s: %s", path, strerror(errno));
298 map_finish(map);
299 map_file_finish(file);
300 zfile_close(&zf);
301
302 return -1;
303 }
304
305 void
306 map_file_finish(struct map_file *file)
307 {
308 assert(file);
309
310 free(file->layers[0].tiles);
311 free(file->layers[1].tiles);
312 free(file->layers[2].tiles);
313
314 tileset_file_finish(&file->tileset_file);
315 alloc_pool_finish(&file->blocks);
316
317 memset(file, 0, sizeof (*file));
318 }