1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2000 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
18 #include "libgame/libgame.h"
25 #define MAX_FILENAME_LEN 256 /* maximal filename length */
26 #define MAX_LINE_LEN 1000 /* maximal input line length */
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
30 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
31 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
32 #define FILE_VERSION_1_0 10 /* 1.0 file version (old) */
33 #define FILE_VERSION_1_2 12 /* 1.2 file version (still in use) */
34 #define FILE_VERSION_1_4 14 /* 1.4 file version (new) */
36 /* file identifier strings */
37 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
38 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
39 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
40 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
41 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
42 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
43 /* old file identifiers for backward compatibility */
44 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
45 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
46 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
48 /* file names and filename extensions */
49 #if !defined(PLATFORM_MSDOS)
50 #define LEVELSETUP_DIRECTORY "levelsetup"
51 #define SETUP_FILENAME "setup.conf"
52 #define LEVELSETUP_FILENAME "levelsetup.conf"
53 #define LEVELINFO_FILENAME "levelinfo.conf"
54 #define LEVELFILE_EXTENSION "level"
55 #define TAPEFILE_EXTENSION "tape"
56 #define SCOREFILE_EXTENSION "score"
58 #define LEVELSETUP_DIRECTORY "lvlsetup"
59 #define SETUP_FILENAME "setup.cnf"
60 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
61 #define LEVELINFO_FILENAME "lvlinfo.cnf"
62 #define LEVELFILE_EXTENSION "lvl"
63 #define TAPEFILE_EXTENSION "tap"
64 #define SCOREFILE_EXTENSION "sco"
67 #if defined(PLATFORM_WIN32)
69 #define S_IRGRP S_IRUSR
72 #define S_IROTH S_IRUSR
75 #define S_IWGRP S_IWUSR
78 #define S_IWOTH S_IWUSR
81 #define S_IXGRP S_IXUSR
84 #define S_IXOTH S_IXUSR
86 #endif /* PLATFORM_WIN32 */
88 /* file permissions for newly written files */
89 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
90 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
91 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
92 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
93 #define SCORE_PERMS LEVEL_PERMS
94 #define TAPE_PERMS LEVEL_PERMS
95 #define SETUP_PERMS LEVEL_PERMS
97 /* sort priorities of level series (also used as level series classes) */
98 #define LEVELCLASS_TUTORIAL_START 10
99 #define LEVELCLASS_TUTORIAL_END 99
100 #define LEVELCLASS_CLASSICS_START 100
101 #define LEVELCLASS_CLASSICS_END 199
102 #define LEVELCLASS_CONTRIBUTION_START 200
103 #define LEVELCLASS_CONTRIBUTION_END 299
104 #define LEVELCLASS_USER_START 300
105 #define LEVELCLASS_USER_END 399
106 #define LEVELCLASS_BD_START 400
107 #define LEVELCLASS_BD_END 499
108 #define LEVELCLASS_EM_START 500
109 #define LEVELCLASS_EM_END 599
110 #define LEVELCLASS_SP_START 600
111 #define LEVELCLASS_SP_END 699
112 #define LEVELCLASS_DX_START 700
113 #define LEVELCLASS_DX_END 799
115 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
116 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
117 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
118 #define LEVELCLASS_USER LEVELCLASS_USER_START
119 #define LEVELCLASS_BD LEVELCLASS_BD_START
120 #define LEVELCLASS_EM LEVELCLASS_EM_START
121 #define LEVELCLASS_SP LEVELCLASS_SP_START
122 #define LEVELCLASS_DX LEVELCLASS_DX_START
124 #define LEVELCLASS_UNDEFINED 999
126 #define NUM_LEVELCLASS_DESC 8
127 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
139 #define IS_LEVELCLASS_TUTORIAL(p) \
140 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
141 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
142 #define IS_LEVELCLASS_CLASSICS(p) \
143 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
144 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
145 #define IS_LEVELCLASS_CONTRIBUTION(p) \
146 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
147 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
148 #define IS_LEVELCLASS_USER(p) \
149 ((p)->sort_priority >= LEVELCLASS_USER_START && \
150 (p)->sort_priority <= LEVELCLASS_USER_END)
151 #define IS_LEVELCLASS_BD(p) \
152 ((p)->sort_priority >= LEVELCLASS_BD_START && \
153 (p)->sort_priority <= LEVELCLASS_BD_END)
154 #define IS_LEVELCLASS_EM(p) \
155 ((p)->sort_priority >= LEVELCLASS_EM_START && \
156 (p)->sort_priority <= LEVELCLASS_EM_END)
157 #define IS_LEVELCLASS_SP(p) \
158 ((p)->sort_priority >= LEVELCLASS_SP_START && \
159 (p)->sort_priority <= LEVELCLASS_SP_END)
160 #define IS_LEVELCLASS_DX(p) \
161 ((p)->sort_priority >= LEVELCLASS_DX_START && \
162 (p)->sort_priority <= LEVELCLASS_DX_END)
164 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
165 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
166 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
167 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
168 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
169 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
170 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
171 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
172 LEVELCLASS_UNDEFINED)
174 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
175 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
176 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
177 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
178 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
179 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
180 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
181 IS_LEVELCLASS_USER(n) ? FC_RED : \
184 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
185 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
186 IS_LEVELCLASS_BD(n) ? 2 : \
187 IS_LEVELCLASS_EM(n) ? 3 : \
188 IS_LEVELCLASS_SP(n) ? 4 : \
189 IS_LEVELCLASS_DX(n) ? 5 : \
190 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
191 IS_LEVELCLASS_USER(n) ? 7 : \
194 char *getLevelClassDescription(struct LevelDirInfo *ldi)
196 int position = ldi->sort_priority / 100;
198 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
199 return levelclass_desc[position];
201 return "Unknown Level Class";
204 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
205 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
207 static char *getSetupDir()
209 return getUserDataDir();
212 static char *getUserLevelDir(char *level_subdir)
214 static char *userlevel_dir = NULL;
215 char *data_dir = getUserDataDir();
216 char *userlevel_subdir = LEVELS_DIRECTORY;
221 if (strlen(level_subdir) > 0)
222 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
224 userlevel_dir = getPath2(data_dir, userlevel_subdir);
226 return userlevel_dir;
229 static char *getTapeDir(char *level_subdir)
231 static char *tape_dir = NULL;
232 char *data_dir = getUserDataDir();
233 char *tape_subdir = TAPES_DIRECTORY;
238 if (strlen(level_subdir) > 0)
239 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
241 tape_dir = getPath2(data_dir, tape_subdir);
246 static char *getScoreDir(char *level_subdir)
248 static char *score_dir = NULL;
249 char *data_dir = options.rw_base_directory;
250 char *score_subdir = SCORES_DIRECTORY;
255 if (strlen(level_subdir) > 0)
256 score_dir = getPath3(data_dir, score_subdir, level_subdir);
258 score_dir = getPath2(data_dir, score_subdir);
263 static char *getLevelSetupDir(char *level_subdir)
265 static char *levelsetup_dir = NULL;
266 char *data_dir = getUserDataDir();
267 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
270 free(levelsetup_dir);
272 if (strlen(level_subdir) > 0)
273 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
275 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
277 return levelsetup_dir;
280 static char *getLevelFilename(int nr)
282 static char *filename = NULL;
283 char basename[MAX_FILENAME_LEN];
285 if (filename != NULL)
288 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
289 filename = getPath3((leveldir_current->user_defined ?
290 getUserLevelDir("") :
291 options.level_directory),
292 leveldir_current->fullpath,
298 static char *getTapeFilename(int nr)
300 static char *filename = NULL;
301 char basename[MAX_FILENAME_LEN];
303 if (filename != NULL)
306 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
307 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
312 static char *getScoreFilename(int nr)
314 static char *filename = NULL;
315 char basename[MAX_FILENAME_LEN];
317 if (filename != NULL)
320 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
321 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
326 static void InitTapeDirectory(char *level_subdir)
328 createDirectory(getUserDataDir(), "user data");
329 createDirectory(getTapeDir(""), "main tape");
330 createDirectory(getTapeDir(level_subdir), "level tape");
333 static void InitScoreDirectory(char *level_subdir)
335 createDirectory(getScoreDir(""), "main score");
336 createDirectory(getScoreDir(level_subdir), "level score");
339 static void InitUserLevelDirectory(char *level_subdir)
341 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
343 createDirectory(getUserDataDir(), "user data");
344 createDirectory(getUserLevelDir(""), "main user level");
345 createDirectory(getUserLevelDir(level_subdir), "user level");
351 static void InitLevelSetupDirectory(char *level_subdir)
353 createDirectory(getUserDataDir(), "user data");
354 createDirectory(getLevelSetupDir(""), "main level setup");
355 createDirectory(getLevelSetupDir(level_subdir), "level setup");
358 static void setLevelInfoToDefaults()
362 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
363 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
365 for(x=0; x<MAX_LEV_FIELDX; x++)
366 for(y=0; y<MAX_LEV_FIELDY; y++)
367 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
370 level.gems_needed = 0;
371 level.amoeba_speed = 10;
372 level.time_magic_wall = 10;
373 level.time_wheel = 10;
374 level.time_light = 10;
375 level.time_timegate = 10;
376 level.amoeba_content = EL_DIAMANT;
377 level.double_speed = FALSE;
378 level.gravity = FALSE;
380 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
381 level.name[i] = '\0';
382 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
383 level.author[i] = '\0';
385 strcpy(level.name, NAMELESS_LEVEL_NAME);
386 strcpy(level.author, ANONYMOUS_NAME);
388 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
391 level.num_yam_contents = STD_ELEMENT_CONTENTS;
392 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
395 level.yam_content[i][x][y] = EL_FELSBROCKEN;
397 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
398 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
399 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
401 BorderElement = EL_BETON;
403 /* try to determine better author name than 'anonymous' */
404 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
406 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
407 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
411 switch (LEVELCLASS(leveldir_current))
413 case LEVELCLASS_TUTORIAL:
414 strcpy(level.author, PROGRAM_AUTHOR_STRING);
417 case LEVELCLASS_CONTRIBUTION:
418 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
419 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
422 case LEVELCLASS_USER:
423 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
424 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
428 /* keep default value */
434 static int checkLevelElement(int element)
436 if (element >= EL_FIRST_RUNTIME_EL)
438 Error(ERR_WARN, "invalid level element %d", element);
439 element = EL_CHAR_FRAGE;
445 void LoadLevel(int level_nr)
448 char *filename = getLevelFilename(level_nr);
449 char cookie[MAX_LINE_LEN];
450 char chunk[CHUNK_ID_LEN + 1];
451 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
452 int file_version = FILE_VERSION_1_4; /* last version of level files */
456 /* always start with reliable default values */
457 setLevelInfoToDefaults();
459 if (!(file = fopen(filename, MODE_READ)))
461 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
465 /* check file identifier */
466 fgets(cookie, MAX_LINE_LEN, file);
467 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
468 cookie[strlen(cookie) - 1] = '\0';
470 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
471 file_version = FILE_VERSION_1_0;
472 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
473 file_version = FILE_VERSION_1_2;
474 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
476 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
481 /* read chunk "HEAD" */
482 if (file_version >= FILE_VERSION_1_2)
484 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
485 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
487 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
493 lev_fieldx = level.fieldx = fgetc(file);
494 lev_fieldy = level.fieldy = fgetc(file);
496 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
497 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
499 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
500 level.name[i] = fgetc(file);
501 level.name[MAX_LEVEL_NAME_LEN] = 0;
503 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
504 level.score[i] = fgetc(file);
506 level.num_yam_contents = STD_ELEMENT_CONTENTS;
507 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
513 if (i < STD_ELEMENT_CONTENTS)
514 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
516 level.yam_content[i][x][y] = EL_LEERRAUM;
521 level.amoeba_speed = fgetc(file);
522 level.time_magic_wall = fgetc(file);
523 level.time_wheel = fgetc(file);
524 level.amoeba_content = checkLevelElement(fgetc(file));
525 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
526 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
528 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
530 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
533 if (file_version >= FILE_VERSION_1_2)
535 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
537 /* look for optional author chunk */
538 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
540 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
541 level.author[i] = fgetc(file);
542 level.author[MAX_LEVEL_NAME_LEN] = 0;
544 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
547 /* look for optional content chunk */
548 if (strcmp(chunk, "CONT") == 0 &&
549 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
552 level.num_yam_contents = fgetc(file);
556 if (level.num_yam_contents < 1 ||
557 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
560 printf("WARNING: num_yam_contents == %d (corrected)\n",
561 level.num_yam_contents);
563 level.num_yam_contents = STD_ELEMENT_CONTENTS;
566 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
569 level.yam_content[i][x][y] =
570 checkLevelElement(encoding_16bit ?
571 getFile16BitInteger(file,
572 BYTE_ORDER_BIG_ENDIAN) :
575 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
578 /* next check body chunk identifier and chunk length */
579 if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
581 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
587 /* clear all other level fields (needed if resized in level editor later) */
588 for(x=0; x<MAX_LEV_FIELDX; x++)
589 for(y=0; y<MAX_LEV_FIELDY; y++)
590 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
592 /* now read in the valid level fields from level file */
593 for(y=0; y<lev_fieldy; y++)
594 for(x=0; x<lev_fieldx; x++)
595 Feld[x][y] = Ur[x][y] =
596 checkLevelElement(encoding_16bit ?
597 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
602 /* player was faster than monsters in pre-1.0 levels */
603 if (file_version == FILE_VERSION_1_0 &&
604 IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
606 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
607 Error(ERR_WARN, "using high speed movement for player");
608 level.double_speed = TRUE;
611 /* determine border element for this level */
615 void SaveLevel(int level_nr)
618 char *filename = getLevelFilename(level_nr);
619 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
620 char *oldest_possible_cookie;
623 if (!(file = fopen(filename, MODE_WRITE)))
625 Error(ERR_WARN, "cannot save level file '%s'", filename);
629 /* check yam content for 16-bit elements */
630 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
633 if (level.yam_content[i][x][y] > 255)
634 encoding_16bit = TRUE;
636 /* check level field for 16-bit elements */
637 for(y=0; y<lev_fieldy; y++)
638 for(x=0; x<lev_fieldx; x++)
640 encoding_16bit = TRUE;
642 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
644 fputs(oldest_possible_cookie, file); /* file identifier */
647 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
649 fputc(level.fieldx, file);
650 fputc(level.fieldy, file);
652 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
653 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
655 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
656 fputc(level.name[i], file);
657 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
658 fputc(level.score[i], file);
659 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
662 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
663 fputc(level.amoeba_speed, file);
664 fputc(level.time_magic_wall, file);
665 fputc(level.time_wheel, file);
666 fputc(level.amoeba_content, file);
667 fputc((level.double_speed ? 1 : 0), file);
668 fputc((level.gravity ? 1 : 0), file);
670 fputc((encoding_16bit ? 1 : 0), file);
672 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
675 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
677 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
678 fputc(level.author[i], file);
680 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
681 BYTE_ORDER_BIG_ENDIAN);
683 fputc(EL_MAMPFER, file);
684 fputc(level.num_yam_contents, file);
688 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
692 putFile16BitInteger(file, level.yam_content[i][x][y],
693 BYTE_ORDER_BIG_ENDIAN);
695 fputc(level.yam_content[i][x][y], file);
697 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
699 for(y=0; y<lev_fieldy; y++)
700 for(x=0; x<lev_fieldx; x++)
702 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
704 fputc(Ur[x][y], file);
708 chmod(filename, LEVEL_PERMS);
711 void LoadTape(int level_nr)
714 char *filename = getTapeFilename(level_nr);
715 char cookie[MAX_LINE_LEN];
716 char chunk[CHUNK_ID_LEN + 1];
718 int num_participating_players;
719 int file_version = FILE_VERSION_1_2; /* last version of tape files */
722 /* always start with reliable default values (empty tape) */
725 /* default values (also for pre-1.2 tapes) with only the first player */
726 tape.player_participates[0] = TRUE;
727 for(i=1; i<MAX_PLAYERS; i++)
728 tape.player_participates[i] = FALSE;
730 /* at least one (default: the first) player participates in every tape */
731 num_participating_players = 1;
733 if (!(file = fopen(filename, MODE_READ)))
736 /* check file identifier */
737 fgets(cookie, MAX_LINE_LEN, file);
738 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
739 cookie[strlen(cookie) - 1] = '\0';
741 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
742 file_version = FILE_VERSION_1_0;
743 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
745 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
750 /* read chunk "HEAD" */
751 if (file_version >= FILE_VERSION_1_2)
753 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
754 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
756 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
762 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
763 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
764 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
766 /* read header fields that are new since version 1.2 */
767 if (file_version >= FILE_VERSION_1_2)
769 byte store_participating_players = fgetc(file);
771 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
774 /* since version 1.2, tapes store which players participate in the tape */
775 num_participating_players = 0;
776 for(i=0; i<MAX_PLAYERS; i++)
778 tape.player_participates[i] = FALSE;
780 if (store_participating_players & (1 << i))
782 tape.player_participates[i] = TRUE;
783 num_participating_players++;
788 tape.level_nr = level_nr;
790 tape.changed = FALSE;
792 tape.recording = FALSE;
793 tape.playing = FALSE;
794 tape.pausing = FALSE;
796 /* read chunk "BODY" */
797 if (file_version >= FILE_VERSION_1_2)
799 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
800 if (strcmp(chunk, "BODY") ||
801 chunk_length != (num_participating_players + 1) * tape.length)
803 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
809 for(i=0; i<tape.length; i++)
811 if (i >= MAX_TAPELEN)
814 for(j=0; j<MAX_PLAYERS; j++)
816 tape.pos[i].action[j] = MV_NO_MOVING;
818 if (tape.player_participates[j])
819 tape.pos[i].action[j] = fgetc(file);
822 tape.pos[i].delay = fgetc(file);
824 if (file_version == FILE_VERSION_1_0)
826 /* eliminate possible diagonal moves in old tapes */
827 /* this is only for backward compatibility */
829 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
830 byte action = tape.pos[i].action[0];
831 int k, num_moves = 0;
835 if (action & joy_dir[k])
837 tape.pos[i + num_moves].action[0] = joy_dir[k];
839 tape.pos[i + num_moves].delay = 0;
848 tape.length += num_moves;
858 if (i != tape.length)
859 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
861 tape.length_seconds = GetTapeLength();
864 void SaveTape(int level_nr)
867 char *filename = getTapeFilename(level_nr);
869 boolean new_tape = TRUE;
870 byte store_participating_players;
871 int num_participating_players;
873 InitTapeDirectory(leveldir_current->filename);
875 /* if a tape still exists, ask to overwrite it */
876 if (access(filename, F_OK) == 0)
879 if (!Request("Replace old tape ?", REQ_ASK))
883 /* count number of players and set corresponding bits for compact storage */
884 store_participating_players = 0;
885 num_participating_players = 0;
886 for(i=0; i<MAX_PLAYERS; i++)
888 if (tape.player_participates[i])
890 num_participating_players++;
891 store_participating_players |= (1 << i);
895 if (!(file = fopen(filename, MODE_WRITE)))
897 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
901 fputs(TAPE_COOKIE, file); /* file identifier */
904 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
906 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
907 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
908 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
910 fputc(store_participating_players, file);
912 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
915 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
916 BYTE_ORDER_BIG_ENDIAN);
918 for(i=0; i<tape.length; i++)
922 for(j=0; j<MAX_PLAYERS; j++)
923 if (tape.player_participates[j])
924 fputc(tape.pos[i].action[j], file);
926 fputc(tape.pos[i].delay, file);
931 chmod(filename, TAPE_PERMS);
933 tape.changed = FALSE;
936 Request("tape saved !", REQ_CONFIRM);
939 void LoadScore(int level_nr)
942 char *filename = getScoreFilename(level_nr);
943 char cookie[MAX_LINE_LEN];
944 char line[MAX_LINE_LEN];
948 /* always start with reliable default values */
949 for(i=0; i<MAX_SCORE_ENTRIES; i++)
951 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
952 highscore[i].Score = 0;
955 if (!(file = fopen(filename, MODE_READ)))
958 /* check file identifier */
959 fgets(cookie, MAX_LINE_LEN, file);
960 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
961 cookie[strlen(cookie) - 1] = '\0';
963 if (strcmp(cookie, SCORE_COOKIE) != 0)
965 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
970 for(i=0; i<MAX_SCORE_ENTRIES; i++)
972 fscanf(file, "%d", &highscore[i].Score);
973 fgets(line, MAX_LINE_LEN, file);
975 if (line[strlen(line) - 1] == '\n')
976 line[strlen(line) - 1] = '\0';
978 for (line_ptr = line; *line_ptr; line_ptr++)
980 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
982 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
983 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
992 void SaveScore(int level_nr)
995 char *filename = getScoreFilename(level_nr);
998 InitScoreDirectory(leveldir_current->filename);
1000 if (!(file = fopen(filename, MODE_WRITE)))
1002 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1006 fprintf(file, "%s\n\n", SCORE_COOKIE);
1008 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1009 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1013 chmod(filename, SCORE_PERMS);
1016 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1017 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1018 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1019 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1020 #define TOKEN_STR_PLAYER_PREFIX "player_"
1022 #define TOKEN_VALUE_POSITION 30
1025 #define SETUP_TOKEN_PLAYER_NAME 0
1026 #define SETUP_TOKEN_SOUND 1
1027 #define SETUP_TOKEN_SOUND_LOOPS 2
1028 #define SETUP_TOKEN_SOUND_MUSIC 3
1029 #define SETUP_TOKEN_SOUND_SIMPLE 4
1032 #define SETUP_TOKEN_TOONS 5
1033 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1036 #define SETUP_TOKEN_SCROLL_DELAY 5
1037 #define SETUP_TOKEN_SOFT_SCROLLING 6
1038 #define SETUP_TOKEN_FADING 7
1039 #define SETUP_TOKEN_AUTORECORD 8
1040 #define SETUP_TOKEN_QUICK_DOORS 9
1041 #define SETUP_TOKEN_TEAM_MODE 10
1042 #define SETUP_TOKEN_HANDICAP 11
1043 #define SETUP_TOKEN_TIME_LIMIT 12
1044 #define SETUP_TOKEN_FULLSCREEN 13
1047 #define SETUP_TOKEN_USE_JOYSTICK 14
1048 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1049 #define SETUP_TOKEN_JOY_XLEFT 16
1050 #define SETUP_TOKEN_JOY_XMIDDLE 17
1051 #define SETUP_TOKEN_JOY_XRIGHT 18
1052 #define SETUP_TOKEN_JOY_YUPPER 19
1053 #define SETUP_TOKEN_JOY_YMIDDLE 20
1054 #define SETUP_TOKEN_JOY_YLOWER 21
1055 #define SETUP_TOKEN_JOY_SNAP 22
1056 #define SETUP_TOKEN_JOY_BOMB 23
1057 #define SETUP_TOKEN_KEY_LEFT 24
1058 #define SETUP_TOKEN_KEY_RIGHT 25
1059 #define SETUP_TOKEN_KEY_UP 26
1060 #define SETUP_TOKEN_KEY_DOWN 27
1061 #define SETUP_TOKEN_KEY_SNAP 28
1062 #define SETUP_TOKEN_KEY_BOMB 29
1064 /* level directory info */
1065 #define LEVELINFO_TOKEN_NAME 30
1066 #define LEVELINFO_TOKEN_NAME_SHORT 31
1067 #define LEVELINFO_TOKEN_NAME_SORTING 32
1068 #define LEVELINFO_TOKEN_AUTHOR 33
1069 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1070 #define LEVELINFO_TOKEN_LEVELS 35
1071 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1072 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1073 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1074 #define LEVELINFO_TOKEN_READONLY 39
1076 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1077 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1079 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1080 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1082 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1083 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1085 #define TYPE_BOOLEAN 1
1086 #define TYPE_SWITCH 2
1088 #define TYPE_INTEGER 4
1089 #define TYPE_STRING 5
1091 static struct SetupInfo si;
1092 static struct SetupInputInfo sii;
1093 static struct LevelDirInfo ldi;
1102 { TYPE_STRING, &si.player_name, "player_name" },
1103 { TYPE_SWITCH, &si.sound, "sound" },
1104 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1105 { TYPE_SWITCH, &si.sound_music, "background_music" },
1106 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1109 { TYPE_SWITCH, &si.toons, "toons" },
1110 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1113 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1114 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1115 { TYPE_SWITCH, &si.fading, "screen_fading" },
1116 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1117 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1118 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1119 { TYPE_SWITCH, &si.handicap, "handicap" },
1120 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1121 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1124 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1125 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1126 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1127 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1128 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1129 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1130 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1131 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1132 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1133 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1134 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1135 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1136 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1137 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1138 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1139 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1141 /* level directory info */
1142 { TYPE_STRING, &ldi.name, "name" },
1143 { TYPE_STRING, &ldi.name_short, "name_short" },
1144 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1145 { TYPE_STRING, &ldi.author, "author" },
1146 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1147 { TYPE_INTEGER, &ldi.levels, "levels" },
1148 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1149 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1150 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1151 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1154 static char *string_tolower(char *s)
1156 static char s_lower[100];
1159 if (strlen(s) >= 100)
1164 for (i=0; i<strlen(s_lower); i++)
1165 s_lower[i] = tolower(s_lower[i]);
1170 static int get_string_integer_value(char *s)
1172 static char *number_text[][3] =
1174 { "0", "zero", "null", },
1175 { "1", "one", "first" },
1176 { "2", "two", "second" },
1177 { "3", "three", "third" },
1178 { "4", "four", "fourth" },
1179 { "5", "five", "fifth" },
1180 { "6", "six", "sixth" },
1181 { "7", "seven", "seventh" },
1182 { "8", "eight", "eighth" },
1183 { "9", "nine", "ninth" },
1184 { "10", "ten", "tenth" },
1185 { "11", "eleven", "eleventh" },
1186 { "12", "twelve", "twelfth" },
1191 for (i=0; i<13; i++)
1193 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1199 static boolean get_string_boolean_value(char *s)
1201 if (strcmp(string_tolower(s), "true") == 0 ||
1202 strcmp(string_tolower(s), "yes") == 0 ||
1203 strcmp(string_tolower(s), "on") == 0 ||
1204 get_string_integer_value(s) == 1)
1210 static char *getFormattedSetupEntry(char *token, char *value)
1213 static char entry[MAX_LINE_LEN];
1215 sprintf(entry, "%s:", token);
1216 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1220 strcat(entry, value);
1225 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1227 if (!setup_file_list)
1230 if (setup_file_list->token)
1231 free(setup_file_list->token);
1232 if (setup_file_list->value)
1233 free(setup_file_list->value);
1234 if (setup_file_list->next)
1235 freeSetupFileList(setup_file_list->next);
1236 free(setup_file_list);
1239 static struct SetupFileList *newSetupFileList(char *token, char *value)
1241 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1243 new->token = checked_malloc(strlen(token) + 1);
1244 strcpy(new->token, token);
1246 new->value = checked_malloc(strlen(value) + 1);
1247 strcpy(new->value, value);
1254 static char *getTokenValue(struct SetupFileList *setup_file_list,
1257 if (!setup_file_list)
1260 if (strcmp(setup_file_list->token, token) == 0)
1261 return setup_file_list->value;
1263 return getTokenValue(setup_file_list->next, token);
1266 static void setTokenValue(struct SetupFileList *setup_file_list,
1267 char *token, char *value)
1269 if (!setup_file_list)
1272 if (strcmp(setup_file_list->token, token) == 0)
1274 free(setup_file_list->value);
1275 setup_file_list->value = checked_malloc(strlen(value) + 1);
1276 strcpy(setup_file_list->value, value);
1278 else if (setup_file_list->next == NULL)
1279 setup_file_list->next = newSetupFileList(token, value);
1281 setTokenValue(setup_file_list->next, token, value);
1285 static void printSetupFileList(struct SetupFileList *setup_file_list)
1287 if (!setup_file_list)
1290 printf("token: '%s'\n", setup_file_list->token);
1291 printf("value: '%s'\n", setup_file_list->value);
1293 printSetupFileList(setup_file_list->next);
1297 static struct SetupFileList *loadSetupFileList(char *filename)
1300 char line[MAX_LINE_LEN];
1301 char *token, *value, *line_ptr;
1302 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1303 struct SetupFileList *first_valid_list_entry;
1307 if (!(file = fopen(filename, MODE_READ)))
1309 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1315 /* read next line of input file */
1316 if (!fgets(line, MAX_LINE_LEN, file))
1319 /* cut trailing comment or whitespace from input line */
1320 for (line_ptr = line; *line_ptr; line_ptr++)
1322 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1329 /* cut trailing whitespaces from input line */
1330 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1331 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1334 /* ignore empty lines */
1338 line_len = strlen(line);
1340 /* cut leading whitespaces from token */
1341 for (token = line; *token; token++)
1342 if (*token != ' ' && *token != '\t')
1345 /* find end of token */
1346 for (line_ptr = token; *line_ptr; line_ptr++)
1348 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1355 if (line_ptr < line + line_len)
1356 value = line_ptr + 1;
1360 /* cut leading whitespaces from value */
1361 for (; *value; value++)
1362 if (*value != ' ' && *value != '\t')
1365 if (*token && *value)
1366 setTokenValue(setup_file_list, token, value);
1371 first_valid_list_entry = setup_file_list->next;
1373 /* free empty list header */
1374 setup_file_list->next = NULL;
1375 freeSetupFileList(setup_file_list);
1377 if (first_valid_list_entry == NULL)
1378 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1380 return first_valid_list_entry;
1383 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1386 if (!setup_file_list)
1389 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1391 if (strcmp(setup_file_list->value, identifier) != 0)
1393 Error(ERR_WARN, "configuration file has wrong version");
1400 if (setup_file_list->next)
1401 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1404 Error(ERR_WARN, "configuration file has no version information");
1409 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1411 ldi->filename = NULL;
1412 ldi->fullpath = NULL;
1413 ldi->basepath = NULL;
1414 ldi->name = getStringCopy(ANONYMOUS_NAME);
1415 ldi->name_short = NULL;
1416 ldi->name_sorting = NULL;
1417 ldi->author = getStringCopy(ANONYMOUS_NAME);
1418 ldi->imported_from = NULL;
1420 ldi->first_level = 0;
1421 ldi->last_level = 0;
1422 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1423 ldi->level_group = FALSE;
1424 ldi->parent_link = FALSE;
1425 ldi->user_defined = FALSE;
1426 ldi->readonly = TRUE;
1428 ldi->class_desc = NULL;
1429 ldi->handicap_level = 0;
1431 ldi->cl_cursor = -1;
1433 ldi->node_parent = NULL;
1434 ldi->node_group = NULL;
1438 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1439 struct LevelDirInfo *parent)
1443 setLevelDirInfoToDefaults(ldi);
1447 /* first copy all values from the parent structure ... */
1450 /* ... then set all fields to default that cannot be inherited from parent.
1451 This is especially important for all those fields that can be set from
1452 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1453 calls 'free()' for all already set token values which requires that no
1454 other structure's pointer may point to them!
1457 ldi->filename = NULL;
1458 ldi->fullpath = NULL;
1459 ldi->basepath = NULL;
1460 ldi->name = getStringCopy(ANONYMOUS_NAME);
1461 ldi->name_short = NULL;
1462 ldi->name_sorting = NULL;
1463 ldi->author = getStringCopy(parent->author);
1464 ldi->imported_from = getStringCopy(parent->imported_from);
1466 ldi->level_group = FALSE;
1467 ldi->parent_link = FALSE;
1469 ldi->node_parent = parent;
1470 ldi->node_group = NULL;
1474 static void setSetupInfoToDefaults(struct SetupInfo *si)
1478 si->player_name = getStringCopy(getLoginName());
1481 si->sound_loops = TRUE;
1482 si->sound_music = TRUE;
1483 si->sound_simple = TRUE;
1485 si->double_buffering = TRUE;
1486 si->direct_draw = !si->double_buffering;
1487 si->scroll_delay = TRUE;
1488 si->soft_scrolling = TRUE;
1490 si->autorecord = TRUE;
1491 si->quick_doors = FALSE;
1492 si->team_mode = FALSE;
1493 si->handicap = TRUE;
1494 si->time_limit = TRUE;
1495 si->fullscreen = FALSE;
1497 for (i=0; i<MAX_PLAYERS; i++)
1499 si->input[i].use_joystick = FALSE;
1500 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1501 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1502 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1503 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1504 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1505 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1506 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1507 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1508 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1509 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1510 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1511 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1512 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1513 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1514 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1518 static void setSetupInfo(int token_nr, char *token_value)
1520 int token_type = token_info[token_nr].type;
1521 void *setup_value = token_info[token_nr].value;
1523 if (token_value == NULL)
1526 /* set setup field to corresponding token value */
1531 *(boolean *)setup_value = get_string_boolean_value(token_value);
1535 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1539 *(int *)setup_value = get_string_integer_value(token_value);
1543 if (*(char **)setup_value != NULL)
1544 free(*(char **)setup_value);
1545 *(char **)setup_value = getStringCopy(token_value);
1553 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1557 if (!setup_file_list)
1560 /* handle global setup values */
1562 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1563 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1566 /* handle player specific setup values */
1567 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1571 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1573 sii = setup.input[pnr];
1574 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1576 char full_token[100];
1578 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1579 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1581 setup.input[pnr] = sii;
1585 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1587 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1588 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1591 if (entry1->parent_link || entry2->parent_link)
1592 compare_result = (entry1->parent_link ? -1 : +1);
1593 else if (entry1->sort_priority == entry2->sort_priority)
1595 char *name1 = getStringToLower(entry1->name_sorting);
1596 char *name2 = getStringToLower(entry2->name_sorting);
1598 compare_result = strcmp(name1, name2);
1603 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1604 compare_result = entry1->sort_priority - entry2->sort_priority;
1606 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1608 return compare_result;
1611 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1613 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1615 setLevelDirInfoToDefaults(leveldir_new);
1617 leveldir_new->node_parent = node_parent;
1618 leveldir_new->parent_link = TRUE;
1620 leveldir_new->name = ".. (parent directory)";
1621 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1622 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1624 leveldir_new->filename = "..";
1625 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1627 leveldir_new->sort_priority = node_parent->sort_priority;
1628 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1630 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1633 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1634 struct LevelDirInfo *node_parent,
1635 char *level_directory)
1638 struct dirent *dir_entry;
1639 boolean valid_entry_found = FALSE;
1641 if ((dir = opendir(level_directory)) == NULL)
1643 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1647 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1649 struct SetupFileList *setup_file_list = NULL;
1650 struct stat file_status;
1651 char *directory_name = dir_entry->d_name;
1652 char *directory_path = getPath2(level_directory, directory_name);
1653 char *filename = NULL;
1655 /* skip entries for current and parent directory */
1656 if (strcmp(directory_name, ".") == 0 ||
1657 strcmp(directory_name, "..") == 0)
1659 free(directory_path);
1663 /* find out if directory entry is itself a directory */
1664 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1665 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1667 free(directory_path);
1671 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1672 setup_file_list = loadSetupFileList(filename);
1674 if (setup_file_list)
1676 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1679 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1680 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1682 /* set all structure fields according to the token/value pairs */
1683 ldi = *leveldir_new;
1684 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1685 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1686 *leveldir_new = ldi;
1688 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1690 if (leveldir_new->name_short == NULL)
1691 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1693 if (leveldir_new->name_sorting == NULL)
1694 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1696 leveldir_new->filename = getStringCopy(directory_name);
1698 if (node_parent == NULL) /* top level group */
1700 leveldir_new->basepath = level_directory;
1701 leveldir_new->fullpath = leveldir_new->filename;
1703 else /* sub level group */
1705 leveldir_new->basepath = node_parent->basepath;
1706 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1710 if (leveldir_new->levels < 1)
1711 leveldir_new->levels = 1;
1713 leveldir_new->last_level =
1714 leveldir_new->first_level + leveldir_new->levels - 1;
1716 leveldir_new->user_defined =
1717 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1719 leveldir_new->color = LEVELCOLOR(leveldir_new);
1720 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1722 leveldir_new->handicap_level = /* set handicap to default value */
1723 (leveldir_new->user_defined ?
1724 leveldir_new->last_level :
1725 leveldir_new->first_level);
1727 pushLevelDirInfo(node_first, leveldir_new);
1729 freeSetupFileList(setup_file_list);
1730 valid_entry_found = TRUE;
1732 if (leveldir_new->level_group)
1734 /* create node to link back to current level directory */
1735 createParentLevelDirNode(leveldir_new);
1737 /* step into sub-directory and look for more level series */
1738 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1739 leveldir_new, directory_path);
1743 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1745 free(directory_path);
1751 if (!valid_entry_found)
1752 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1756 void LoadLevelInfo()
1758 InitUserLevelDirectory(getLoginName());
1760 DrawInitText("Loading level series:", 120, FC_GREEN);
1762 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1763 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1765 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1767 if (leveldir_first == NULL)
1768 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1770 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1773 dumpLevelDirInfo(leveldir_first, 0);
1777 static void SaveUserLevelInfo()
1783 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1785 if (!(file = fopen(filename, MODE_WRITE)))
1787 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1792 /* always start with reliable default values */
1793 setLevelDirInfoToDefaults(&ldi);
1795 ldi.name = getLoginName();
1796 ldi.author = getRealName();
1798 ldi.first_level = 1;
1799 ldi.sort_priority = LEVELCLASS_USER_START;
1800 ldi.readonly = FALSE;
1802 fprintf(file, "%s\n\n",
1803 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1805 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1806 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1807 i != LEVELINFO_TOKEN_NAME_SORTING &&
1808 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1809 fprintf(file, "%s\n", getSetupLine("", i));
1814 chmod(filename, SETUP_PERMS);
1820 struct SetupFileList *setup_file_list = NULL;
1822 /* always start with reliable default values */
1823 setSetupInfoToDefaults(&setup);
1825 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1827 setup_file_list = loadSetupFileList(filename);
1829 if (setup_file_list)
1831 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1832 decodeSetupFileList(setup_file_list);
1834 setup.direct_draw = !setup.double_buffering;
1836 freeSetupFileList(setup_file_list);
1838 /* needed to work around problems with fixed length strings */
1839 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1840 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1841 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1843 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1845 strcpy(new_name, setup.player_name);
1846 free(setup.player_name);
1847 setup.player_name = new_name;
1851 Error(ERR_WARN, "using default setup values");
1856 static char *getSetupLine(char *prefix, int token_nr)
1859 static char entry[MAX_LINE_LEN];
1860 int token_type = token_info[token_nr].type;
1861 void *setup_value = token_info[token_nr].value;
1862 char *token_text = token_info[token_nr].text;
1864 /* start with the prefix, token and some spaces to format output line */
1865 sprintf(entry, "%s%s:", prefix, token_text);
1866 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1869 /* continue with the token's value (which can have different types) */
1873 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1877 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1882 Key key = *(Key *)setup_value;
1883 char *keyname = getKeyNameFromKey(key);
1885 strcat(entry, getX11KeyNameFromKey(key));
1886 for (i=strlen(entry); i<50; i++)
1889 /* add comment, if useful */
1890 if (strcmp(keyname, "(undefined)") != 0 &&
1891 strcmp(keyname, "(unknown)") != 0)
1893 strcat(entry, "# ");
1894 strcat(entry, keyname);
1901 char buffer[MAX_LINE_LEN];
1903 sprintf(buffer, "%d", *(int *)setup_value);
1904 strcat(entry, buffer);
1909 strcat(entry, *(char **)setup_value);
1925 InitUserDataDirectory();
1927 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1929 if (!(file = fopen(filename, MODE_WRITE)))
1931 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1936 fprintf(file, "%s\n",
1937 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1938 fprintf(file, "\n");
1940 /* handle global setup values */
1942 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1944 fprintf(file, "%s\n", getSetupLine("", i));
1946 /* just to make things nicer :) */
1947 if (i == SETUP_TOKEN_PLAYER_NAME)
1948 fprintf(file, "\n");
1951 /* handle player specific setup values */
1952 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1956 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1957 fprintf(file, "\n");
1959 sii = setup.input[pnr];
1960 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1961 fprintf(file, "%s\n", getSetupLine(prefix, i));
1967 chmod(filename, SETUP_PERMS);
1970 void LoadLevelSetup_LastSeries()
1973 struct SetupFileList *level_setup_list = NULL;
1975 /* always start with reliable default values */
1976 leveldir_current = leveldir_first;
1978 /* ----------------------------------------------------------------------- */
1979 /* ~/.rocksndiamonds/levelsetup.conf */
1980 /* ----------------------------------------------------------------------- */
1982 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1984 if ((level_setup_list = loadSetupFileList(filename)))
1986 char *last_level_series =
1987 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1989 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
1990 if (leveldir_current == NULL)
1991 leveldir_current = leveldir_first;
1993 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1995 freeSetupFileList(level_setup_list);
1998 Error(ERR_WARN, "using default setup values");
2003 void SaveLevelSetup_LastSeries()
2006 char *level_subdir = leveldir_current->filename;
2009 /* ----------------------------------------------------------------------- */
2010 /* ~/.rocksndiamonds/levelsetup.conf */
2011 /* ----------------------------------------------------------------------- */
2013 InitUserDataDirectory();
2015 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2017 if (!(file = fopen(filename, MODE_WRITE)))
2019 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2024 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2025 LEVELSETUP_COOKIE));
2026 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2032 chmod(filename, SETUP_PERMS);
2035 static void checkSeriesInfo()
2037 static char *level_directory = NULL;
2039 struct dirent *dir_entry;
2041 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2043 level_directory = getPath2((leveldir_current->user_defined ?
2044 getUserLevelDir("") :
2045 options.level_directory),
2046 leveldir_current->fullpath);
2048 if ((dir = opendir(level_directory)) == NULL)
2050 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2054 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2056 if (strlen(dir_entry->d_name) > 4 &&
2057 dir_entry->d_name[3] == '.' &&
2058 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2060 char levelnum_str[4];
2063 strncpy(levelnum_str, dir_entry->d_name, 3);
2064 levelnum_str[3] = '\0';
2066 levelnum_value = atoi(levelnum_str);
2068 if (levelnum_value < leveldir_current->first_level)
2070 Error(ERR_WARN, "additional level %d found", levelnum_value);
2071 leveldir_current->first_level = levelnum_value;
2073 else if (levelnum_value > leveldir_current->last_level)
2075 Error(ERR_WARN, "additional level %d found", levelnum_value);
2076 leveldir_current->last_level = levelnum_value;
2084 void LoadLevelSetup_SeriesInfo()
2087 struct SetupFileList *level_setup_list = NULL;
2088 char *level_subdir = leveldir_current->filename;
2090 /* always start with reliable default values */
2091 level_nr = leveldir_current->first_level;
2093 checkSeriesInfo(leveldir_current);
2095 /* ----------------------------------------------------------------------- */
2096 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2097 /* ----------------------------------------------------------------------- */
2099 level_subdir = leveldir_current->filename;
2101 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2103 if ((level_setup_list = loadSetupFileList(filename)))
2107 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2111 level_nr = atoi(token_value);
2113 if (level_nr < leveldir_current->first_level)
2114 level_nr = leveldir_current->first_level;
2115 if (level_nr > leveldir_current->last_level)
2116 level_nr = leveldir_current->last_level;
2119 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2123 int level_nr = atoi(token_value);
2125 if (level_nr < leveldir_current->first_level)
2126 level_nr = leveldir_current->first_level;
2127 if (level_nr > leveldir_current->last_level + 1)
2128 level_nr = leveldir_current->last_level;
2130 if (leveldir_current->user_defined)
2131 level_nr = leveldir_current->last_level;
2133 leveldir_current->handicap_level = level_nr;
2136 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2138 freeSetupFileList(level_setup_list);
2141 Error(ERR_WARN, "using default setup values");
2146 void SaveLevelSetup_SeriesInfo()
2149 char *level_subdir = leveldir_current->filename;
2150 char *level_nr_str = int2str(level_nr, 0);
2151 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2154 /* ----------------------------------------------------------------------- */
2155 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2156 /* ----------------------------------------------------------------------- */
2158 InitLevelSetupDirectory(level_subdir);
2160 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2162 if (!(file = fopen(filename, MODE_WRITE)))
2164 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2169 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2170 LEVELSETUP_COOKIE));
2171 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2173 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2174 handicap_level_str));
2179 chmod(filename, SETUP_PERMS);
2181 /* LocalWords: Rocks'n