1 /*
2  * Copyright (c) 2008,2009 Bertrand Janin <tamentis@neopulsar.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 
31 #include "SDL.h"
32 
33 #include "rezerwar.h"
34 
35 /**
36  * Level constructor
37  */
38 Level *
lvl_new()39 lvl_new()
40 {
41 	Level *level;
42 
43 	level = malloc(sizeof(Level));
44 	level->name = NULL;
45 	level->description = NULL;
46 	level->next = NULL;
47 	level->queue = NULL;
48 	level->queue_len = 0;
49 	level->allow_bomb = true;
50 	level->allow_medic = true;
51 	level->rising_speed = -1;
52 	level->time_limit = -1;
53 	level->max_moles = 0;
54 	level->max_cubes = 0;
55 	level->dead_pipes = 0;
56 	level->cmap = malloc(sizeof(byte) * BOARD_WIDTH * BOARD_HEIGHT);
57 
58 	return level;
59 }
60 
61 /**
62  * Level destructor
63  */
64 void
lvl_kill(Level * level)65 lvl_kill(Level *level)
66 {
67 	r_free(level->name);
68 	free(level->description);
69 	r_free(level);
70 }
71 
72 
73 /*************************
74 * LEVEL PSEUDO VARIABLES *
75 *************************/
76 
77 /* $ObjectiveType */
78 void
lvl_var_objtype(Level * level,char * value)79 lvl_var_objtype(Level *level, char *value)
80 {
81 	if (strcmp(value, "CLEARALL") == 0) {
82 		level->objective_type = OBJTYPE_CLEARALL;
83 	} else if (strcmp(value, "LINK") == 0) {
84 		level->objective_type = OBJTYPE_LINK;
85 	} else {
86 		fatal("Unknown value for ObjectiveType");
87 	}
88 }
89 
90 /* $NextLevel */
91 void
lvl_var_nextlevel(Level * level,char * value)92 lvl_var_nextlevel(Level *level, char *value)
93 {
94 	size_t len = strlen(value) + 1;
95 
96 	level->next = malloc(len);
97 	strlcpy(level->next, value, len);
98 }
99 
100 /* $AllowDynamite */
101 void
lvl_var_allowdynamite(Level * level,const char * value)102 lvl_var_allowdynamite(Level *level, const char *value)
103 {
104 	level->allow_bomb = lvl_bool(value);
105 }
106 
107 /* $AllowMedic */
108 void
lvl_var_allowmedic(Level * level,const char * value)109 lvl_var_allowmedic(Level *level, const char *value)
110 {
111 	level->allow_medic = lvl_bool(value);
112 }
113 
114 /* $DeadPipes */
115 void
lvl_var_deadpipes(Level * level,char * value)116 lvl_var_deadpipes(Level *level, char *value)
117 {
118 	level->dead_pipes = atoi(value);
119 }
120 
121 /* $MaxCubesAllowed */
122 void
lvl_var_maxcubesallowed(Level * level,char * value)123 lvl_var_maxcubesallowed(Level *level, char *value)
124 {
125 	level->max_cubes = atoi(value);
126 }
127 
128 /* $MaxMoles */
129 void
lvl_var_maxmoles(Level * level,char * value)130 lvl_var_maxmoles(Level *level, char *value)
131 {
132 	level->max_moles = atoi(value);
133 }
134 
135 /* $TimeLimit */
136 void
lvl_var_timelimit(Level * level,char * value)137 lvl_var_timelimit(Level *level, char *value)
138 {
139 	level->time_limit = atoi(value);
140 }
141 
142 /* $RisingSpeed */
143 void
lvl_var_risingspeed(Level * level,char * value)144 lvl_var_risingspeed(Level *level, char *value)
145 {
146 	level->rising_speed = atoi(value);
147 }
148 
149 
150 /**
151  * Copy the next line of *buf in *lbuf, assuming it has enough space and
152  * return the number of char we moved including the new line.
153  */
154 size_t
lvl_getline(byte * lbuf,byte * buf)155 lvl_getline(byte *lbuf, byte *buf)
156 {
157 	size_t len = 0;
158 
159 	if (buf[len] == '\0')
160 		return -1;
161 
162 	while (buf[len] != '\n' && buf[len] != '\0') {
163 		lbuf[len] = buf[len];
164 		len++;
165 		if (len > 80)
166 			fatal("Syntax error: line too long! (MAX=80)");
167 	}
168 	lbuf[len] = '\0';
169 
170 	return len + 1;
171 }
172 
173 /**
174  * Get a variable line buffer, split it and assign the proper variable.
175  */
176 void
lvl_splitvar(Level * level,byte * lbuf,size_t len)177 lvl_splitvar(Level *level, byte *lbuf, size_t len)
178 {
179 	char *c, *l = (char *)lbuf;
180 
181 	/* Find the first colon (non optional) and terminal the string to
182 	 * find the variable name, increment c to skip the colon/NUL */
183 	c = strchr(l, ':');
184 	if (c == NULL) fatal("Syntax error: variable without ':'.");
185 	*c = '\0';
186 
187 	/* Skip the NUL/:, the spaces... */
188 	c++; while (*c == ' ') c++;
189 
190 	if (strcmp("ObjectiveType", l) == 0)
191 		lvl_var_objtype(level, c);
192 	else if (strcmp("NextLevel", l) == 0)
193 		lvl_var_nextlevel(level, c);
194 	else if (strcmp("AllowDynamite", l) == 0)
195 		lvl_var_allowdynamite(level, c);
196 	else if (strcmp("AllowMedic", l) == 0)
197 		lvl_var_allowmedic(level, c);
198 	else if (strcmp("MaxCubesAllowed", l) == 0)
199 		lvl_var_maxcubesallowed(level, c);
200 	else if (strcmp("MaxMoles", l) == 0)
201 		lvl_var_maxmoles(level, c);
202 	else if (strcmp("DeadPipes", l) == 0)
203 		lvl_var_deadpipes(level, c);
204 	else if (strcmp("TimeLimit", l) == 0)
205 		lvl_var_timelimit(level, c);
206 	else if (strcmp("RisingSpeed", l) == 0)
207 		lvl_var_risingspeed(level, c);
208 	else
209 		fatal("Syntax error: unknown variable: \"%s\".", l);
210 }
211 
212 
213 /**
214  * Return a freshly loaded level.
215  */
216 Level *
lvl_load(char * name)217 lvl_load(char *name)
218 {
219 	Level *level;
220 	FILE *fp;
221 	char filename[64];
222 	char *path;
223 	byte buffer[LVL_MAX_SIZE];
224 	byte lbuf[81];
225 	byte *cursor = buffer;
226 	byte *c;
227 	size_t len, offset, lineno = 1;
228 	int phase = 0, i, j;
229 
230 	snprintf(filename, 64, "levels/%s.lvl", name);
231 	path = dpath(filename);
232 
233 	fp = fopen(path, "r");
234 	if (fp == NULL)
235 		fatal("Error opening \"%s\"", name);
236 	r_free(path);
237 
238 	len = fread(buffer, 1, LVL_MAX_SIZE, fp);
239 	if (len >= LVL_MAX_SIZE)
240 		fatal("Level file too big (LVL_MAX_SIZE=%d)", LVL_MAX_SIZE);
241 
242 	fclose(fp);
243 
244 	level = lvl_new();
245 
246 	for (i = 0; cursor < buffer + len;) {
247 		/* offset becomes the total length of the string including the
248 		 * terminating NUL. */
249 		offset = lvl_getline(lbuf, cursor);
250 
251 		if (offset == -1)
252 			break;
253 		lineno++;
254 		cursor += offset;
255 
256 		/* Skip comments */
257 		if (lbuf[0] == '#')
258 			continue;
259 
260 		/* Skip the blank lines before the name */
261 		if (phase == 0 && lbuf[0] == '\0')
262 			continue;
263 
264 		/* Delimitors between the phases */
265 		if (lbuf[0] == '\0') {
266 			phase++;
267 			i = 0;
268 			continue;
269 		}
270 
271 		/* Save the name (phase 0) */
272 		if (level->name == NULL) {
273 			level->name = r_malloc(offset);
274 			strlcpy(level->name, (char *)lbuf, offset);
275 			phase++;
276 			cursor++; // skip next blank line
277 			continue;
278 		}
279 
280 		/* Save the description (phase 1) */
281 		if (phase == 1) {
282 			if (i > 0) level->description[i - 1] = '\n';
283 			level->description = realloc(level->description,
284 					i + offset);
285 			strlcpy(level->description + i, (char*)lbuf, offset);
286 			i += offset;
287 		}
288 
289 		/* Save the map */
290 		if (phase == 2) {
291 			if (offset - 1 > BOARD_WIDTH)
292 				fatal("Syntax error: map line has to be %d wide.", BOARD_WIDTH);
293 			memcpy(level->cmap + i, lbuf, offset - 1);
294 			i += offset - 1;
295 			if (i > BOARD_WIDTH * BOARD_HEIGHT)
296 				fatal("map seems bigger than board.");
297 		}
298 
299 		/* Save the queue */
300 		if (phase == 3) {
301 			if (offset < 4)
302 				fatal("Syntax error on line %d: cube definition erroneous.", lineno);
303 			level->queue = realloc(level->queue,
304 					sizeof(QueuedCube*) * (i + 1));
305 			level->queue[i] = malloc(sizeof(QueuedCube));
306 			level->queue[i]->type = lbuf[0] - 48;
307 			level->queue[i]->pos = lbuf[1] - 48;
308 			level->queue[i]->cmap_len = offset - 3;
309 			level->queue[i]->cmap = malloc(offset - 3);
310 			c = lbuf + 2;
311 			j = 0;
312 			while (*c != '\n' && j < offset - 3) {
313 				level->queue[i]->cmap[j] = c[j];
314 				j++;
315 			}
316 			i++;
317 			level->queue_len++;
318 		}
319 
320 		/* Variables */
321 		if (lbuf[0] == '$') {
322 			lvl_splitvar(level, lbuf + 1, offset - 2);
323 		}
324 	}
325 
326 	return level;
327 }
328 
329 /**
330  * Debugging tool for Level
331  */
332 void
lvl_dump(Level * level)333 lvl_dump(Level *level)
334 {
335 	int i, j;
336 
337 	printf("NAME: %s\n", level->name);
338 	printf("DESCRIPTION:\n%s\n", level->description);
339 	printf("MAP:%s\n", level->cmap);
340 	printf("QUEUE\n");
341 
342 	printf("ALLOW_BOMB: %d\n", level->allow_bomb);
343 	printf("ALLOW_MEDIC: %d\n", level->allow_medic);
344 	printf("MOLES: %d\n", level->max_moles);
345 	printf("DEADPIPES: %d\n", level->dead_pipes);
346 
347 	for (i = 0; i < level->queue_len; i++) {
348 		printf(" - type=%d, pos=%d, cubes(%zu)=",
349 				level->queue[i]->type,
350 				level->queue[i]->pos,
351 				level->queue[i]->cmap_len);
352 		for (j = 0; j < level->queue[i]->cmap_len; j++) {
353 			printf("%02hhx ", level->queue[i]->cmap[j]);
354 		}
355 		printf("\n");
356 	}
357 }
358 
359 bool
lvl_bool(const char * value)360 lvl_bool(const char *value)
361 {
362 	if (strcmp(value, "TRUE") == 0) {
363 		return true;
364 	} else if (strcmp(value, "FALSE") == 0) {
365 		return false;
366 	}
367 
368 	fatal("Unknown value for BOOLEAN: %s", value);
369 	return false;
370 }
371