1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-98 Artsoft Entertainment *
8 * phone: ++49 +521 290471 *
9 * email: aeglos@valinor.owl.de *
10 *----------------------------------------------------------*
12 ***********************************************************/
24 #define MAX_FILENAME_LEN 256 /* maximal filename length */
25 #define MAX_LINE_LEN 1000 /* maximal input line length */
26 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
27 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
28 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
29 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
30 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
31 #define FILE_VERSION_1_0 10 /* 1.0 file version (old) */
32 #define FILE_VERSION_1_2 12 /* 1.2 file version (still in use) */
33 #define FILE_VERSION_1_4 14 /* 1.4 file version (new) */
35 /* file identifier strings */
36 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
37 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
38 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
39 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
40 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
41 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
42 /* old file identifiers for backward compatibility */
43 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
44 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
45 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
47 /* file names and filename extensions */
49 #define USERDATA_DIRECTORY ".rocksndiamonds"
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 USERDATA_DIRECTORY "userdata"
59 #define LEVELSETUP_DIRECTORY "lvlsetup"
60 #define SETUP_FILENAME "setup.cnf"
61 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
62 #define LEVELINFO_FILENAME "lvlinfo.cnf"
63 #define LEVELFILE_EXTENSION "lvl"
64 #define TAPEFILE_EXTENSION "tap"
65 #define SCOREFILE_EXTENSION "sco"
66 #define ERROR_FILENAME "error.out"
69 /* file permissions for newly written files */
70 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
71 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
72 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
73 #define USERDATA_DIR_MODE (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
74 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
75 #define SCORE_PERMS LEVEL_PERMS
76 #define TAPE_PERMS LEVEL_PERMS
77 #define SETUP_PERMS LEVEL_PERMS
79 /* sort priorities of level series (also used as level series classes) */
80 #define LEVELCLASS_TUTORIAL_START 10
81 #define LEVELCLASS_TUTORIAL_END 99
82 #define LEVELCLASS_CLASSICS_START 100
83 #define LEVELCLASS_CLASSICS_END 199
84 #define LEVELCLASS_CONTRIBUTION_START 200
85 #define LEVELCLASS_CONTRIBUTION_END 299
86 #define LEVELCLASS_USER_START 300
87 #define LEVELCLASS_USER_END 399
88 #define LEVELCLASS_BD_START 400
89 #define LEVELCLASS_BD_END 499
90 #define LEVELCLASS_EM_START 500
91 #define LEVELCLASS_EM_END 599
92 #define LEVELCLASS_SP_START 600
93 #define LEVELCLASS_SP_END 699
94 #define LEVELCLASS_DX_START 700
95 #define LEVELCLASS_DX_END 799
97 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
98 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
99 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
100 #define LEVELCLASS_USER LEVELCLASS_USER_START
101 #define LEVELCLASS_BD LEVELCLASS_BD_START
102 #define LEVELCLASS_EM LEVELCLASS_EM_START
103 #define LEVELCLASS_SP LEVELCLASS_SP_START
104 #define LEVELCLASS_DX LEVELCLASS_DX_START
106 #define LEVELCLASS_UNDEFINED 999
108 #define NUM_LEVELCLASS_DESC 8
109 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
121 #define IS_LEVELCLASS_TUTORIAL(p) \
122 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
123 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
124 #define IS_LEVELCLASS_CLASSICS(p) \
125 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
126 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
127 #define IS_LEVELCLASS_CONTRIBUTION(p) \
128 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
129 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
130 #define IS_LEVELCLASS_USER(p) \
131 ((p)->sort_priority >= LEVELCLASS_USER_START && \
132 (p)->sort_priority <= LEVELCLASS_USER_END)
133 #define IS_LEVELCLASS_BD(p) \
134 ((p)->sort_priority >= LEVELCLASS_BD_START && \
135 (p)->sort_priority <= LEVELCLASS_BD_END)
136 #define IS_LEVELCLASS_EM(p) \
137 ((p)->sort_priority >= LEVELCLASS_EM_START && \
138 (p)->sort_priority <= LEVELCLASS_EM_END)
139 #define IS_LEVELCLASS_SP(p) \
140 ((p)->sort_priority >= LEVELCLASS_SP_START && \
141 (p)->sort_priority <= LEVELCLASS_SP_END)
142 #define IS_LEVELCLASS_DX(p) \
143 ((p)->sort_priority >= LEVELCLASS_DX_START && \
144 (p)->sort_priority <= LEVELCLASS_DX_END)
146 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
147 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
148 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
149 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
150 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
151 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
152 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
153 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
154 LEVELCLASS_UNDEFINED)
156 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
157 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
158 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
159 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
160 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
161 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
162 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
163 IS_LEVELCLASS_USER(n) ? FC_RED : \
166 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
167 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
168 IS_LEVELCLASS_BD(n) ? 2 : \
169 IS_LEVELCLASS_EM(n) ? 3 : \
170 IS_LEVELCLASS_SP(n) ? 4 : \
171 IS_LEVELCLASS_DX(n) ? 5 : \
172 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
173 IS_LEVELCLASS_USER(n) ? 7 : \
176 char *getLevelClassDescription(struct LevelDirInfo *ldi)
178 int position = ldi->sort_priority / 100;
180 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
181 return levelclass_desc[position];
183 return "Unknown Level Class";
186 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
187 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
189 char *getUserDataDir()
191 static char *userdata_dir = NULL;
195 char *home_dir = getHomeDir();
196 char *data_dir = USERDATA_DIRECTORY;
198 userdata_dir = getPath2(home_dir, data_dir);
204 static char *getSetupDir()
206 return getUserDataDir();
209 static char *getUserLevelDir(char *level_subdir)
211 static char *userlevel_dir = NULL;
212 char *data_dir = getUserDataDir();
213 char *userlevel_subdir = LEVELS_DIRECTORY;
218 if (strlen(level_subdir) > 0)
219 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
221 userlevel_dir = getPath2(data_dir, userlevel_subdir);
223 return userlevel_dir;
226 static char *getTapeDir(char *level_subdir)
228 static char *tape_dir = NULL;
229 char *data_dir = getUserDataDir();
230 char *tape_subdir = TAPES_DIRECTORY;
235 if (strlen(level_subdir) > 0)
236 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
238 tape_dir = getPath2(data_dir, tape_subdir);
243 static char *getScoreDir(char *level_subdir)
245 static char *score_dir = NULL;
246 char *data_dir = options.rw_base_directory;
247 char *score_subdir = SCORES_DIRECTORY;
252 if (strlen(level_subdir) > 0)
253 score_dir = getPath3(data_dir, score_subdir, level_subdir);
255 score_dir = getPath2(data_dir, score_subdir);
260 static char *getLevelSetupDir(char *level_subdir)
262 static char *levelsetup_dir = NULL;
263 char *data_dir = getUserDataDir();
264 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
267 free(levelsetup_dir);
269 if (strlen(level_subdir) > 0)
270 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
272 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
274 return levelsetup_dir;
277 static char *getLevelFilename(int nr)
279 static char *filename = NULL;
280 char basename[MAX_FILENAME_LEN];
282 if (filename != NULL)
285 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
286 filename = getPath3((leveldir_current->user_defined ?
287 getUserLevelDir("") :
288 options.level_directory),
289 leveldir_current->fullpath,
295 static char *getTapeFilename(int nr)
297 static char *filename = NULL;
298 char basename[MAX_FILENAME_LEN];
300 if (filename != NULL)
303 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
304 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
309 static char *getScoreFilename(int nr)
311 static char *filename = NULL;
312 char basename[MAX_FILENAME_LEN];
314 if (filename != NULL)
317 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
318 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
323 static void createDirectory(char *dir, char *text)
325 if (access(dir, F_OK) != 0)
326 if (mkdir(dir, USERDATA_DIR_MODE) != 0)
327 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
330 static void InitUserDataDirectory()
332 createDirectory(getUserDataDir(), "user data");
335 static void InitTapeDirectory(char *level_subdir)
337 createDirectory(getUserDataDir(), "user data");
338 createDirectory(getTapeDir(""), "main tape");
339 createDirectory(getTapeDir(level_subdir), "level tape");
342 static void InitScoreDirectory(char *level_subdir)
344 createDirectory(getScoreDir(""), "main score");
345 createDirectory(getScoreDir(level_subdir), "level score");
348 static void InitUserLevelDirectory(char *level_subdir)
350 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
352 createDirectory(getUserDataDir(), "user data");
353 createDirectory(getUserLevelDir(""), "main user level");
354 createDirectory(getUserLevelDir(level_subdir), "user level");
360 static void InitLevelSetupDirectory(char *level_subdir)
362 createDirectory(getUserDataDir(), "user data");
363 createDirectory(getLevelSetupDir(""), "main level setup");
364 createDirectory(getLevelSetupDir(level_subdir), "level setup");
367 static void setLevelInfoToDefaults()
371 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
372 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
374 for(x=0; x<MAX_LEV_FIELDX; x++)
375 for(y=0; y<MAX_LEV_FIELDY; y++)
376 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
379 level.gems_needed = 0;
380 level.amoeba_speed = 10;
381 level.time_magic_wall = 10;
382 level.time_wheel = 10;
383 level.time_light = 10;
384 level.time_timegate = 10;
385 level.amoeba_content = EL_DIAMANT;
386 level.double_speed = FALSE;
387 level.gravity = FALSE;
389 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
390 level.name[i] = '\0';
391 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
392 level.author[i] = '\0';
394 strcpy(level.name, NAMELESS_LEVEL_NAME);
395 strcpy(level.author, ANONYMOUS_NAME);
397 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
400 level.num_yam_contents = STD_ELEMENT_CONTENTS;
401 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
404 level.yam_content[i][x][y] = EL_FELSBROCKEN;
406 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
407 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
408 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
410 BorderElement = EL_BETON;
412 /* try to determine better author name than 'anonymous' */
413 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
415 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
416 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
420 switch (LEVELCLASS(leveldir_current))
422 case LEVELCLASS_TUTORIAL:
423 strcpy(level.author, PROGRAM_AUTHOR_STRING);
426 case LEVELCLASS_CONTRIBUTION:
427 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
428 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
431 case LEVELCLASS_USER:
432 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
433 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
437 /* keep default value */
443 static int checkLevelElement(int element)
445 if (element >= EL_FIRST_RUNTIME_EL)
447 Error(ERR_WARN, "invalid level element %d", element);
448 element = EL_CHAR_FRAGE;
454 void LoadLevel(int level_nr)
457 char *filename = getLevelFilename(level_nr);
458 char cookie[MAX_LINE_LEN];
459 char chunk[CHUNK_ID_LEN + 1];
460 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
461 int file_version = FILE_VERSION_1_4; /* last version of level files */
465 /* always start with reliable default values */
466 setLevelInfoToDefaults();
468 if (!(file = fopen(filename, "r")))
470 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
474 /* check file identifier */
475 fgets(cookie, MAX_LINE_LEN, file);
476 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
477 cookie[strlen(cookie) - 1] = '\0';
479 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
480 file_version = FILE_VERSION_1_0;
481 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
482 file_version = FILE_VERSION_1_2;
483 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
485 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
490 /* read chunk "HEAD" */
491 if (file_version >= FILE_VERSION_1_2)
493 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
494 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
496 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
502 lev_fieldx = level.fieldx = fgetc(file);
503 lev_fieldy = level.fieldy = fgetc(file);
505 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
506 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
508 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
509 level.name[i] = fgetc(file);
510 level.name[MAX_LEVEL_NAME_LEN] = 0;
512 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
513 level.score[i] = fgetc(file);
515 level.num_yam_contents = STD_ELEMENT_CONTENTS;
516 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
522 if (i < STD_ELEMENT_CONTENTS)
523 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
525 level.yam_content[i][x][y] = EL_LEERRAUM;
530 level.amoeba_speed = fgetc(file);
531 level.time_magic_wall = fgetc(file);
532 level.time_wheel = fgetc(file);
533 level.amoeba_content = checkLevelElement(fgetc(file));
534 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
535 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
537 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
539 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
542 if (file_version >= FILE_VERSION_1_2)
544 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
546 /* look for optional author chunk */
547 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
549 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
550 level.author[i] = fgetc(file);
551 level.author[MAX_LEVEL_NAME_LEN] = 0;
553 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
556 /* look for optional content chunk */
557 if (strcmp(chunk, "CONT") == 0 &&
558 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
561 level.num_yam_contents = fgetc(file);
565 if (level.num_yam_contents < 1 ||
566 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
569 printf("WARNING: num_yam_contents == %d (corrected)\n",
570 level.num_yam_contents);
572 level.num_yam_contents = STD_ELEMENT_CONTENTS;
575 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
578 level.yam_content[i][x][y] =
579 checkLevelElement(encoding_16bit ?
580 getFile16BitInteger(file,
581 BYTE_ORDER_BIG_ENDIAN) :
584 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
587 /* next check body chunk identifier and chunk length */
588 if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
590 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
596 /* clear all other level fields (needed if resized in level editor later) */
597 for(x=0; x<MAX_LEV_FIELDX; x++)
598 for(y=0; y<MAX_LEV_FIELDY; y++)
599 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
601 /* now read in the valid level fields from level file */
602 for(y=0; y<lev_fieldy; y++)
603 for(x=0; x<lev_fieldx; x++)
604 Feld[x][y] = Ur[x][y] =
605 checkLevelElement(encoding_16bit ?
606 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
611 /* player was faster than monsters in pre-1.0 levels */
612 if (file_version == FILE_VERSION_1_0 &&
613 IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
615 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
616 Error(ERR_WARN, "using high speed movement for player");
617 level.double_speed = TRUE;
620 /* determine border element for this level */
624 void SaveLevel(int level_nr)
627 char *filename = getLevelFilename(level_nr);
628 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
629 char *oldest_possible_cookie;
632 if (!(file = fopen(filename, "w")))
634 Error(ERR_WARN, "cannot save level file '%s'", filename);
638 /* check yam content for 16-bit elements */
639 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
642 if (level.yam_content[i][x][y] > 255)
643 encoding_16bit = TRUE;
645 /* check level field for 16-bit elements */
646 for(y=0; y<lev_fieldy; y++)
647 for(x=0; x<lev_fieldx; x++)
649 encoding_16bit = TRUE;
651 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
653 fputs(oldest_possible_cookie, file); /* file identifier */
656 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
658 fputc(level.fieldx, file);
659 fputc(level.fieldy, file);
661 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
662 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
664 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
665 fputc(level.name[i], file);
666 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
667 fputc(level.score[i], file);
668 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
671 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
672 fputc(level.amoeba_speed, file);
673 fputc(level.time_magic_wall, file);
674 fputc(level.time_wheel, file);
675 fputc(level.amoeba_content, file);
676 fputc((level.double_speed ? 1 : 0), file);
677 fputc((level.gravity ? 1 : 0), file);
679 fputc((encoding_16bit ? 1 : 0), file);
681 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
684 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
686 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
687 fputc(level.author[i], file);
689 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
690 BYTE_ORDER_BIG_ENDIAN);
692 fputc(EL_MAMPFER, file);
693 fputc(level.num_yam_contents, file);
697 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
701 putFile16BitInteger(file, level.yam_content[i][x][y],
702 BYTE_ORDER_BIG_ENDIAN);
704 fputc(level.yam_content[i][x][y], file);
706 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
708 for(y=0; y<lev_fieldy; y++)
709 for(x=0; x<lev_fieldx; x++)
711 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
713 fputc(Ur[x][y], file);
717 chmod(filename, LEVEL_PERMS);
720 void LoadTape(int level_nr)
723 char *filename = getTapeFilename(level_nr);
724 char cookie[MAX_LINE_LEN];
725 char chunk[CHUNK_ID_LEN + 1];
727 int num_participating_players;
728 int file_version = FILE_VERSION_1_2; /* last version of tape files */
731 /* always start with reliable default values (empty tape) */
734 /* default values (also for pre-1.2 tapes) with only the first player */
735 tape.player_participates[0] = TRUE;
736 for(i=1; i<MAX_PLAYERS; i++)
737 tape.player_participates[i] = FALSE;
739 /* at least one (default: the first) player participates in every tape */
740 num_participating_players = 1;
742 if (!(file = fopen(filename, "r")))
745 /* check file identifier */
746 fgets(cookie, MAX_LINE_LEN, file);
747 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
748 cookie[strlen(cookie) - 1] = '\0';
750 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
751 file_version = FILE_VERSION_1_0;
752 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
754 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
759 /* read chunk "HEAD" */
760 if (file_version >= FILE_VERSION_1_2)
762 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
763 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
765 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
771 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
772 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
773 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
775 /* read header fields that are new since version 1.2 */
776 if (file_version >= FILE_VERSION_1_2)
778 byte store_participating_players = fgetc(file);
780 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
783 /* since version 1.2, tapes store which players participate in the tape */
784 num_participating_players = 0;
785 for(i=0; i<MAX_PLAYERS; i++)
787 tape.player_participates[i] = FALSE;
789 if (store_participating_players & (1 << i))
791 tape.player_participates[i] = TRUE;
792 num_participating_players++;
797 tape.level_nr = level_nr;
799 tape.changed = FALSE;
801 tape.recording = FALSE;
802 tape.playing = FALSE;
803 tape.pausing = FALSE;
805 /* read chunk "BODY" */
806 if (file_version >= FILE_VERSION_1_2)
808 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
809 if (strcmp(chunk, "BODY") ||
810 chunk_length != (num_participating_players + 1) * tape.length)
812 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
818 for(i=0; i<tape.length; i++)
820 if (i >= MAX_TAPELEN)
823 for(j=0; j<MAX_PLAYERS; j++)
825 tape.pos[i].action[j] = MV_NO_MOVING;
827 if (tape.player_participates[j])
828 tape.pos[i].action[j] = fgetc(file);
831 tape.pos[i].delay = fgetc(file);
833 if (file_version == FILE_VERSION_1_0)
835 /* eliminate possible diagonal moves in old tapes */
836 /* this is only for backward compatibility */
838 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
839 byte action = tape.pos[i].action[0];
840 int k, num_moves = 0;
844 if (action & joy_dir[k])
846 tape.pos[i + num_moves].action[0] = joy_dir[k];
848 tape.pos[i + num_moves].delay = 0;
857 tape.length += num_moves;
867 if (i != tape.length)
868 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
870 tape.length_seconds = GetTapeLength();
873 void SaveTape(int level_nr)
876 char *filename = getTapeFilename(level_nr);
878 boolean new_tape = TRUE;
879 byte store_participating_players;
880 int num_participating_players;
882 InitTapeDirectory(leveldir_current->filename);
884 /* if a tape still exists, ask to overwrite it */
885 if (access(filename, F_OK) == 0)
888 if (!Request("Replace old tape ?", REQ_ASK))
892 /* count number of players and set corresponding bits for compact storage */
893 store_participating_players = 0;
894 num_participating_players = 0;
895 for(i=0; i<MAX_PLAYERS; i++)
897 if (tape.player_participates[i])
899 num_participating_players++;
900 store_participating_players |= (1 << i);
904 if (!(file = fopen(filename, "w")))
906 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
910 fputs(TAPE_COOKIE, file); /* file identifier */
913 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
915 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
916 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
917 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
919 fputc(store_participating_players, file);
921 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
924 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
925 BYTE_ORDER_BIG_ENDIAN);
927 for(i=0; i<tape.length; i++)
931 for(j=0; j<MAX_PLAYERS; j++)
932 if (tape.player_participates[j])
933 fputc(tape.pos[i].action[j], file);
935 fputc(tape.pos[i].delay, file);
940 chmod(filename, TAPE_PERMS);
942 tape.changed = FALSE;
945 Request("tape saved !", REQ_CONFIRM);
948 void LoadScore(int level_nr)
951 char *filename = getScoreFilename(level_nr);
952 char cookie[MAX_LINE_LEN];
953 char line[MAX_LINE_LEN];
957 /* always start with reliable default values */
958 for(i=0; i<MAX_SCORE_ENTRIES; i++)
960 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
961 highscore[i].Score = 0;
964 if (!(file = fopen(filename, "r")))
967 /* check file identifier */
968 fgets(cookie, MAX_LINE_LEN, file);
969 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
970 cookie[strlen(cookie) - 1] = '\0';
972 if (strcmp(cookie, SCORE_COOKIE) != 0)
974 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
979 for(i=0; i<MAX_SCORE_ENTRIES; i++)
981 fscanf(file, "%d", &highscore[i].Score);
982 fgets(line, MAX_LINE_LEN, file);
984 if (line[strlen(line) - 1] == '\n')
985 line[strlen(line) - 1] = '\0';
987 for (line_ptr = line; *line_ptr; line_ptr++)
989 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
991 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
992 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1001 void SaveScore(int level_nr)
1004 char *filename = getScoreFilename(level_nr);
1007 InitScoreDirectory(leveldir_current->filename);
1009 if (!(file = fopen(filename, "w")))
1011 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1015 fprintf(file, "%s\n\n", SCORE_COOKIE);
1017 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1018 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1022 chmod(filename, SCORE_PERMS);
1025 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1026 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1027 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1028 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1029 #define TOKEN_STR_PLAYER_PREFIX "player_"
1031 #define TOKEN_VALUE_POSITION 30
1034 #define SETUP_TOKEN_PLAYER_NAME 0
1035 #define SETUP_TOKEN_SOUND 1
1036 #define SETUP_TOKEN_SOUND_LOOPS 2
1037 #define SETUP_TOKEN_SOUND_MUSIC 3
1038 #define SETUP_TOKEN_SOUND_SIMPLE 4
1041 #define SETUP_TOKEN_TOONS 5
1042 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1045 #define SETUP_TOKEN_SCROLL_DELAY 5
1046 #define SETUP_TOKEN_SOFT_SCROLLING 6
1047 #define SETUP_TOKEN_FADING 7
1048 #define SETUP_TOKEN_AUTORECORD 8
1049 #define SETUP_TOKEN_QUICK_DOORS 9
1050 #define SETUP_TOKEN_TEAM_MODE 10
1051 #define SETUP_TOKEN_HANDICAP 11
1052 #define SETUP_TOKEN_TIME_LIMIT 12
1055 #define SETUP_TOKEN_USE_JOYSTICK 13
1056 #define SETUP_TOKEN_JOY_DEVICE_NAME 14
1057 #define SETUP_TOKEN_JOY_XLEFT 15
1058 #define SETUP_TOKEN_JOY_XMIDDLE 16
1059 #define SETUP_TOKEN_JOY_XRIGHT 17
1060 #define SETUP_TOKEN_JOY_YUPPER 18
1061 #define SETUP_TOKEN_JOY_YMIDDLE 19
1062 #define SETUP_TOKEN_JOY_YLOWER 20
1063 #define SETUP_TOKEN_JOY_SNAP 21
1064 #define SETUP_TOKEN_JOY_BOMB 22
1065 #define SETUP_TOKEN_KEY_LEFT 23
1066 #define SETUP_TOKEN_KEY_RIGHT 24
1067 #define SETUP_TOKEN_KEY_UP 25
1068 #define SETUP_TOKEN_KEY_DOWN 26
1069 #define SETUP_TOKEN_KEY_SNAP 27
1070 #define SETUP_TOKEN_KEY_BOMB 28
1072 /* level directory info */
1073 #define LEVELINFO_TOKEN_NAME 29
1074 #define LEVELINFO_TOKEN_NAME_SHORT 30
1075 #define LEVELINFO_TOKEN_NAME_SORTING 31
1076 #define LEVELINFO_TOKEN_AUTHOR 32
1077 #define LEVELINFO_TOKEN_IMPORTED_FROM 33
1078 #define LEVELINFO_TOKEN_LEVELS 34
1079 #define LEVELINFO_TOKEN_FIRST_LEVEL 35
1080 #define LEVELINFO_TOKEN_SORT_PRIORITY 36
1081 #define LEVELINFO_TOKEN_LEVEL_GROUP 37
1082 #define LEVELINFO_TOKEN_READONLY 38
1084 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1085 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_TIME_LIMIT
1087 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1088 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1090 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1091 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1093 #define TYPE_BOOLEAN 1
1094 #define TYPE_SWITCH 2
1096 #define TYPE_INTEGER 4
1097 #define TYPE_STRING 5
1099 static struct SetupInfo si;
1100 static struct SetupInputInfo sii;
1101 static struct LevelDirInfo ldi;
1110 { TYPE_STRING, &si.player_name, "player_name" },
1111 { TYPE_SWITCH, &si.sound, "sound" },
1112 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1113 { TYPE_SWITCH, &si.sound_music, "background_music" },
1114 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1117 { TYPE_SWITCH, &si.toons, "toons" },
1118 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1121 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1122 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1123 { TYPE_SWITCH, &si.fading, "screen_fading" },
1124 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1125 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1126 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1127 { TYPE_SWITCH, &si.handicap, "handicap" },
1128 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1131 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1132 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1133 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1134 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1135 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1136 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1137 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1138 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1139 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1140 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1141 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1142 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1143 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1144 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1145 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1146 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1148 /* level directory info */
1149 { TYPE_STRING, &ldi.name, "name" },
1150 { TYPE_STRING, &ldi.name_short, "name_short" },
1151 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1152 { TYPE_STRING, &ldi.author, "author" },
1153 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1154 { TYPE_INTEGER, &ldi.levels, "levels" },
1155 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1156 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1157 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1158 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1161 static char *string_tolower(char *s)
1163 static char s_lower[100];
1166 if (strlen(s) >= 100)
1171 for (i=0; i<strlen(s_lower); i++)
1172 s_lower[i] = tolower(s_lower[i]);
1177 static int get_string_integer_value(char *s)
1179 static char *number_text[][3] =
1181 { "0", "zero", "null", },
1182 { "1", "one", "first" },
1183 { "2", "two", "second" },
1184 { "3", "three", "third" },
1185 { "4", "four", "fourth" },
1186 { "5", "five", "fifth" },
1187 { "6", "six", "sixth" },
1188 { "7", "seven", "seventh" },
1189 { "8", "eight", "eighth" },
1190 { "9", "nine", "ninth" },
1191 { "10", "ten", "tenth" },
1192 { "11", "eleven", "eleventh" },
1193 { "12", "twelve", "twelfth" },
1198 for (i=0; i<13; i++)
1200 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1206 static boolean get_string_boolean_value(char *s)
1208 if (strcmp(string_tolower(s), "true") == 0 ||
1209 strcmp(string_tolower(s), "yes") == 0 ||
1210 strcmp(string_tolower(s), "on") == 0 ||
1211 get_string_integer_value(s) == 1)
1217 static char *getFormattedSetupEntry(char *token, char *value)
1220 static char entry[MAX_LINE_LEN];
1222 sprintf(entry, "%s:", token);
1223 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1227 strcat(entry, value);
1232 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1234 if (!setup_file_list)
1237 if (setup_file_list->token)
1238 free(setup_file_list->token);
1239 if (setup_file_list->value)
1240 free(setup_file_list->value);
1241 if (setup_file_list->next)
1242 freeSetupFileList(setup_file_list->next);
1243 free(setup_file_list);
1246 static struct SetupFileList *newSetupFileList(char *token, char *value)
1248 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1250 new->token = checked_malloc(strlen(token) + 1);
1251 strcpy(new->token, token);
1253 new->value = checked_malloc(strlen(value) + 1);
1254 strcpy(new->value, value);
1261 static char *getTokenValue(struct SetupFileList *setup_file_list,
1264 if (!setup_file_list)
1267 if (strcmp(setup_file_list->token, token) == 0)
1268 return setup_file_list->value;
1270 return getTokenValue(setup_file_list->next, token);
1273 static void setTokenValue(struct SetupFileList *setup_file_list,
1274 char *token, char *value)
1276 if (!setup_file_list)
1279 if (strcmp(setup_file_list->token, token) == 0)
1281 free(setup_file_list->value);
1282 setup_file_list->value = checked_malloc(strlen(value) + 1);
1283 strcpy(setup_file_list->value, value);
1285 else if (setup_file_list->next == NULL)
1286 setup_file_list->next = newSetupFileList(token, value);
1288 setTokenValue(setup_file_list->next, token, value);
1292 static void printSetupFileList(struct SetupFileList *setup_file_list)
1294 if (!setup_file_list)
1297 printf("token: '%s'\n", setup_file_list->token);
1298 printf("value: '%s'\n", setup_file_list->value);
1300 printSetupFileList(setup_file_list->next);
1304 static struct SetupFileList *loadSetupFileList(char *filename)
1307 char line[MAX_LINE_LEN];
1308 char *token, *value, *line_ptr;
1309 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1310 struct SetupFileList *first_valid_list_entry;
1314 if (!(file = fopen(filename, "r")))
1316 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1322 /* read next line of input file */
1323 if (!fgets(line, MAX_LINE_LEN, file))
1326 /* cut trailing comment or whitespace from input line */
1327 for (line_ptr = line; *line_ptr; line_ptr++)
1329 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1336 /* cut trailing whitespaces from input line */
1337 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1338 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1341 /* ignore empty lines */
1345 line_len = strlen(line);
1347 /* cut leading whitespaces from token */
1348 for (token = line; *token; token++)
1349 if (*token != ' ' && *token != '\t')
1352 /* find end of token */
1353 for (line_ptr = token; *line_ptr; line_ptr++)
1355 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1362 if (line_ptr < line + line_len)
1363 value = line_ptr + 1;
1367 /* cut leading whitespaces from value */
1368 for (; *value; value++)
1369 if (*value != ' ' && *value != '\t')
1372 if (*token && *value)
1373 setTokenValue(setup_file_list, token, value);
1378 first_valid_list_entry = setup_file_list->next;
1380 /* free empty list header */
1381 setup_file_list->next = NULL;
1382 freeSetupFileList(setup_file_list);
1384 if (first_valid_list_entry == NULL)
1385 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1387 return first_valid_list_entry;
1390 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1393 if (!setup_file_list)
1396 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1398 if (strcmp(setup_file_list->value, identifier) != 0)
1400 Error(ERR_WARN, "configuration file has wrong version");
1407 if (setup_file_list->next)
1408 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1411 Error(ERR_WARN, "configuration file has no version information");
1416 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1418 ldi->filename = NULL;
1419 ldi->fullpath = NULL;
1420 ldi->basepath = NULL;
1421 ldi->name = getStringCopy(ANONYMOUS_NAME);
1422 ldi->name_short = NULL;
1423 ldi->name_sorting = NULL;
1424 ldi->author = getStringCopy(ANONYMOUS_NAME);
1425 ldi->imported_from = NULL;
1427 ldi->first_level = 0;
1428 ldi->last_level = 0;
1429 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1430 ldi->level_group = FALSE;
1431 ldi->parent_link = FALSE;
1432 ldi->user_defined = FALSE;
1433 ldi->readonly = TRUE;
1435 ldi->class_desc = NULL;
1436 ldi->handicap_level = 0;
1438 ldi->cl_cursor = -1;
1440 ldi->node_parent = NULL;
1441 ldi->node_group = NULL;
1445 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1446 struct LevelDirInfo *parent)
1450 setLevelDirInfoToDefaults(ldi);
1454 /* first copy all values from the parent structure ... */
1457 /* ... then set all fields to default that cannot be inherited from parent.
1458 This is especially important for all those fields that can be set from
1459 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1460 calls 'free()' for all already set token values which requires that no
1461 other structure's pointer may point to them!
1464 ldi->filename = NULL;
1465 ldi->fullpath = NULL;
1466 ldi->basepath = NULL;
1467 ldi->name = getStringCopy(ANONYMOUS_NAME);
1468 ldi->name_short = NULL;
1469 ldi->name_sorting = NULL;
1470 ldi->author = getStringCopy(parent->author);
1471 ldi->imported_from = getStringCopy(parent->imported_from);
1473 ldi->level_group = FALSE;
1474 ldi->parent_link = FALSE;
1476 ldi->node_parent = parent;
1477 ldi->node_group = NULL;
1481 static void setSetupInfoToDefaults(struct SetupInfo *si)
1485 si->player_name = getStringCopy(getLoginName());
1488 si->sound_loops = TRUE;
1489 si->sound_music = TRUE;
1490 si->sound_simple = TRUE;
1492 si->double_buffering = TRUE;
1493 si->direct_draw = !si->double_buffering;
1494 si->scroll_delay = TRUE;
1495 si->soft_scrolling = TRUE;
1497 si->autorecord = TRUE;
1498 si->quick_doors = FALSE;
1499 si->team_mode = FALSE;
1500 si->handicap = TRUE;
1501 si->time_limit = TRUE;
1503 for (i=0; i<MAX_PLAYERS; i++)
1505 si->input[i].use_joystick = FALSE;
1506 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1507 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1508 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1509 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1510 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1511 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1512 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1513 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1514 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1515 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KEY_UNDEFINED);
1516 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINED);
1517 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KEY_UNDEFINED);
1518 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KEY_UNDEFINED);
1519 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KEY_UNDEFINED);
1520 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KEY_UNDEFINED);
1524 static void setSetupInfo(int token_nr, char *token_value)
1526 int token_type = token_info[token_nr].type;
1527 void *setup_value = token_info[token_nr].value;
1529 if (token_value == NULL)
1532 /* set setup field to corresponding token value */
1537 *(boolean *)setup_value = get_string_boolean_value(token_value);
1541 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1545 *(int *)setup_value = get_string_integer_value(token_value);
1549 if (*(char **)setup_value != NULL)
1550 free(*(char **)setup_value);
1551 *(char **)setup_value = getStringCopy(token_value);
1559 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1563 if (!setup_file_list)
1566 /* handle global setup values */
1568 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1569 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1572 /* handle player specific setup values */
1573 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1577 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1579 sii = setup.input[pnr];
1580 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1582 char full_token[100];
1584 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1585 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1587 setup.input[pnr] = sii;
1591 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1593 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1594 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1597 if (entry1->parent_link || entry2->parent_link)
1598 compare_result = (entry1->parent_link ? -1 : +1);
1599 else if (entry1->sort_priority == entry2->sort_priority)
1601 char *name1 = getStringToLower(entry1->name_sorting);
1602 char *name2 = getStringToLower(entry2->name_sorting);
1604 compare_result = strcmp(name1, name2);
1609 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1610 compare_result = entry1->sort_priority - entry2->sort_priority;
1612 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1614 return compare_result;
1617 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1619 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1621 setLevelDirInfoToDefaults(leveldir_new);
1623 leveldir_new->node_parent = node_parent;
1624 leveldir_new->parent_link = TRUE;
1626 leveldir_new->name = ".. (parent directory)";
1627 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1628 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1630 leveldir_new->filename = "..";
1631 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1633 leveldir_new->sort_priority = node_parent->sort_priority;
1634 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1636 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1639 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1640 struct LevelDirInfo *node_parent,
1641 char *level_directory)
1644 struct dirent *dir_entry;
1645 boolean valid_entry_found = FALSE;
1647 if ((dir = opendir(level_directory)) == NULL)
1649 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1653 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1655 struct SetupFileList *setup_file_list = NULL;
1656 struct stat file_status;
1657 char *directory_name = dir_entry->d_name;
1658 char *directory_path = getPath2(level_directory, directory_name);
1659 char *filename = NULL;
1661 /* skip entries for current and parent directory */
1662 if (strcmp(directory_name, ".") == 0 ||
1663 strcmp(directory_name, "..") == 0)
1665 free(directory_path);
1669 /* find out if directory entry is itself a directory */
1670 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1671 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1673 free(directory_path);
1677 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1678 setup_file_list = loadSetupFileList(filename);
1680 if (setup_file_list)
1682 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1685 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1686 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1688 /* set all structure fields according to the token/value pairs */
1689 ldi = *leveldir_new;
1690 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1691 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1692 *leveldir_new = ldi;
1694 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1696 if (leveldir_new->name_short == NULL)
1697 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1699 if (leveldir_new->name_sorting == NULL)
1700 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1702 leveldir_new->filename = getStringCopy(directory_name);
1704 if (node_parent == NULL) /* top level group */
1706 leveldir_new->basepath = level_directory;
1707 leveldir_new->fullpath = leveldir_new->filename;
1709 else /* sub level group */
1711 leveldir_new->basepath = node_parent->basepath;
1712 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1716 if (leveldir_new->levels < 1)
1717 leveldir_new->levels = 1;
1719 leveldir_new->last_level =
1720 leveldir_new->first_level + leveldir_new->levels - 1;
1722 leveldir_new->user_defined =
1723 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1725 leveldir_new->color = LEVELCOLOR(leveldir_new);
1726 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1728 leveldir_new->handicap_level = /* set handicap to default value */
1729 (leveldir_new->user_defined ?
1730 leveldir_new->last_level :
1731 leveldir_new->first_level);
1733 pushLevelDirInfo(node_first, leveldir_new);
1735 freeSetupFileList(setup_file_list);
1736 valid_entry_found = TRUE;
1738 if (leveldir_new->level_group)
1740 /* create node to link back to current level directory */
1741 createParentLevelDirNode(leveldir_new);
1743 /* step into sub-directory and look for more level series */
1744 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1745 leveldir_new, directory_path);
1749 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1751 free(directory_path);
1757 if (!valid_entry_found)
1758 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1762 void LoadLevelInfo()
1764 InitUserLevelDirectory(getLoginName());
1766 DrawInitText("Loading level series:", 120, FC_GREEN);
1768 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1769 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1771 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1773 if (leveldir_first == NULL)
1774 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1776 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1779 dumpLevelDirInfo(leveldir_first, 0);
1783 static void SaveUserLevelInfo()
1789 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1791 if (!(file = fopen(filename, "w")))
1793 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1798 /* always start with reliable default values */
1799 setLevelDirInfoToDefaults(&ldi);
1801 ldi.name = getLoginName();
1802 ldi.author = getRealName();
1804 ldi.first_level = 1;
1805 ldi.sort_priority = LEVELCLASS_USER_START;
1806 ldi.readonly = FALSE;
1808 fprintf(file, "%s\n\n",
1809 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1811 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1812 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1813 i != LEVELINFO_TOKEN_NAME_SORTING &&
1814 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1815 fprintf(file, "%s\n", getSetupLine("", i));
1820 chmod(filename, SETUP_PERMS);
1826 struct SetupFileList *setup_file_list = NULL;
1828 /* always start with reliable default values */
1829 setSetupInfoToDefaults(&setup);
1831 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1833 setup_file_list = loadSetupFileList(filename);
1835 if (setup_file_list)
1837 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1838 decodeSetupFileList(setup_file_list);
1840 setup.direct_draw = !setup.double_buffering;
1842 freeSetupFileList(setup_file_list);
1844 /* needed to work around problems with fixed length strings */
1845 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1846 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1847 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1849 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1851 strcpy(new_name, setup.player_name);
1852 free(setup.player_name);
1853 setup.player_name = new_name;
1857 Error(ERR_WARN, "using default setup values");
1862 static char *getSetupLine(char *prefix, int token_nr)
1865 static char entry[MAX_LINE_LEN];
1866 int token_type = token_info[token_nr].type;
1867 void *setup_value = token_info[token_nr].value;
1868 char *token_text = token_info[token_nr].text;
1870 /* start with the prefix, token and some spaces to format output line */
1871 sprintf(entry, "%s%s:", prefix, token_text);
1872 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1875 /* continue with the token's value (which can have different types) */
1879 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1883 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1888 Key key = *(Key *)setup_value;
1889 char *keyname = getKeyNameFromKey(key);
1891 strcat(entry, getX11KeyNameFromKey(key));
1892 for (i=strlen(entry); i<50; i++)
1895 /* add comment, if useful */
1896 if (strcmp(keyname, "(undefined)") != 0 &&
1897 strcmp(keyname, "(unknown)") != 0)
1899 strcat(entry, "# ");
1900 strcat(entry, keyname);
1907 char buffer[MAX_LINE_LEN];
1909 sprintf(buffer, "%d", *(int *)setup_value);
1910 strcat(entry, buffer);
1915 strcat(entry, *(char **)setup_value);
1931 InitUserDataDirectory();
1933 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1935 if (!(file = fopen(filename, "w")))
1937 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1942 fprintf(file, "%s\n",
1943 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1944 fprintf(file, "\n");
1946 /* handle global setup values */
1948 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1950 fprintf(file, "%s\n", getSetupLine("", i));
1952 /* just to make things nicer :) */
1953 if (i == SETUP_TOKEN_PLAYER_NAME)
1954 fprintf(file, "\n");
1957 /* handle player specific setup values */
1958 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1962 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1963 fprintf(file, "\n");
1965 sii = setup.input[pnr];
1966 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1967 fprintf(file, "%s\n", getSetupLine(prefix, i));
1973 chmod(filename, SETUP_PERMS);
1976 void LoadLevelSetup_LastSeries()
1979 struct SetupFileList *level_setup_list = NULL;
1981 /* always start with reliable default values */
1982 leveldir_current = leveldir_first;
1984 /* ----------------------------------------------------------------------- */
1985 /* ~/.rocksndiamonds/levelsetup.conf */
1986 /* ----------------------------------------------------------------------- */
1988 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1990 if ((level_setup_list = loadSetupFileList(filename)))
1992 char *last_level_series =
1993 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1995 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
1996 if (leveldir_current == NULL)
1997 leveldir_current = leveldir_first;
1999 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2001 freeSetupFileList(level_setup_list);
2004 Error(ERR_WARN, "using default setup values");
2009 void SaveLevelSetup_LastSeries()
2012 char *level_subdir = leveldir_current->filename;
2015 /* ----------------------------------------------------------------------- */
2016 /* ~/.rocksndiamonds/levelsetup.conf */
2017 /* ----------------------------------------------------------------------- */
2019 InitUserDataDirectory();
2021 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2023 if (!(file = fopen(filename, "w")))
2025 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2030 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2031 LEVELSETUP_COOKIE));
2032 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2038 chmod(filename, SETUP_PERMS);
2041 static void checkSeriesInfo()
2043 static char *level_directory = NULL;
2045 struct dirent *dir_entry;
2047 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2049 level_directory = getPath2((leveldir_current->user_defined ?
2050 getUserLevelDir("") :
2051 options.level_directory),
2052 leveldir_current->filename);
2054 if ((dir = opendir(level_directory)) == NULL)
2056 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2060 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2062 if (strlen(dir_entry->d_name) > 4 &&
2063 dir_entry->d_name[3] == '.' &&
2064 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2066 char levelnum_str[4];
2069 strncpy(levelnum_str, dir_entry->d_name, 3);
2070 levelnum_str[3] = '\0';
2072 levelnum_value = atoi(levelnum_str);
2074 if (levelnum_value < leveldir_current->first_level)
2076 Error(ERR_WARN, "additional level %d found", levelnum_value);
2077 leveldir_current->first_level = levelnum_value;
2079 else if (levelnum_value > leveldir_current->last_level)
2081 Error(ERR_WARN, "additional level %d found", levelnum_value);
2082 leveldir_current->last_level = levelnum_value;
2090 void LoadLevelSetup_SeriesInfo()
2093 struct SetupFileList *level_setup_list = NULL;
2094 char *level_subdir = leveldir_current->filename;
2096 /* always start with reliable default values */
2097 level_nr = leveldir_current->first_level;
2099 checkSeriesInfo(leveldir_current);
2101 /* ----------------------------------------------------------------------- */
2102 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2103 /* ----------------------------------------------------------------------- */
2105 level_subdir = leveldir_current->filename;
2107 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2109 if ((level_setup_list = loadSetupFileList(filename)))
2113 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2117 level_nr = atoi(token_value);
2119 if (level_nr < leveldir_current->first_level)
2120 level_nr = leveldir_current->first_level;
2121 if (level_nr > leveldir_current->last_level)
2122 level_nr = leveldir_current->last_level;
2125 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2129 int level_nr = atoi(token_value);
2131 if (level_nr < leveldir_current->first_level)
2132 level_nr = leveldir_current->first_level;
2133 if (level_nr > leveldir_current->last_level + 1)
2134 level_nr = leveldir_current->last_level;
2136 if (leveldir_current->user_defined)
2137 level_nr = leveldir_current->last_level;
2139 leveldir_current->handicap_level = level_nr;
2142 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2144 freeSetupFileList(level_setup_list);
2147 Error(ERR_WARN, "using default setup values");
2152 void SaveLevelSetup_SeriesInfo()
2155 char *level_subdir = leveldir_current->filename;
2156 char *level_nr_str = int2str(level_nr, 0);
2157 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2160 /* ----------------------------------------------------------------------- */
2161 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2162 /* ----------------------------------------------------------------------- */
2164 InitLevelSetupDirectory(level_subdir);
2166 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2168 if (!(file = fopen(filename, "w")))
2170 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2175 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2176 LEVELSETUP_COOKIE));
2177 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2179 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2180 handicap_level_str));
2185 chmod(filename, SETUP_PERMS);
2189 void initErrorFile()
2193 InitUserDataDirectory();
2195 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2200 FILE *openErrorFile()
2205 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2206 error_file = fopen(filename, "a");
2212 void dumpErrorFile()
2217 filename = getPath2(getUserDataDir(), ERROR_FILENAME);
2218 error_file = fopen(filename, "r");
2221 if (error_file != NULL)
2223 while (!feof(error_file))
2224 fputc(fgetc(error_file), stderr);