3 * \brief Load a list of possible graphics modes.
5 * Copyright (c) 2011 Brett Reid
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
13 * b) the "Angband license":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
19 * Imported from Angband 4.2.0 to support main-cocoa.m for Hengband. Did not
20 * bring over the datafile parsing (datafile.h and datafile.c) so the
21 * implementation here is different from that in Angband.
27 #define GFPARSE_HAVE_NOTHING (0)
28 #define GFPARSE_HAVE_NAME (1)
29 #define GFPARSE_HAVE_DIR (2)
30 #define GFPARSE_HAVE_SIZE (4)
31 #define GFPARSE_HAVE_PREF (8)
32 #define GFPARSE_HAVE_EXTRA (16)
33 #define GFPARSE_HAVE_GRAF (32)
35 typedef struct GrafModeParserState {
42 } GrafModeParserState;
45 /* This is the global state exposed to Angband. */
46 graphics_mode *graphics_modes;
47 graphics_mode *current_graphics_mode = NULL;
48 int graphics_mode_high_id;
51 static int check_last_mode(GrafModeParserState* pgps) {
54 if ((pgps->stage & GFPARSE_HAVE_DIR) == 0) {
56 msg_format("no directory set for tile set, %s, in %s",
57 pgps->list->menuname, pgps->file_name);
59 if ((pgps->stage & GFPARSE_HAVE_SIZE) == 0) {
61 msg_format("no size set for tile set, %s, in %s",
62 pgps->list->menuname, pgps->file_name);
64 if ((pgps->stage & GFPARSE_HAVE_PREF) == 0) {
66 msg_format("no preference file for tile set, %s, in %s",
67 pgps->list->menuname, pgps->file_name);
69 if ((pgps->stage & GFPARSE_HAVE_GRAF) == 0) {
71 msg_format("no graf string set for tile set, %s, in %s",
72 pgps->list->menuname, pgps->file_name);
78 static void parse_line(GrafModeParserState* pgps, const char* line) {
79 static const char whitespc[] = " \t\v\f";
83 while (line[offset] && strchr(whitespc, line[offset]) != 0) {
86 if (! line[offset] || line[offset] == '#') {
90 if (strncmp(line + offset, "name:", 5) == 0) {
91 stage = GFPARSE_HAVE_NAME;
93 } else if (strncmp(line + offset, "directory:", 10) == 0) {
94 stage = GFPARSE_HAVE_DIR;
96 } else if (strncmp(line + offset, "size:", 5) == 0) {
97 stage = GFPARSE_HAVE_SIZE;
99 } else if (strncmp(line + offset, "pref:", 5) == 0) {
100 stage = GFPARSE_HAVE_PREF;
102 } else if (strncmp(line + offset, "extra:", 6) == 0) {
103 stage = GFPARSE_HAVE_EXTRA;
105 } else if (strncmp(line + offset, "graf:", 5) == 0) {
106 stage = GFPARSE_HAVE_GRAF;
109 msg_format("Unexpected data at line %d of %s", pgps->line_no,
115 if (stage == GFPARSE_HAVE_NAME) {
116 graphics_mode *new_mode;
118 if (pgps->stage != GFPARSE_HAVE_NOTHING) {
119 if (check_last_mode(pgps) != 0) {
124 pgps->stage = GFPARSE_HAVE_NAME;
125 new_mode = (graphics_mode *) malloc(sizeof(graphics_mode));
128 msg_format("failed memory allocation for tile set "
129 "information at line %d of %s", pgps->line_no,
133 new_mode->pNext = pgps->list;
134 new_mode->grafID = 0;
135 new_mode->alphablend = 0;
136 new_mode->overdrawRow = 0;
137 new_mode->overdrawMax = 0;
138 new_mode->cell_width = 0;
139 new_mode->cell_height = 0;
140 new_mode->path[0] = '\0';
141 new_mode->pref[0] = '\0';
142 new_mode->file[0] = '\0';
143 new_mode->menuname[0] = '\0';
144 new_mode->graf[0] = '\0';
145 pgps->list = new_mode;
147 if (pgps->stage == GFPARSE_HAVE_NOTHING) {
149 msg_format("values set before tile set name given at line %d"
150 " of %s", pgps->line_no, pgps->file_name);
153 if (pgps->stage & stage) {
154 msg_format("values set more than once for tile set, %s, at line "
155 " %d of %s", pgps->list->menuname, pgps->line_no,
161 case GFPARSE_HAVE_NAME:
166 if (sscanf(line + offset, "%u:%n", &id, &nscan) == 1) {
169 msg_format("ID greater than 255 for tile set at line"
170 " %d of %s", pgps->line_no, pgps->file_name);
171 } else if (id == GRAPHICS_NONE) {
173 msg_format("ID of tile set matches value, %d, reserved "
174 " for no graphics at line %d of %s",
175 GRAPHICS_NONE, pgps->line_no, pgps->file_name);
177 graphics_mode *mode = pgps->list->pNext;
183 if (mode->grafID == id) {
185 msg_format("ID for tile set, %s, at line %d of %s"
186 " is the same as for tile set %s",
187 pgps->list->menuname, pgps->line_no,
188 pgps->file_name, mode->menuname);
193 pgps->list->grafID = id;
196 if (strlen(line + offset) >= sizeof(pgps->list->menuname)) {
198 msg_format("name is too long for tile set at line %d"
199 " of %s", pgps->line_no, pgps->file_name);
200 } else if (line[offset] == '\0') {
202 msg_format("empty name for tile set at line %d of %s",
203 pgps->line_no, pgps->file_name);
205 strcpy(pgps->list->menuname, line + offset);
209 msg_format("malformed ID for tile set at line %d of %s",
210 pgps->line_no, pgps->file_name);
215 case GFPARSE_HAVE_DIR:
217 size_t len = strlen(line + offset);
218 size_t sep_len = strlen(PATH_SEP);
220 if (len >= sizeof(pgps->list->path) ||
221 len + pgps->dir_len + sep_len >= sizeof(pgps->list->path)) {
223 msg_format("directory name is too long for tile set, %s, at"
224 " line %d of %s", pgps->list->menuname,
225 pgps->line_no, pgps->file_name);
226 } else if (line[offset] == '\0') {
228 msg_format("empty directory name for tile set, %s, at line"
229 " line %d of %s", pgps->list->menuname,
230 pgps->line_no, pgps->file_name);
233 * Temporarily hack the path to list.txt so it is not necessary
234 * to separately store the base directory for the tile files.
236 char chold = pgps->file_name[pgps->dir_len];
238 pgps->file_name[pgps->dir_len] = '\0';
241 sizeof(pgps->list->path),
245 pgps->file_name[pgps->dir_len] = chold;
250 case GFPARSE_HAVE_SIZE:
255 if (sscanf(line + offset, "%u:%u:%n", &w, &h, &nscan) == 2) {
257 pgps->list->cell_width = w;
260 msg_format("zero width for tile set, %s, at line"
261 " %d of %s", pgps->list->menuname,
262 pgps->line_no, pgps->file_name);
265 pgps->list->cell_height = h;
268 msg_format("zero height for tile set, %s, at line"
269 " %d of %s", pgps->list->menuname,
270 pgps->line_no, pgps->file_name);
273 if (strlen(line + offset) >= sizeof(pgps->list->file)) {
275 msg_format("file name is too long for tile set, %s,"
276 " at line %d of %s", pgps->list->menuname,
277 pgps->line_no, pgps->file_name);
278 } else if (line[offset] == '\0') {
280 msg_format("empty file name for tile set, %s, at line %d"
281 " of %s", pgps->list->menuname, pgps->line_no,
284 (void) strcpy(pgps->list->file, line + offset);
288 msg_format("malformed dimensions for tile set, %s, at line"
289 " %d of %s", pgps->list->menuname, pgps->line_no,
295 case GFPARSE_HAVE_PREF:
296 if (strlen(line + offset) >= sizeof(pgps->list->pref)) {
298 msg_format("preference file name is too long for tile set, %s, "
299 "at line %d of %s", pgps->list->menuname, pgps->line_no,
301 } else if (line[offset] == '\0') {
303 msg_format("empty preference file name for tile set, %s, "
304 "at line %d of %s", pgps->list->menuname, pgps->line_no,
307 strcpy(pgps->list->pref, line + offset);
311 case GFPARSE_HAVE_EXTRA:
313 unsigned int alpha, startdbl, enddbl;
316 if (sscanf(line + offset, "%u:%u:%u%n",
317 &alpha, &startdbl, &enddbl, &nscan) == 3 &&
318 (line[offset + nscan] == '\0' ||
319 strchr(whitespc, line[offset + nscan]) != 0 ||
320 line[offset + nscan] == '#')) {
321 if (startdbl > 255 || enddbl > 255) {
323 msg_format("overdrawMax or overdrawRow is greater than"
324 " 255 for tile set, %s, at line %d of %s",
325 pgps->list->menuname, pgps->line_no,
327 } else if (enddbl < startdbl) {
329 msg_format("overdrawMax less than overdrawRow for tile"
330 "set, %s, at line %d of %s",
331 pgps->list->menuname, pgps->line_no,
334 pgps->list->alphablend = (alpha != 0);
335 pgps->list->overdrawRow = startdbl;
336 pgps->list->overdrawMax = enddbl;
340 msg_format("malformed data for tile set, %s, at line %d of"
341 " %s", pgps->list->menuname, pgps->line_no,
347 case GFPARSE_HAVE_GRAF:
348 if (strlen(line + offset) >= sizeof(pgps->list->graf)) {
350 msg_format("graf string is too long for tile set, %s, at line %d"
351 " of %s", pgps->list->menuname, pgps->line_no,
353 } else if (line[offset] == '\0') {
355 msg_format("empty graf string for tile set, %s, at line %d of %s",
356 pgps->list->menuname, pgps->line_no, pgps->file_name);
358 strcpy(pgps->list->graf, line + offset);
363 if (pgps->result == 0) {
364 pgps->stage |= stage;
369 static void finish_parse_grafmode(GrafModeParserState* pgps,
370 int transfer_results) {
372 * Check what was read for the last mode parsed, since parse_line did
375 if (transfer_results) {
376 if (pgps->list == 0 || pgps->stage == GFPARSE_HAVE_NOTHING) {
377 msg_format("no graphics modes in %s", pgps->file_name);
379 if (check_last_mode(pgps) != 0) {
380 transfer_results = 0;
386 if (transfer_results) {
387 graphics_mode *mode = pgps->list;
388 int max = GRAPHICS_NONE;
390 graphics_mode *new_list;
393 if (mode->grafID > max) {
400 /* Assemble the modes into a contiguous block of memory. */
401 new_list = (graphics_mode *)
402 malloc(sizeof(graphics_mode) * (count + 1));
407 for (i = count - 1; i >= 0; --i, mode = mode->pNext) {
408 memcpy(&(new_list[i]), mode, sizeof(graphics_mode));
409 new_list[i].pNext = &(new_list[i + 1]);
412 /* Hardcode the no graphics option. */
413 new_list[count].pNext = NULL;
414 new_list[count].grafID = GRAPHICS_NONE;
415 new_list[count].alphablend = 0;
416 new_list[count].overdrawRow = 0;
417 new_list[count].overdrawMax = 0;
419 new_list[count].pref, "none", sizeof(new_list[count].pref));
421 new_list[count].path, "", sizeof(new_list[count].path));
423 new_list[count].file, "", sizeof(new_list[count].file));
425 new_list[count].menuname,
427 sizeof(new_list[count].menuname)
430 new_list[count].graf, "ascii", sizeof(new_list[count].graf));
432 /* Release the old global state. */
433 close_graphics_modes();
435 graphics_modes = new_list;
436 graphics_mode_high_id = max;
437 /* Set the default graphics mode to be no graphics */
438 current_graphics_mode = &(graphics_modes[count]);
441 msg_print("failed memory allocation for new graphics modes");
445 /* Release the memory allocated for parsing the file. */
446 while (pgps->list != 0) {
447 graphics_mode *mode = pgps->list;
449 pgps->list = mode->pNext;
455 bool init_graphics_modes(void) {
458 GrafModeParserState gps = { 0, buf, 0, 0, GFPARSE_HAVE_NOTHING, 0 };
461 /* Build the filename */
462 path_build(line, sizeof(line), ANGBAND_DIR_XTRA, "graf");
463 gps.dir_len = strlen(line);
464 path_build(buf, sizeof(buf), line, "list.txt");
466 f = my_fopen(buf, "r");
468 msg_format("Cannot open '%s'.", buf);
471 while (my_fgets(f, line, sizeof line) == 0) {
473 parse_line(&gps, line);
474 if (gps.result != 0) {
479 finish_parse_grafmode(&gps, gps.result == 0);
484 return gps.result == 0;
488 void close_graphics_modes(void) {
489 if (graphics_modes) {
490 free(graphics_modes);
491 graphics_modes = NULL;
493 current_graphics_mode = NULL;
497 graphics_mode *get_graphics_mode(byte id) {
498 graphics_mode *test = graphics_modes;
500 if (test->grafID == id) {