1 /*
2  * enigma/levelfile.c - provide routines that load a level
3  * configuration file (which points to a set of levels) and a level
4  * file itself.
5  *
6  * Copyright 2000 Simon Tatham. All rights reserved.
7  *
8  * Enigma is licensed under the MIT licence. See the file LICENCE for
9  * details.
10  *
11  * - we are all amf -
12  */
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <ctype.h>
18 
19 #include "enigma.h"
20 
level_load(char * filename)21 static level *level_load(char *filename) {
22     FILE *fp;
23     char buf[FILENAME_MAX+10];
24     char fname[FILENAME_MAX];
25     level *level;
26     int nlines;
27 
28     fname[sizeof(fname)-1] = '\0';
29     strncpy(fname, LEVELDIR, sizeof(fname));
30     strncpy(fname + strlen(fname), filename, sizeof(fname)-strlen(fname));
31     if (fname[sizeof(fname)-1] != '\0') {
32 	fatal("File name length overflow");
33     }
34 
35     fp = fopen(fname, "r");
36     if (!fp) {
37 	fatal("Unable to read level file");
38     }
39 
40     level = level_new();
41     level->title = NULL;
42     level->width = level->height = -1;
43     level->flags = 0;
44     nlines = 0;
45 
46     while (fgets(buf, sizeof(buf), fp)) {
47 	if (buf[strlen(buf)-1] != '\n') {
48 	    fatal("Line length overflow in level file");
49 	}
50 	buf[strcspn(buf, "\r\n")] = '\0';
51 	if (!buf[0])
52 	    continue;
53 	if (ishdr(buf, "Title: ")) {
54 	    if (level->title) {
55 		fatal("Multiple titles in level file");
56 	    }
57 	    level->title = dupstr(buf + 7);
58 	} else if (ishdr(buf, "Width: ")) {
59 	    level->width = atoi(buf + 7);
60 	} else if (ishdr(buf, "Height: ")) {
61 	    level->height = atoi(buf + 8);
62 	} else if (ishdr(buf, "Flags: ")) {
63 	    char *p = buf + 7;
64 	    char *q;
65 
66 	    while (*p) {
67 		while (*p && isspace(*p)) p++;
68 		q = p;
69 		while (*p && !isspace(*p)) p++;
70 		if (*p) *p++ = '\0';
71 		if (!strcmp(q, "flimsy-bombs")) {
72 		    level->flags |= LEVEL_FLIMSY_BOMBS;
73 		} else if (!strcmp(q, "relative-priority")) {
74 		    level->flags |= LEVEL_REL_PRIORITY;
75 		} else {
76 		    fatal("Unknown flag keyword in level file");
77 		}
78 	    }
79 	} else if (ishdr(buf, "Map: ")) {
80 	    if (level->leveldata == NULL) {
81 		fatal("Map before size in level file");
82 	    }
83 	    if ((int)strlen(buf + 5) != level->width) {
84 		fatal("Wrong length map line in level file");
85 	    }
86 	    if (nlines >= level->height) {
87 		fatal("Too many map lines in level file");
88 	    }
89 	    memcpy(level->leveldata + level->width * nlines,
90 		   buf + 5, level->width);
91 	    nlines++;
92 	} else {
93 	    fatal("Unrecognised keyword in level file");
94 	}
95 	if (level->width > 0 && level->height > 0 && level->leveldata == NULL)
96 	    level_setsize(level, level->width, level->height);
97     }
98     if (nlines < level->height) {
99 	fatal("Not enough map lines in level file");
100     }
101 
102     fclose(fp);
103 
104     {
105 	char buf[256], buf2[384];
106 	char *err;
107 
108 	err = validate(level, buf, sizeof(buf));
109 	if (err) {
110 	    sprintf(buf2, "Error in level file '%.64s': %s", filename, err);
111 	    fatal(buf2);
112 	}
113     }
114 
115     return level;
116 }
117 
levelset_load(char * filename)118 levelset *levelset_load(char *filename) {
119     FILE *fp;
120     char buf[FILENAME_MAX+10];
121     char fname[FILENAME_MAX];
122     levelset *set;
123 
124     /*
125      * Sanity-check level set name.
126      */
127     if (filename[strcspn(filename, SETNAME_INVALID)] != '\0') {
128 	fatal("Invalid character in level set name");
129     }
130 
131     fname[sizeof(fname)-1] = '\0';
132     strncpy(fname, LEVELDIR, sizeof(fname));
133     strncpy(fname + strlen(fname), filename, sizeof(fname)-strlen(fname));
134     strncpy(fname + strlen(fname), ".set", sizeof(fname)-strlen(fname));
135     if (fname[sizeof(fname)-1] != '\0') {
136 	fatal("File name length overflow");
137     }
138 
139     fp = fopen(fname, "r");
140     if (!fp) {
141 	fatal("Unable to read level set file");
142     }
143 
144     set = levelset_new();
145     set->title = NULL;
146     set->name = filename;
147 
148     while (fgets(buf, sizeof(buf), fp)) {
149 	if (buf[strlen(buf)-1] != '\n') {
150 	    fatal("Line length overflow in level set file");
151 	}
152 	buf[strcspn(buf, "\r\n")] = '\0';
153 	if (!buf[0])
154 	    continue;
155 	if (ishdr(buf, "Title: ")) {
156 	    if (set->title) {
157 		fatal("Multiple titles in level set file");
158 	    }
159 	    set->title = dupstr(buf + 7);
160 	} else if (ishdr(buf, "Level: ")) {
161 	    levelset_nlevels(set, set->nlevels+1);
162 	    set->levels[set->nlevels-1] = level_load(buf + 7);
163 	} else {
164 	    fatal("Unrecognised keyword in level set file");
165 	}
166     }
167 
168     fclose(fp);
169 
170     return set;
171 }
172