phantasia

Phantasia - 2D SDL3 RPG prototype.
git clone git://git.beep.wimdupont.com/phantasia.git
Log | Files | Refs | README | LICENSE

mapc.c (5062B)


      1 #include "engine/world.h"
      2 #include "game/content.h"
      3 #include "config.h"
      4 
      5 #include <stdint.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #define LEN(a) (sizeof(a) / sizeof(0[a]))
     11 
     12 enum {
     13 	PH_MAP_LINE_MAX = 512,
     14 };
     15 
     16 typedef struct {
     17 	uint32_t magic;
     18 	uint32_t version;
     19 	uint32_t width;
     20 	uint32_t height;
     21 	uint32_t marker_count;
     22 	char name[PH_AREA_NAME_MAX];
     23 } PhMapHeader;
     24 
     25 typedef struct {
     26 	uint32_t symbol;
     27 	uint32_t tile_x;
     28 	uint32_t tile_y;
     29 } PhMapMarker;
     30 
     31 static int
     32 ph_tile_index(unsigned char symbol)
     33 {
     34 	size_t i;
     35 
     36 	for (i = 0; i < LEN(ph_tile_defs); ++i)
     37 		if (ph_tile_defs[i].symbol == symbol) return (int)i;
     38 	return -1;
     39 }
     40 
     41 static const PhMarkerDef *
     42 ph_marker_def(int area_id, unsigned char symbol)
     43 {
     44 	size_t i;
     45 
     46 	for (i = 0; i < LEN(ph_marker_defs); ++i)
     47 		if (ph_marker_defs[i].area_id == area_id &&
     48 				ph_marker_defs[i].symbol == symbol) return &ph_marker_defs[i];
     49 	return NULL;
     50 }
     51 
     52 static int
     53 ph_area_id(const char *path)
     54 {
     55 	size_t i;
     56 
     57 	for (i = 0; i < LEN(ph_area_defs); ++i)
     58 		if (strcmp(ph_area_defs[i].path, path) == 0) return ph_area_defs[i].id;
     59 	return 0;
     60 }
     61 
     62 static void
     63 ph_strip_newline(char *line)
     64 {
     65 	size_t len = strlen(line);
     66 
     67 	while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
     68 		line[--len] = '\0';
     69 	}
     70 }
     71 
     72 static int
     73 ph_compile_map(const char *src_path, const char *dst_path)
     74 {
     75 	FILE *src;
     76 	FILE *dst;
     77 	PhMapHeader header = {
     78 		.magic = PH_MAP_MAGIC,
     79 		.version = PH_MAP_VERSION,
     80 	};
     81 	char line[PH_MAP_LINE_MAX];
     82 	unsigned char *tiles = NULL;
     83 	PhMapMarker *markers = NULL;
     84 	size_t tile_cap = 0;
     85 	size_t tile_count = 0;
     86 	size_t marker_cap = 0;
     87 	int in_tiles = 0;
     88 	int area_id = ph_area_id(dst_path);
     89 
     90 	if (!area_id) {
     91 		fprintf(stderr, "%s: output path has no area definition\n", dst_path);
     92 		return 1;
     93 	}
     94 
     95 	if (LEN(ph_tile_defs) > 256) {
     96 		fprintf(stderr, "too many configured tile types\n");
     97 		return 1;
     98 	}
     99 
    100 	src = fopen(src_path, "r");
    101 	if (!src) {
    102 		perror(src_path);
    103 		return 1;
    104 	}
    105 
    106 	while (fgets(line, sizeof(line), src)) {
    107 		size_t len;
    108 		size_t x;
    109 
    110 		ph_strip_newline(line);
    111 		if (!in_tiles && strncmp(line, "name:", 5) == 0) {
    112 			const char *name = line + 5;
    113 			size_t name_len;
    114 
    115 			while (*name == ' ' || *name == '\t') {
    116 				++name;
    117 			}
    118 			name_len = strlen(name);
    119 			if (name_len >= sizeof(header.name)) {
    120 				fprintf(stderr, "%s: map name too long\n", src_path);
    121 				free(markers);
    122 				free(tiles);
    123 				fclose(src);
    124 				return 1;
    125 			}
    126 			memcpy(header.name, name, name_len + 1);
    127 			continue;
    128 		}
    129 		if (!in_tiles && line[0] == '\0') {
    130 			in_tiles = 1;
    131 			continue;
    132 		}
    133 		if (line[0] == '\0') {
    134 			continue;
    135 		}
    136 
    137 		len = strlen(line);
    138 		for (x = 0; x < len; ++x) {
    139 			const PhMarkerDef *marker = ph_marker_def(area_id,
    140 				(unsigned char)line[x]);
    141 			int tile = ph_tile_index(marker ? marker->tile_symbol :
    142 				(unsigned char)line[x]);
    143 
    144 			if (tile < 0) {
    145 				fprintf(stderr, "%s: unknown tile or marker '%c' in row %u\n",
    146 					src_path, line[x], header.height + 1);
    147 				free(markers);
    148 				free(tiles);
    149 				fclose(src);
    150 				return 1;
    151 			}
    152 			if (marker) {
    153 				PhMapMarker *new_markers;
    154 
    155 				if (header.marker_count == marker_cap) {
    156 					marker_cap = marker_cap ? marker_cap * 2 : 16;
    157 					new_markers = realloc(markers,
    158 						marker_cap * sizeof(*markers));
    159 					if (!new_markers) {
    160 						free(markers);
    161 						free(tiles);
    162 						fclose(src);
    163 						return 1;
    164 					}
    165 					markers = new_markers;
    166 				}
    167 				markers[header.marker_count++] = (PhMapMarker){
    168 					(unsigned char)line[x], (uint32_t)x, header.height };
    169 			}
    170 			line[x] = (char)tile;
    171 		}
    172 		if (header.width == 0) {
    173 			header.width = (uint32_t)len;
    174 		} else if (len != header.width) {
    175 			fprintf(stderr, "%s: inconsistent row width: got %zu, expected %u\n",
    176 				src_path, len, header.width);
    177 			free(markers);
    178 			free(tiles);
    179 			fclose(src);
    180 			return 1;
    181 		}
    182 
    183 		if (tile_count + len > tile_cap) {
    184 			size_t new_cap = tile_cap ? tile_cap * 2 : 256;
    185 			unsigned char *new_tiles;
    186 
    187 			while (new_cap < tile_count + len) {
    188 				new_cap *= 2;
    189 			}
    190 			new_tiles = realloc(tiles, new_cap);
    191 			if (!new_tiles) {
    192 				free(markers);
    193 				free(tiles);
    194 				fclose(src);
    195 				return 1;
    196 			}
    197 			tiles = new_tiles;
    198 			tile_cap = new_cap;
    199 		}
    200 
    201 		memcpy(tiles + tile_count, line, len);
    202 		tile_count += len;
    203 		++header.height;
    204 	}
    205 	fclose(src);
    206 
    207 	if (header.width == 0 || header.height == 0 || header.name[0] == '\0') {
    208 		fprintf(stderr, "%s: missing name or tile data\n", src_path);
    209 		free(markers);
    210 		free(tiles);
    211 		return 1;
    212 	}
    213 
    214 	dst = fopen(dst_path, "wb");
    215 	if (!dst) {
    216 		perror(dst_path);
    217 		free(markers);
    218 		free(tiles);
    219 		return 1;
    220 	}
    221 
    222 	if (fwrite(&header, sizeof(header), 1, dst) != 1 ||
    223 			fwrite(tiles, 1, tile_count, dst) != tile_count ||
    224 			fwrite(markers, sizeof(*markers), header.marker_count, dst) !=
    225 				header.marker_count) {
    226 		perror(dst_path);
    227 		free(markers);
    228 		free(tiles);
    229 		fclose(dst);
    230 		return 1;
    231 	}
    232 
    233 	free(markers);
    234 	free(tiles);
    235 	fclose(dst);
    236 	return 0;
    237 }
    238 
    239 int
    240 main(int argc, char **argv)
    241 {
    242 	if (argc != 3) {
    243 		fprintf(stderr, "usage: %s <map.txt> <map.phmap>\n", argv[0]);
    244 		return 1;
    245 	}
    246 	return ph_compile_map(argv[1], argv[2]);
    247 }