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 }