OSDN Git Service

Merge branch 'master' of git://git.osdn.net/gitroot/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / grafmode.c
1 /**
2  * \file grafmode.c
3  * \brief Load a list of possible graphics modes.
4  *
5  * Copyright (c) 2011 Brett Reid
6  *
7  * This work is free software; you can redistribute it and/or modify it
8  * under the terms of either:
9  *
10  * a) the GNU General Public License as published by the Free Software
11  *    Foundation, version 2, or
12  *
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.
17  */
18 /*
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.
22  */
23 #include "angband.h"
24 #include "grafmode.h"
25
26
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)
34
35 typedef struct GrafModeParserState {
36     graphics_mode* list;
37     char* file_name;
38     int dir_len;
39     int line_no;
40     int stage;
41     errr result;
42 } GrafModeParserState;
43
44
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;
49
50
51 static int check_last_mode(GrafModeParserState* pgps) {
52     int result = 0;
53
54     if ((pgps->stage & GFPARSE_HAVE_DIR) == 0) {
55         result = 1;
56         msg_format("no directory set for tile set, %s, in %s",
57                    pgps->list->menuname, pgps->file_name);
58     }
59     if ((pgps->stage & GFPARSE_HAVE_SIZE) == 0) {
60         result = 1;
61         msg_format("no size set for tile set, %s, in %s",
62                    pgps->list->menuname, pgps->file_name);
63     }
64     if ((pgps->stage & GFPARSE_HAVE_PREF) == 0) {
65         result = 1;
66         msg_format("no preference file for tile set, %s, in %s",
67                    pgps->list->menuname, pgps->file_name);
68     }
69     if ((pgps->stage & GFPARSE_HAVE_GRAF) == 0) {
70         result = 1;
71         msg_format("no graf string set for tile set, %s, in %s",
72                    pgps->list->menuname, pgps->file_name);
73     }
74     return result;
75 }
76
77
78 static void parse_line(GrafModeParserState* pgps, const char* line) {
79     static const char whitespc[] = " \t\v\f";
80     int offset = 0;
81     int stage;
82
83     while (line[offset] && strchr(whitespc, line[offset]) != 0) {
84         ++offset;
85     }
86     if (! line[offset] || line[offset] == '#') {
87         return;
88     }
89
90     if (strncmp(line + offset, "name:", 5) == 0) {
91         stage = GFPARSE_HAVE_NAME;
92         offset += 5;
93     } else if (strncmp(line + offset, "directory:", 10) == 0) {
94         stage = GFPARSE_HAVE_DIR;
95         offset += 10;
96     } else if (strncmp(line + offset, "size:", 5) == 0) {
97         stage = GFPARSE_HAVE_SIZE;
98         offset += 5;
99     } else if (strncmp(line + offset, "pref:", 5) == 0) {
100         stage = GFPARSE_HAVE_PREF;
101         offset += 5;
102     } else if (strncmp(line + offset, "extra:", 6) == 0) {
103         stage = GFPARSE_HAVE_EXTRA;
104         offset += 6;
105     } else if (strncmp(line + offset, "graf:", 5) == 0) {
106         stage = GFPARSE_HAVE_GRAF;
107         offset += 5;
108     } else {
109         msg_format("Unexpected data at line %d of %s", pgps->line_no,
110                    pgps->file_name);
111         pgps->result = 1;
112         return;
113     }
114
115     if (stage == GFPARSE_HAVE_NAME) {
116         graphics_mode *new_mode;
117
118         if (pgps->stage != GFPARSE_HAVE_NOTHING) {
119             if (check_last_mode(pgps) != 0) {
120                 pgps->result = 1;
121             }
122         }
123
124         pgps->stage = GFPARSE_HAVE_NAME;
125         new_mode = (graphics_mode *) malloc(sizeof(graphics_mode));
126         if (new_mode == 0) {
127             pgps->result = 1;
128             msg_format("failed memory allocation for tile set "
129                        "information at line %d of %s", pgps->line_no,
130                        pgps->file_name);
131             return;
132         }
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;
146     } else {
147         if (pgps->stage == GFPARSE_HAVE_NOTHING) {
148             pgps->result = 1;
149             msg_format("values set before tile set name given at line %d"
150                        " of %s", pgps->line_no, pgps->file_name);
151             return;
152         }
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,
156                        pgps->file_name);
157         }
158     }
159
160     switch (stage) {
161     case GFPARSE_HAVE_NAME:
162         {
163             unsigned int id;
164             int nscan;
165
166             if (sscanf(line + offset, "%u:%n", &id, &nscan) == 1) {
167                 if (id > 255) {
168                     pgps->result = 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) {
172                     pgps->result = 1;
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);
176                 } else {
177                     graphics_mode *mode = pgps->list->pNext;
178
179                     while (1) {
180                         if (mode == 0) {
181                             break;
182                         }
183                         if (mode->grafID == id) {
184                             pgps->result = 1;
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);
189                             break;
190                         }
191                         mode = mode->pNext;
192                     }
193                     pgps->list->grafID = id;
194                 }
195                 offset += nscan;
196                 if (strlen(line + offset) >= sizeof(pgps->list->menuname)) {
197                     pgps->result = 1;
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') {
201                     pgps->result = 1;
202                     msg_format("empty name for tile set at line %d of %s",
203                                pgps->line_no, pgps->file_name);
204                 } else {
205                     strcpy(pgps->list->menuname, line + offset);
206                 }
207             } else {
208                 pgps->result = 1;
209                 msg_format("malformed ID for tile set at line %d of %s",
210                            pgps->line_no, pgps->file_name);
211             }
212         }
213         break;
214
215     case GFPARSE_HAVE_DIR:
216         {
217             size_t len = strlen(line + offset);
218             size_t sep_len = strlen(PATH_SEP);
219
220             if (len >= sizeof(pgps->list->path) ||
221                 len + pgps->dir_len + sep_len >= sizeof(pgps->list->path)) {
222                 pgps->result = 1;
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') {
227                 pgps->result = 1;
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);
231             } else {
232                 /*
233                  * Temporarily hack the path to list.txt so it is not necessary
234                  * to separately store the base directory for the tile files.
235                  */
236                 char chold = pgps->file_name[pgps->dir_len];
237
238                 pgps->file_name[pgps->dir_len] = '\0';
239                 path_build(
240                     pgps->list->path,
241                     sizeof(pgps->list->path),
242                     pgps->file_name,
243                     line + offset
244                 );
245                 pgps->file_name[pgps->dir_len] = chold;
246             }
247         }
248         break;
249
250     case GFPARSE_HAVE_SIZE:
251         {
252             unsigned w, h;
253             int nscan;
254
255             if (sscanf(line + offset, "%u:%u:%n", &w, &h, &nscan) == 2) {
256                 if (w > 0) {
257                     pgps->list->cell_width = w;
258                 } else {
259                     pgps->result = 1;
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);
263                 }
264                 if (h > 0) {
265                     pgps->list->cell_height = h;
266                 } else {
267                     pgps->result = 1;
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);
271                 }
272                 offset += nscan;
273                 if (strlen(line + offset) >= sizeof(pgps->list->file)) {
274                     pgps->result = 1;
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') {
279                     pgps->result = 1;
280                     msg_format("empty file name for tile set, %s, at line %d"
281                                " of %s", pgps->list->menuname, pgps->line_no,
282                                pgps->file_name);
283                 } else {
284                     (void) strcpy(pgps->list->file, line + offset);
285                 }
286             } else {
287                 pgps->result = 1;
288                 msg_format("malformed dimensions for tile set, %s, at line"
289                            " %d of %s", pgps->list->menuname, pgps->line_no,
290                            pgps->file_name);
291             }
292         }
293         break;
294
295     case GFPARSE_HAVE_PREF:
296         if (strlen(line + offset) >= sizeof(pgps->list->pref)) {
297             pgps->result = 1;
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,
300                        pgps->file_name);
301         } else if (line[offset] == '\0') {
302             pgps->result = 1;
303             msg_format("empty preference file name for tile set, %s, "
304                        "at line %d of %s", pgps->list->menuname, pgps->line_no,
305                        pgps->file_name);
306         } else {
307             strcpy(pgps->list->pref, line + offset);
308         }
309         break;
310
311     case GFPARSE_HAVE_EXTRA:
312         {
313             unsigned int alpha, startdbl, enddbl;
314             int nscan;
315
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) {
322                     pgps->result = 1;
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,
326                                pgps->file_name);
327                 } else if (enddbl < startdbl) {
328                     pgps->result = 1;
329                     msg_format("overdrawMax less than overdrawRow for tile"
330                                "set, %s, at line %d of %s",
331                                pgps->list->menuname, pgps->line_no,
332                                pgps->file_name);
333                 } else {
334                     pgps->list->alphablend = (alpha != 0);
335                     pgps->list->overdrawRow = startdbl;
336                     pgps->list->overdrawMax = enddbl;
337                 }
338             } else {
339                 pgps->result = 1;
340                 msg_format("malformed data for tile set, %s, at line %d of"
341                            " %s", pgps->list->menuname, pgps->line_no,
342                            pgps->file_name);
343             }
344         }
345         break;
346
347     case GFPARSE_HAVE_GRAF:
348         if (strlen(line + offset) >= sizeof(pgps->list->graf)) {
349             pgps->result = 1;
350             msg_format("graf string is too long for tile set, %s, at line %d"
351                        " of %s", pgps->list->menuname, pgps->line_no,
352                        pgps->file_name);
353         } else if (line[offset] == '\0') {
354             pgps->result = 1;
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);
357         } else {
358             strcpy(pgps->list->graf, line + offset);
359         }
360         break;
361     }
362
363     if (pgps->result == 0) {
364         pgps->stage |= stage;
365     }
366 }
367
368
369 static void finish_parse_grafmode(GrafModeParserState* pgps,
370                                   int transfer_results) {
371     /*
372      * Check what was read for the last mode parsed, since parse_line did
373      * not.
374      */
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);
378         } else {
379             if (check_last_mode(pgps) != 0) {
380                 transfer_results = 0;
381                 pgps->result = 1;
382             }
383         }
384     }
385
386     if (transfer_results) {
387         graphics_mode *mode = pgps->list;
388         int max = GRAPHICS_NONE;
389         int count = 0;
390         graphics_mode *new_list;
391
392         while (mode) {
393             if (mode->grafID > max) {
394                 max = mode->grafID;
395             }
396             ++count;
397             mode = mode->pNext;
398         }
399
400         /* Assemble the modes into a contiguous block of memory. */
401         new_list = (graphics_mode *)
402             malloc(sizeof(graphics_mode) * (count + 1));
403         if (new_list != 0) {
404             int i;
405
406             mode = pgps->list;
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]);
410             }
411
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;
418             strncpy(
419                 new_list[count].pref, "none", sizeof(new_list[count].pref));
420             strncpy(
421                 new_list[count].path, "", sizeof(new_list[count].path));
422             strncpy(
423                 new_list[count].file, "", sizeof(new_list[count].file));
424             strncpy(
425                 new_list[count].menuname,
426                 "Classic ASCII",
427                 sizeof(new_list[count].menuname)
428             );
429             strncpy(
430                 new_list[count].graf, "ascii", sizeof(new_list[count].graf));
431
432             /* Release the old global state. */
433             close_graphics_modes();
434
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]);
439         } else {
440             pgps->result = 1;
441             msg_print("failed memory allocation for new graphics modes");
442         }
443     }
444
445     /* Release the memory allocated for parsing the file. */
446     while (pgps->list != 0) {
447         graphics_mode *mode = pgps->list;
448
449         pgps->list = mode->pNext;
450         free(mode);
451     }
452 }
453
454
455 bool init_graphics_modes(void) {
456     char buf[1024];
457     char line[1024];
458     GrafModeParserState gps = { 0, buf, 0, 0, GFPARSE_HAVE_NOTHING, 0 };
459     FILE *f;
460
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");
465
466     f = my_fopen(buf, "r");
467     if (!f) {
468         msg_format("Cannot open '%s'.", buf);
469         gps.result = 1;
470     } else {
471         while (my_fgets(f, line, sizeof line) == 0) {
472             ++gps.line_no;
473             parse_line(&gps, line);
474             if (gps.result != 0) {
475                 break;
476             }
477         }
478
479         finish_parse_grafmode(&gps, gps.result == 0);
480         my_fclose(f);
481     }
482
483     /* Result */
484     return gps.result == 0;
485 }
486
487
488 void close_graphics_modes(void) {
489     if (graphics_modes) {
490         free(graphics_modes);
491         graphics_modes = NULL;
492     }
493     current_graphics_mode = NULL;
494 }
495
496
497 graphics_mode *get_graphics_mode(byte id) {
498     graphics_mode *test = graphics_modes;
499     while (test) {
500         if (test->grafID == id) {
501             return test;
502         }
503         test = test->pNext;
504     }
505     return NULL;
506 }