OSDN Git Service

Changed so that font changes on the main window while playing do a better job of...
[hengbandforosx/hengbandosx.git] / src / grafmode.c
index ea95d02..188f288 100644 (file)
  *    and not for profit purposes provided that this copyright and statement
  *    are included in all such copies.  Other copyrights may also apply.
  */
-
+/*
+ * Imported from Angband 4.2.0 to support main-cocoa.m for Hengband.  Did not
+ * bring over the datafile parsing (datafile.h and datafile.c) so the
+ * implementation here is different from that in Angband.
+ */
 #include "angband.h"
-#include "datafile.h"
 #include "grafmode.h"
-#include "init.h"
 
+
+#define GFPARSE_HAVE_NOTHING (0)
+#define GFPARSE_HAVE_NAME (1)
+#define GFPARSE_HAVE_DIR (2)
+#define GFPARSE_HAVE_SIZE (4)
+#define GFPARSE_HAVE_PREF (8)
+#define GFPARSE_HAVE_EXTRA (16)
+#define GFPARSE_HAVE_GRAF (32)
+
+typedef struct GrafModeParserState {
+    graphics_mode* list;
+    char* file_name;
+    int dir_len;
+    int line_no;
+    int stage;
+    errr result;
+} GrafModeParserState;
+
+
+/* This is the global state exposed to Angband. */
 graphics_mode *graphics_modes;
 graphics_mode *current_graphics_mode = NULL;
 int graphics_mode_high_id;
 
