comparison libmlk-rpg/rpg/map-file.c @ 243:71b3b7036de7

misc: lot of cleanups, - prefix libraries with libmlk, - move assets from source directories closes #2520, - prefix header guards closes #2519
author David Demelier <markand@malikania.fr>
date Sat, 28 Nov 2020 22:37:30 +0100
parents librpg/rpg/map-file.c@76afe639fd72
children 8ef7fb7f14ad
comparison
equal deleted inserted replaced
242:4c24604efcab 243:71b3b7036de7
1 /*
2 * map-file.c -- map file loader
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 #define _XOPEN_SOURCE 700
20 #include <assert.h>
21 #include <errno.h>
22 #include <libgen.h>
23 #include <limits.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <core/alloc.h>
30 #include <core/error.h>
31 #include <core/image.h>
32 #include <core/trace.h>
33
34 #include "map-file.h"
35 #include "rpg_p.h"
36
37 /* Create %<v>c string literal for scanf */
38 #define MAX_F(v) MAX_F_(v)
39 #define MAX_F_(v) "%" #v "c"
40
41 struct context {
42 struct map_file *mf; /* Map loader. */
43 struct map *map; /* Map object to fill. */
44 FILE *fp; /* Map file pointer. */
45 char basedir[PATH_MAX]; /* Parent map directory */
46 };
47
48 static bool
49 parse_layer_tiles(struct context *ctx, const char *layer_name)
50 {
51 enum map_layer_type layer_type;
52 size_t amount, current;
53
54 if (strcmp(layer_name, "background") == 0)
55 layer_type = MAP_LAYER_TYPE_BACKGROUND;
56 else if (strcmp(layer_name, "foreground") == 0)
57 layer_type = MAP_LAYER_TYPE_FOREGROUND;
58 else if (strcmp(layer_name, "above") == 0)
59 layer_type = MAP_LAYER_TYPE_ABOVE;
60 else
61 return errorf(_("invalid layer type: %s"), layer_name);
62
63 amount = ctx->map->columns * ctx->map->rows;
64 current = 0;
65
66 /*
67 * The next line after a layer declaration is a list of plain integer
68 * that fill the layer tiles.
69 */
70 if (!(ctx->mf->layers[layer_type].tiles = alloc_array0(amount, sizeof (unsigned short))))
71 return false;
72
73 for (int tile; fscanf(ctx->fp, "%d\n", &tile) && current < amount; ++current)
74 ctx->mf->layers[layer_type].tiles[current] = tile;
75
76 ctx->map->layers[layer_type].tiles = ctx->mf->layers[layer_type].tiles;
77
78 return true;
79 }
80
81 static bool
82 parse_actions(struct context *ctx)
83 {
84 char exec[128 + 1];
85 int x = 0, y = 0;
86 unsigned int w = 0, h = 0;
87
88 while (fscanf(ctx->fp, "%d|%d|%u|%u|%128[^\n]\n", &x, &y, &w, &h, exec) == 5) {
89 struct action *act;
90
91 if (!ctx->mf->load_action) {
92 tracef(_("ignoring action %d,%d,%u,%u,%s"), x, y, w, h, exec);
93 continue;
94 }
95
96 if ((act = ctx->mf->load_action(ctx->map, x, y, w, h, exec)))
97 action_stack_add(&ctx->map->actions, act);
98 }
99
100 return true;
101 }
102
103 static bool
104 parse_layer(struct context *ctx, const char *line)
105 {
106 char layer_name[32 + 1] = {0};
107
108 /* Check if weight/height has been specified. */
109 if (ctx->map->columns == 0 || ctx->map->rows == 0)
110 return errorf(_("missing map dimensions before layer"));
111
112 /* Determine layer type. */
113 if (sscanf(line, "layer|%32s", layer_name) <= 0)
114 return errorf(_("missing layer type definition"));
115
116 if (strcmp(layer_name, "actions") == 0)
117 return parse_actions(ctx);
118
119 return parse_layer_tiles(ctx, layer_name);
120 }
121
122 static bool
123 parse_tileset(struct context *ctx, const char *line)
124 {
125 char path[PATH_MAX] = {0}, *p;
126 struct map_file *mf = ctx->mf;
127 struct tileset_file *tf = &mf->tileset_file;
128
129 if (!(p = strchr(line, '|')))
130 return errorf(_("could not parse tileset"));
131
132 snprintf(path, sizeof (path), "%s/%s", ctx->basedir, p + 1);
133
134 if (!tileset_file_open(tf, &mf->tileset, path))
135 return false;
136
137 ctx->map->tileset = &mf->tileset;
138
139 return true;
140 }
141
142 static bool
143 parse_title(struct context *ctx, const char *line)
144 {
145 if (sscanf(line, "title|" MAX_F(MAP_FILE_TITLE_MAX), ctx->mf->title) != 1 || strlen(ctx->mf->title) == 0)
146 return errorf(_("null map title"));
147
148 ctx->map->title = ctx->mf->title;
149
150 return true;
151 }
152
153 static bool
154 parse_columns(struct context *ctx, const char *line)
155 {
156 if (sscanf(line, "columns|%u", &ctx->map->columns) != 1 || ctx->map->columns == 0)
157 return errorf(_("null map columns"));
158
159 return true;
160 }
161
162 static bool
163 parse_rows(struct context *ctx, const char *line)
164 {
165 if (sscanf(line, "rows|%u", &ctx->map->rows) != 1 || ctx->map->rows == 0)
166 return errorf(_("null map rows"));
167
168 return true;
169 }
170
171 static bool
172 parse_origin(struct context *ctx, const char *line)
173 {
174 if (sscanf(line, "origin|%d|%d", &ctx->map->player_x, &ctx->map->player_y) != 2)
175 return errorf(_("invalid origin"));
176
177 return true;
178 }
179
180 static bool
181 parse_line(struct context *ctx, const char *line)
182 {
183 static const struct {
184 const char *property;
185 bool (*read)(struct context *, const char *);
186 } props[] = {
187 { "title", parse_title },
188 { "columns", parse_columns },
189 { "rows", parse_rows },
190 { "tileset", parse_tileset },
191 { "origin", parse_origin },
192 { "layer", parse_layer },
193 };
194
195 for (size_t i = 0; i < NELEM(props); ++i)
196 if (strncmp(line, props[i].property, strlen(props[i].property)) == 0)
197 return props[i].read(ctx, line);
198
199 return true;
200 }
201
202 static bool
203 parse(struct context *ctx, const char *path)
204 {
205 char line[1024];
206 char basedir[PATH_MAX];
207
208 snprintf(basedir, sizeof (basedir), "%s", path);
209 snprintf(ctx->basedir, sizeof (ctx->basedir), "%s", dirname(basedir));
210
211 while (fgets(line, sizeof (line), ctx->fp)) {
212 /* Remove \n if any */
213 line[strcspn(line, "\n")] = '\0';
214
215 if (!parse_line(ctx, line))
216 return false;
217 }
218
219 return true;
220 }
221
222 static bool
223 check(struct map *map)
224 {
225 /*
226 * Check that we have parsed every required components.
227 */
228 if (!map->title)
229 return errorf(_("missing title"));
230
231 /*
232 * We don't need to check width/height because parsing layers and
233 * tilesets already check for their presence, so only check layers.
234 */
235 if (!map->layers[0].tiles)
236 return errorf(_("missing background layer"));
237 if (!map->layers[1].tiles)
238 return errorf(_("missing foreground layer"));
239 if (!tileset_ok(map->tileset))
240 return errorf(_("missing tileset"));
241
242 return true;
243 }
244
245 bool
246 map_file_open(struct map_file *file, struct map *map, const char *path)
247 {
248 assert(file);
249 assert(path);
250 assert(map);
251
252 struct context ctx = {
253 .mf = file,
254 .map = map,
255 };
256 bool ret = true;
257
258 memset(map, 0, sizeof (*map));
259
260 if (!(ctx.fp = fopen(path, "r")))
261 return errorf("%s", strerror(errno));
262
263 if (!(ret = parse(&ctx, path)) || !(ret = check(map))) {
264 map_finish(map);
265 map_file_finish(file);
266 }
267
268 fclose(ctx.fp);
269
270 return ret;
271 }
272
273 void
274 map_file_finish(struct map_file *file)
275 {
276 assert(file);
277
278 free(file->layers[0].tiles);
279 free(file->layers[1].tiles);
280 free(file->layers[2].tiles);
281
282 tileset_file_finish(&file->tileset_file);
283
284 memset(file, 0, sizeof (*file));
285 }