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