Mercurial > molko
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 } |