-static enum parser_error parse_graf_name(struct parser *p) {
-       graphics_mode *list = parser_priv(p);
-       graphics_mode *mode = mem_zalloc(sizeof(graphics_mode));
-       if (!mode) {
-               return PARSE_ERROR_OUT_OF_MEMORY;
-       }
-       mode->pNext = list;
-       mode->grafID = parser_getuint(p, "index");
-       strncpy(mode->menuname, parser_getstr(p, "menuname"), 32);
-
-       mode->alphablend = 0;
-       mode->overdrawRow = 0;
-       mode->overdrawMax = 0;
-       strncpy(mode->file, "", 32);
-       strncpy(mode->pref, "none", 32);
-       
-       parser_setpriv(p, mode);
-       return PARSE_ERROR_NONE;
-}
-
-static enum parser_error parse_graf_directory(struct parser *p) {
-       graphics_mode *mode = parser_priv(p);
-       const char *dir = parser_getsym(p, "dirname");
-       if (!mode) {
-               return PARSE_ERROR_INVALID_VALUE;
-       }
 
-       /* Build a usable path */
-       path_build(mode->path, sizeof(mode->path), ANGBAND_DIR_TILES, dir);
-
-       return PARSE_ERROR_NONE;
+static int check_last_mode(GrafModeParserState* pgps) {
+    int result = 0;
+
+    if ((pgps->stage & GFPARSE_HAVE_DIR) == 0) {
+       result = 1;
+       msg_format("no directory set for tile set, %s, in %s",
+                  pgps->list->menuname, pgps->file_name);
+    }
+    if ((pgps->stage & GFPARSE_HAVE_SIZE) == 0) {
+       result = 1;
+       msg_format("no size set for tile set, %s, in %s",
+                  pgps->list->menuname, pgps->file_name);
+    }
+    if ((pgps->stage & GFPARSE_HAVE_PREF) == 0) {
+       result = 1;
+       msg_format("no preference file for tile set, %s, in %s",
+                  pgps->list->menuname, pgps->file_name);
+    }
+    if ((pgps->stage & GFPARSE_HAVE_GRAF) == 0) {
+       result = 1;
+       msg_format("no graf string set for tile set, %s, in %s",
+                  pgps->list->menuname, pgps->file_name);
+    }
+    return result;
 }
 
-static enum parser_error parse_graf_size(struct parser *p) {
-       graphics_mode *mode = parser_priv(p);
-       if (!mode) {
-               return PARSE_ERROR_INVALID_VALUE;
-       }
-       mode->cell_width = parser_getuint(p, "wid");
-       mode->cell_height = parser_getuint(p, "hgt");
-       strncpy(mode->file, parser_getstr(p, "filename"), 32);
-       return PARSE_ERROR_NONE;
-}
 
-static enum parser_error parse_graf_pref(struct parser *p) {
-       graphics_mode *mode = parser_priv(p);
-       if (!mode) {
-               return PARSE_ERROR_INVALID_VALUE;
+static void parse_line(GrafModeParserState* pgps, const char* line) {
+    static const char whitespc[] = " \t\v\f";
+    int offset = 0;
+    int stage;
+
+    while (line[offset] && strchr(whitespc, line[offset]) != 0) {
+       ++offset;
+    }
+    if (! line[offset] || line[offset] == '#') {
+       return;
+    }
+
+    if (strncmp(line + offset, "name:", 5) == 0) {
+       stage = GFPARSE_HAVE_NAME;
+       offset += 5;
+    } else if (strncmp(line + offset, "directory:", 10) == 0) {
+       stage = GFPARSE_HAVE_DIR;
+       offset += 10;
+    } else if (strncmp(line + offset, "size:", 5) == 0) {
+       stage = GFPARSE_HAVE_SIZE;
+       offset += 5;
+    } else if (strncmp(line + offset, "pref:", 5) == 0) {
+       stage = GFPARSE_HAVE_PREF;
+       offset += 5;
+    } else if (strncmp(line + offset, "extra:", 6) == 0) {
+       stage = GFPARSE_HAVE_EXTRA;
+       offset += 6;
+    } else if (strncmp(line + offset, "graf:", 5) == 0) {
+       stage = GFPARSE_HAVE_GRAF;
+       offset += 5;
+    } else {
+        msg_format("Unexpected data at line %d of %s", pgps->line_no,
+                  pgps->file_name);
+       pgps->result = 1;
+       return;
+    }
+
+    if (stage == GFPARSE_HAVE_NAME) {
+       graphics_mode *new_mode;
+
+       if (pgps->stage != GFPARSE_HAVE_NOTHING) {
+           if (check_last_mode(pgps) != 0) {
+               pgps->result = 1;
+           }
        }
-       strncpy(mode->pref, parser_getstr(p, "prefname"), 32);
-       return PARSE_ERROR_NONE;
-}
 
-static enum parser_error parse_graf_extra(struct parser *p) {
-       graphics_mode *mode = parser_priv(p);
-       if (!mode) {
-               return PARSE_ERROR_INVALID_VALUE;
+       pgps->stage = GFPARSE_HAVE_NAME;
+       new_mode = (graphics_mode *) malloc(sizeof(graphics_mode));
+       if (new_mode == 0) {
+           pgps->result = 1;
+           msg_format("failed memory allocation for tile set "
+                      "information at line %d of %s", pgps->line_no,
+                      pgps->file_name);
+           return;
        }
-       mode->alphablend = parser_getuint(p, "alpha");
-       mode->overdrawRow = parser_getuint(p, "row");
-       mode->overdrawMax = parser_getuint(p, "max");
-       return PARSE_ERROR_NONE;
-}
-
-static struct parser *init_parse_grafmode(void) {
-       struct parser *p = parser_new();
-       parser_setpriv(p, NULL);
-
-       parser_reg(p, "name uint index str menuname", parse_graf_name);
-       parser_reg(p, "directory sym dirname", parse_graf_directory);
-       parser_reg(p, "size uint wid uint hgt str filename", parse_graf_size);
-       parser_reg(p, "pref str prefname", parse_graf_pref);
-       parser_reg(p, "extra uint alpha uint row uint max", parse_graf_extra);
-
-       return p;
-}
-
-static errr finish_parse_grafmode(struct parser *p) {
-       graphics_mode *mode, *n;
-       int max = 0;
-       int count = 0;
-       int i;
-       
-       /* See how many graphics modes we have and what the highest index is */
-       if (p) {
-               mode = parser_priv(p);
-               while (mode) {
-                       if (mode->grafID > max) {
-                               max = mode->grafID;
+       new_mode->pNext = pgps->list;
+       new_mode->grafID = 0;
+       new_mode->alphablend = 0;
+       new_mode->overdrawRow = 0;
+       new_mode->overdrawMax = 0;
+       new_mode->cell_width = 0;
+       new_mode->cell_height = 0;
+       new_mode->path[0] = '\0';
+       new_mode->pref[0] = '\0';
+       new_mode->file[0] = '\0';
+       new_mode->menuname[0] = '\0';
+       new_mode->graf[0] = '\0';
+       pgps->list = new_mode;
+    } else {
+       if (pgps->stage == GFPARSE_HAVE_NOTHING) {
+           pgps->result = 1;
+           msg_format("values set before tile set name given at line %d"
+                      " of %s", pgps->line_no, pgps->file_name);
+           return;
+       }
+       if (pgps->stage & stage) {
+           msg_format("values set more than once for tile set, %s, at line "
+                      " %d of %s", pgps->list->menuname, pgps->line_no,
+                      pgps->file_name);
+       }
+    }
+
+    switch (stage) {
+    case GFPARSE_HAVE_NAME:
+       {
+           unsigned int id;
+           int nscan;
+
+           if (sscanf(line + offset, "%u:%n", &id, &nscan) == 1) {
+               if (id > 255) {
+                   pgps->result = 1;
+                   msg_format("ID greater than 255 for tile set at line"
+                              " %d of %s", pgps->line_no, pgps->file_name);
+               } else if (id == GRAPHICS_NONE) {
+                   pgps->result = 1;
+                   msg_format("ID of tile set matches value, %d, reserved "
+                              " for no graphics at line %d of %s",
+                              GRAPHICS_NONE, pgps->line_no, pgps->file_name);
+               } else {
+                   graphics_mode *mode = pgps->list->pNext;
+
+                   while (1) {
+                       if (mode == 0) {
+                           break;
+                       }
+                       if (mode->grafID == id) {
+                           pgps->result = 1;
+                           msg_format("ID for tile set, %s, at line %d of %s"
+                                      " is the same as for tile set %s",
+                                      pgps->list->menuname, pgps->line_no,
+                                      pgps->file_name, mode->menuname);
+                           break;
                        }
-                       count++;
                        mode = mode->pNext;
+                   }
+                   pgps->list->grafID = id;
+               }
+               offset += nscan;
+               if (strlen(line + offset) >= sizeof(pgps->list->menuname)) {
+                   pgps->result = 1;
+                   msg_format("name is too long for tile set at line %d"
+                              " of %s", pgps->line_no, pgps->file_name);
+               } else if (line[offset] == '\0') {
+                   pgps->result = 1;
+                   msg_format("empty name for tile set at line %d of %s",
+                              pgps->line_no, pgps->file_name);
+               } else {
+                   strcpy(pgps->list->menuname, line + offset);
                }
+           } else {
+               pgps->result = 1;
+               msg_format("malformed ID for tile set at line %d of %s",
+                          pgps->line_no, pgps->file_name);
+           }
        }
-
-       /* Copy the loaded modes to the global variable */
-       if (graphics_modes) {
-               close_graphics_modes();
+       break;
+
+    case GFPARSE_HAVE_DIR:
+       {
+           size_t len = strlen(line + offset);
+           size_t sep_len = strlen(PATH_SEP);
+
+           if (len >= sizeof(pgps->list->path) ||
+               len + pgps->dir_len + sep_len >= sizeof(pgps->list->path)) {
+               pgps->result = 1;
+               msg_format("directory name is too long for tile set, %s, at"
+                          " line %d of %s", pgps->list->menuname,
+                          pgps->line_no, pgps->file_name);
+           } else if (line[offset] == '\0') {
+               pgps->result = 1;
+               msg_format("empty directory name for tile set, %s, at line"
+                          " line %d of %s", pgps->list->menuname,
+                          pgps->line_no, pgps->file_name);
+           } else {
+               /*
+                * Temporarily hack the path to list.txt so it is not necessary
+                * to separately store the base directory for the tile files.
+                */
+               char chold = pgps->file_name[pgps->dir_len];
+
+               pgps->file_name[pgps->dir_len] = '\0';
+               path_build(
+                   pgps->list->path,
+                   sizeof(pgps->list->path),
+                   pgps->file_name,
+                   line + offset
+               );
+               pgps->file_name[pgps->dir_len] = chold;
+           }
        }
-
-       graphics_modes = mem_zalloc(sizeof(graphics_mode) * (count+1));
-       if (p) {
-               mode = parser_priv(p);
-               for (i = count-1; i >= 0; i--, mode = mode->pNext) {
-                       memcpy(&(graphics_modes[i]), mode, sizeof(graphics_mode));
-                       graphics_modes[i].pNext = &(graphics_modes[i+1]);
+       break;
+
+    case GFPARSE_HAVE_SIZE:
+       {
+           unsigned w, h;
+           int nscan;
+
+           if (sscanf(line + offset, "%u:%u:%n", &w, &h, &nscan) == 2) {
+               if (w > 0) {
+                   pgps->list->cell_width = w;
+               } else {
+                   pgps->result = 1;
+                   msg_format("zero width for tile set, %s, at line"
+                              " %d of %s", pgps->list->menuname,
+                              pgps->line_no, pgps->file_name);
+               }
+               if (h > 0) {
+                   pgps->list->cell_height = h;
+               } else {
+                   pgps->result = 1;
+                   msg_format("zero height for tile set, %s, at line"
+                              " %d of %s", pgps->list->menuname,
+                              pgps->line_no, pgps->file_name);
                }
+               offset += nscan;
+               if (strlen(line + offset) >= sizeof(pgps->list->file)) {
+                   pgps->result = 1;
+                   msg_format("file name is too long for tile set, %s,"
+                              " at line %d of %s", pgps->list->menuname,
+                              pgps->line_no, pgps->file_name);
+               } else if (line[offset] == '\0') {
+                   pgps->result = 1;
+                   msg_format("empty file name for tile set, %s, at line %d"
+                              " of %s", pgps->list->menuname, pgps->line_no,
+                              pgps->file_name);
+               } else {
+                   (void) strcpy(pgps->list->file, line + offset);
+               }
+           } else {
+               pgps->result = 1;
+               msg_format("malformed dimensions for tile set, %s, at line"
+                          " %d of %s", pgps->list->menuname, pgps->line_no,
+                          pgps->file_name);
+           }
        }
-
-       /* Hardcode the no graphics option */
-       graphics_modes[count].pNext = NULL;
-       graphics_modes[count].grafID = GRAPHICS_NONE;
-       graphics_modes[count].alphablend = 0;
-       graphics_modes[count].overdrawRow = 0;
-       graphics_modes[count].overdrawMax = 0;
-       strncpy(graphics_modes[count].pref, "none", 8);
-       strncpy(graphics_modes[count].path, "", 32);
-       strncpy(graphics_modes[count].file, "", 32);
-       strncpy(graphics_modes[count].menuname, "None", 32);
-
-       graphics_mode_high_id = max;
-
-       /* Set the default graphics mode to be no graphics */
-       current_graphics_mode = &(graphics_modes[count]);
-
-       if (p) {
-               mode = parser_priv(p);
-               while (mode) {
-                       n = mode->pNext;
-                       mem_free(mode);
-                       mode = n;
+       break;
+
+    case GFPARSE_HAVE_PREF:
+       if (strlen(line + offset) >= sizeof(pgps->list->pref)) {
+           pgps->result = 1;
+           msg_format("preference file name is too long for tile set, %s, "
+                      "at line %d of %s", pgps->list->menuname, pgps->line_no,
+                      pgps->file_name);
+       } else if (line[offset] == '\0') {
+           pgps->result = 1;
+           msg_format("empty preference file name for tile set, %s, "
+                      "at line %d of %s", pgps->list->menuname, pgps->line_no,
+                      pgps->file_name);
+       } else {
+           strcpy(pgps->list->pref, line + offset);
+       }
+       break;
+
+    case GFPARSE_HAVE_EXTRA:
+       {
+           unsigned int alpha, startdbl, enddbl;
+           int nscan;
+
+           if (sscanf(line + offset, "%u:%u:%u%n",
+                      &alpha, &startdbl, &enddbl, &nscan) == 3 &&
+               (line[offset + nscan] == '\0' ||
+                strchr(whitespc, line[offset + nscan]) != 0 ||
+                line[offset + nscan] == '#')) {
+               if (startdbl > 255 || enddbl > 255) {
+                   pgps->result = 1;
+                   msg_format("overdrawMax or overdrawRow is greater than"
+                              " 255 for tile set, %s, at line %d of %s",
+                              pgps->list->menuname, pgps->line_no,
+                              pgps->file_name);
+               } else if (enddbl < startdbl) {
+                   pgps->result = 1;
+                   msg_format("overdrawMax less than overdrawRow for tile"
+                              "set, %s, at line %d of %s",
+                              pgps->list->menuname, pgps->line_no,
+                              pgps->file_name);
+               } else {
+                   pgps->list->alphablend = (alpha != 0);
+                   pgps->list->overdrawRow = startdbl;
+                   pgps->list->overdrawMax = enddbl;
                }
-       
-               parser_setpriv(p, NULL);
-               parser_destroy(p);
+           } else {
+               pgps->result = 1;
+               msg_format("malformed data for tile set, %s, at line %d of"
+                          " %s", pgps->list->menuname, pgps->line_no,
+                          pgps->file_name);
+           }
        }
-       return PARSE_ERROR_NONE;
-}
+       break;
+
+    case GFPARSE_HAVE_GRAF:
+       if (strlen(line + offset) >= sizeof(pgps->list->graf)) {
+           pgps->result = 1;
+           msg_format("graf string is too long for tile set, %s, at line %d"
+                      " of %s", pgps->list->menuname, pgps->line_no,
+                      pgps->file_name);
+       } else if (line[offset] == '\0') {
+           pgps->result = 1;
+           msg_format("empty graf string for tile set, %s, at line %d of %s",
+                      pgps->list->menuname, pgps->line_no, pgps->file_name);
+       } else {
+           strcpy(pgps->list->graf, line + offset);
+       }
+       break;
+    }
 
-static void print_error(const char *name, struct parser *p) {
-       struct parser_state s;
-       parser_getstate(p, &s);
-       msg("Parse error in %s line %d column %d: %s: %s", name,
-                  s.line, s.col, s.msg, parser_error_str[s.error]);
-       event_signal(EVENT_MESSAGE_FLUSH);
+    if (pgps->result == 0) {
+       pgps->stage |= stage;
+    }
 }
 
-bool init_graphics_modes(void) {
-       char buf[1024];
-
-       ang_file *f;
-       struct parser *p;
-       errr e = 0;
 
-       int line_no = 0;
+static void finish_parse_grafmode(GrafModeParserState* pgps,
+                                 int transfer_results) {
+    /*
+     * Check what was read for the last mode parsed, since parse_line did
+     * not.
+     */
+    if (transfer_results) {
+       if (pgps->list == 0 || pgps->stage == GFPARSE_HAVE_NOTHING) {
+           msg_format("no graphics modes in %s", pgps->file_name);
+       } else {
+           if (check_last_mode(pgps) != 0) {
+               transfer_results = 0;
+               pgps->result = 1;
+           }
+       }
+    }
 
-       /* Build the filename */
-       path_build(buf, sizeof(buf), ANGBAND_DIR_TILES, "list.txt");
+    if (transfer_results) {
+       graphics_mode *mode = pgps->list;
+       int max = GRAPHICS_NONE;
+       int count = 0;
+       graphics_mode *new_list;
+
+       while (mode) {
+           if (mode->grafID > max) {
+               max = mode->grafID;
+           }
+           ++count;
+           mode = mode->pNext;
+       }
 
-       f = file_open(buf, MODE_READ, FTYPE_TEXT);
-       if (!f) {
-               msg("Cannot open '%s'.", buf);
-               finish_parse_grafmode(NULL);
+       /* Assemble the modes into a contiguous block of memory. */
+       new_list = (graphics_mode *)
+           malloc(sizeof(graphics_mode) * (count + 1));
+       if (new_list != 0) {
+           int i;
+
+           mode = pgps->list;
+           for (i = count - 1; i >= 0; --i, mode = mode->pNext) {
+               memcpy(&(new_list[i]), mode, sizeof(graphics_mode));
+               new_list[i].pNext = &(new_list[i + 1]);
+           }
+
+           /* Hardcode the no graphics option. */
+           new_list[count].pNext = NULL;
+           new_list[count].grafID = GRAPHICS_NONE;
+           new_list[count].alphablend = 0;
+           new_list[count].overdrawRow = 0;
+           new_list[count].overdrawMax = 0;
+           strncpy(
+               new_list[count].pref, "none", sizeof(new_list[count].pref));
+           strncpy(
+               new_list[count].path, "", sizeof(new_list[count].path));
+           strncpy(
+               new_list[count].file, "", sizeof(new_list[count].file));
+           strncpy(
+               new_list[count].menuname,
+               "Classic ASCII",
+               sizeof(new_list[count].menuname)
+           );
+           strncpy(
+               new_list[count].graf, "ascii", sizeof(new_list[count].graf));
+
+           /* Release the old global state. */
+           close_graphics_modes();
+
+           graphics_modes = new_list;
+           graphics_mode_high_id = max;
+           /* Set the default graphics mode to be no graphics */
+           current_graphics_mode = &(graphics_modes[count]);
        } else {
-               char line[1024];
+           pgps->result = 1;
+           msg_print("failed memory allocation for new graphics modes");
+       }
+    }
 
-               p = init_parse_grafmode();
-               while (file_getl(f, line, sizeof line)) {
-                       line_no++;
+    /* Release the memory allocated for parsing the file. */
+    while (pgps->list != 0) {
+       graphics_mode *mode = pgps->list;
+
+       pgps->list = mode->pNext;
+       free(mode);
+    }
+}
 
-                       e = parser_parse(p, line);
-                       if (e != PARSE_ERROR_NONE) {
-                               print_error(buf, p);
-                               break;
-                       }
-               }
 
-               finish_parse_grafmode(p);
-               file_close(f);
+bool init_graphics_modes(void) {
+    char buf[1024];
+    char line[1024];
+    GrafModeParserState gps = { 0, buf, 0, 0, GFPARSE_HAVE_NOTHING, 0 };
+    FILE *f;
+
+    /* Build the filename */
+    path_build(line, sizeof(line), ANGBAND_DIR_XTRA, "graf");
+    gps.dir_len = strlen(line);
+    path_build(buf, sizeof(buf), line, "list.txt");
+
+    f = my_fopen(buf, "r");
+    if (!f) {
+       msg_format("Cannot open '%s'.", buf);
+       gps.result = 1;
+    } else {
+       while (my_fgets(f, line, sizeof line) == 0) {
+           ++gps.line_no;
+           parse_line(&gps, line);
+           if (gps.result != 0) {
+               break;
+           }
        }
 
-       /* Result */
-       return e == PARSE_ERROR_NONE;
+       finish_parse_grafmode(&gps, gps.result == 0);
+       my_fclose(f);
+    }
+
+    /* Result */
+    return gps.result == 0;
 }
 
+
 void close_graphics_modes(void) {
-       if (graphics_modes) {
-               mem_free(graphics_modes);
-               graphics_modes = NULL;
-       }
+    if (graphics_modes) {
+       free(graphics_modes);
+       graphics_modes = NULL;
+    }
+    current_graphics_mode = NULL;
 }
 
+
 graphics_mode *get_graphics_mode(byte id) {
-       graphics_mode *test = graphics_modes;
-       while (test) {
-               if (test->grafID == id) {
-                       return test;
-               }
-               test = test->pNext;
+    graphics_mode *test = graphics_modes;
+    while (test) {
+       if (test->grafID == id) {
+           return test;
        }
-       return NULL;
+       test = test->pNext;
+    }
+    return NULL;
 }