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 ***********************************************************/
25 #define MAX_LINE_LEN 1000
27 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
28 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
30 static char *getGlobalDataDir()
35 static char *getUserDataDir()
37 static char *userdata_dir = NULL;
41 char *home_dir = getHomeDir();
42 char *data_dir = USERDATA_DIRECTORY;
44 userdata_dir = checked_malloc(strlen(home_dir) + strlen(data_dir) + 2);
45 sprintf(userdata_dir, "%s/%s", home_dir, data_dir);
51 static char *getSetupDir()
53 return getUserDataDir();
56 static char *getUserLevelDir(char *level_subdir)
58 static char *userlevel_dir = NULL;
59 char *data_dir = getUserDataDir();
60 char *userlevel_subdir = LEVELS_DIRECTORY;
65 userlevel_dir = checked_malloc(strlen(data_dir) + strlen(userlevel_subdir) +
66 strlen(level_subdir) + 3);
67 sprintf(userlevel_dir, "%s/%s%s%s", data_dir, userlevel_subdir,
68 (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
73 static char *getTapeDir(char *level_subdir)
75 static char *tape_dir = NULL;
76 char *data_dir = getUserDataDir();
77 char *tape_subdir = TAPES_DIRECTORY;
82 tape_dir = checked_malloc(strlen(data_dir) + strlen(tape_subdir) +
83 strlen(level_subdir) + 3);
84 sprintf(tape_dir, "%s/%s%s%s", data_dir, tape_subdir,
85 (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
90 static char *getScoreDir(char *level_subdir)
92 static char *score_dir = NULL;
93 char *data_dir = getGlobalDataDir();
94 char *score_subdir = SCORES_DIRECTORY;
99 score_dir = checked_malloc(strlen(data_dir) + strlen(score_subdir) +
100 strlen(level_subdir) + 3);
101 sprintf(score_dir, "%s/%s%s%s", data_dir, score_subdir,
102 (strlen(level_subdir) > 0 ? "/" : ""), level_subdir);
107 static void createDirectory(char *dir, char *text)
109 if (access(dir, F_OK) != 0)
110 if (mkdir(dir, USERDATA_DIR_MODE) != 0)
111 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
114 static void InitUserDataDirectory()
116 createDirectory(getUserDataDir(), "user data");
119 static void InitTapeDirectory(char *level_subdir)
121 createDirectory(getTapeDir(""), "main tape");
122 createDirectory(getTapeDir(level_subdir), "level tape");
125 static void InitScoreDirectory(char *level_subdir)
127 createDirectory(getScoreDir(""), "main score");
128 createDirectory(getScoreDir(level_subdir), "level score");
131 static void InitUserLevelDirectory(char *level_subdir)
133 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
135 createDirectory(getUserLevelDir(""), "main user level");
136 createDirectory(getUserLevelDir(level_subdir), "user level");
142 void LoadLevel(int level_nr)
145 char filename[MAX_FILENAME_LEN];
146 char cookie[MAX_FILENAME_LEN];
149 if (leveldir[leveldir_nr].user_defined)
150 sprintf(filename, "%s/%s/%d",
151 getUserLevelDir(""), leveldir[leveldir_nr].filename, level_nr);
153 sprintf(filename, "%s/%s/%d",
154 options.level_directory, leveldir[leveldir_nr].filename, level_nr);
156 if (!(file = fopen(filename, "r")))
157 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
160 fgets(cookie, LEVEL_COOKIE_LEN, file);
163 if (strcmp(cookie,LEVEL_COOKIE))
165 Error(ERR_WARN, "wrong format of level file '%s'", filename);
173 lev_fieldx = level.fieldx = fgetc(file);
174 lev_fieldy = level.fieldy = fgetc(file);
176 level.time = (fgetc(file)<<8) | fgetc(file);
177 level.edelsteine = (fgetc(file)<<8) | fgetc(file);
178 for(i=0; i<MAX_LEVNAMLEN; i++)
179 level.name[i] = fgetc(file);
180 level.name[MAX_LEVNAMLEN-1] = 0;
181 for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
182 level.score[i] = fgetc(file);
186 level.mampfer_inhalt[i][x][y] = fgetc(file);
187 level.tempo_amoebe = fgetc(file);
188 level.dauer_sieb = fgetc(file);
189 level.dauer_ablenk = fgetc(file);
190 level.amoebe_inhalt = fgetc(file);
192 for(i=0; i<NUM_FREE_LVHD_BYTES; i++) /* Rest frei / Headergröße 80 Bytes */
195 for(y=0; y<MAX_LEV_FIELDY; y++)
196 for(x=0; x<MAX_LEV_FIELDX; x++)
197 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
199 for(y=0; y<lev_fieldy; y++)
200 for(x=0; x<lev_fieldx; x++)
201 Feld[x][y] = Ur[x][y] = fgetc(file);
205 if (level.time <= 10) /* Mindestspieldauer */
210 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
211 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
214 level.edelsteine = 0;
215 strcpy(level.name, "Nameless Level");
216 for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
221 level.mampfer_inhalt[i][x][y] = EL_FELSBROCKEN;
222 level.tempo_amoebe = 10;
223 level.dauer_sieb = 10;
224 level.dauer_ablenk = 10;
225 level.amoebe_inhalt = EL_DIAMANT;
227 for(y=0; y<STD_LEV_FIELDY; y++)
228 for(x=0; x<STD_LEV_FIELDX; x++)
229 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
230 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
231 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
232 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
236 void SaveLevel(int level_nr)
239 char filename[MAX_FILENAME_LEN];
242 if (leveldir[leveldir_nr].user_defined)
243 sprintf(filename, "%s/%s/%d",
244 getUserLevelDir(""), leveldir[leveldir_nr].filename, level_nr);
246 sprintf(filename, "%s/%s/%d",
247 options.level_directory, leveldir[leveldir_nr].filename, level_nr);
249 if (!(file = fopen(filename, "w")))
251 Error(ERR_WARN, "cannot save level file '%s'", filename);
255 fputs(LEVEL_COOKIE,file); /* Formatkennung */
258 fputc(level.fieldx, file);
259 fputc(level.fieldy, file);
260 fputc(level.time / 256, file);
261 fputc(level.time % 256, file);
262 fputc(level.edelsteine / 256, file);
263 fputc(level.edelsteine % 256, file);
265 for(i=0; i<MAX_LEVNAMLEN; i++)
266 fputc(level.name[i], file);
267 for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
268 fputc(level.score[i], file);
272 fputc(level.mampfer_inhalt[i][x][y], file);
273 fputc(level.tempo_amoebe, file);
274 fputc(level.dauer_sieb, file);
275 fputc(level.dauer_ablenk, file);
276 fputc(level.amoebe_inhalt, file);
278 for(i=0; i<NUM_FREE_LVHD_BYTES; i++) /* Rest frei / Headergröße 80 Bytes */
281 for(y=0; y<lev_fieldy; y++)
282 for(x=0; x<lev_fieldx; x++)
283 fputc(Ur[x][y], file);
287 chmod(filename, LEVEL_PERMS);
290 void LoadTape(int level_nr)
293 char filename[MAX_FILENAME_LEN];
294 char cookie[MAX_FILENAME_LEN];
296 boolean levelrec_10 = FALSE;
298 sprintf(filename, "%s/%d.%s",
299 getTapeDir(leveldir[leveldir_nr].filename),
300 level_nr, TAPEFILE_EXTENSION);
302 if ((file = fopen(filename, "r")))
304 fgets(cookie, LEVELREC_COOKIE_LEN, file);
306 if (!strcmp(cookie, LEVELREC_COOKIE_10)) /* old 1.0 tape format */
308 else if (strcmp(cookie, LEVELREC_COOKIE)) /* unknown tape format */
310 Error(ERR_WARN, "wrong format of level recording file '%s'", filename);
320 (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
322 (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
324 (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
326 tape.level_nr = level_nr;
328 tape.changed = FALSE;
330 tape.recording = FALSE;
331 tape.playing = FALSE;
332 tape.pausing = FALSE;
334 for(i=0; i<tape.length; i++)
338 if (i >= MAX_TAPELEN)
341 for(j=0; j<MAX_PLAYERS; j++)
343 if (levelrec_10 && j > 0)
345 tape.pos[i].action[j] = MV_NO_MOVING;
348 tape.pos[i].action[j] = fgetc(file);
351 tape.pos[i].delay = fgetc(file);
355 /* eliminate possible diagonal moves in old tapes */
356 /* this is only for backward compatibility */
358 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
359 byte action = tape.pos[i].action[0];
360 int k, num_moves = 0;
364 if (action & joy_dir[k])
366 tape.pos[i + num_moves].action[0] = joy_dir[k];
368 tape.pos[i + num_moves].delay = 0;
377 tape.length += num_moves;
387 if (i != tape.length)
388 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
390 tape.length_seconds = GetTapeLength();
393 void SaveTape(int level_nr)
396 char filename[MAX_FILENAME_LEN];
398 boolean new_tape = TRUE;
400 InitTapeDirectory(leveldir[leveldir_nr].filename);
402 sprintf(filename, "%s/%d.%s",
403 getTapeDir(leveldir[leveldir_nr].filename),
404 level_nr, TAPEFILE_EXTENSION);
406 /* if a tape still exists, ask to overwrite it */
407 if ((file = fopen(filename, "r")))
412 if (!Request("Replace old tape ?", REQ_ASK))
416 if (!(file = fopen(filename, "w")))
418 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
422 fputs(LEVELREC_COOKIE, file); /* Formatkennung */
425 fputc((tape.random_seed >> 24) & 0xff,file);
426 fputc((tape.random_seed >> 16) & 0xff,file);
427 fputc((tape.random_seed >> 8) & 0xff,file);
428 fputc((tape.random_seed >> 0) & 0xff,file);
430 fputc((tape.date >> 24) & 0xff,file);
431 fputc((tape.date >> 16) & 0xff,file);
432 fputc((tape.date >> 8) & 0xff,file);
433 fputc((tape.date >> 0) & 0xff,file);
435 fputc((tape.length >> 24) & 0xff,file);
436 fputc((tape.length >> 16) & 0xff,file);
437 fputc((tape.length >> 8) & 0xff,file);
438 fputc((tape.length >> 0) & 0xff,file);
440 for(i=0; i<tape.length; i++)
444 for(j=0; j<MAX_PLAYERS; j++)
445 fputc(tape.pos[i].action[j], file);
447 fputc(tape.pos[i].delay, file);
452 chmod(filename, LEVREC_PERMS);
454 tape.changed = FALSE;
457 Request("tape saved !",REQ_CONFIRM);
460 void LoadScore(int level_nr)
463 char filename[MAX_FILENAME_LEN];
464 char cookie[MAX_FILENAME_LEN];
465 char line[MAX_LINE_LEN];
469 /* start with empty score table */
470 for(i=0; i<MAX_SCORE_ENTRIES; i++)
472 strcpy(highscore[i].Name, EMPTY_ALIAS);
473 highscore[i].Score = 0;
476 sprintf(filename, "%s/%d.%s",
477 getScoreDir(leveldir[leveldir_nr].filename),
478 level_nr, SCOREFILE_EXTENSION);
480 if (!(file = fopen(filename, "r")))
483 fgets(cookie, SCORE_COOKIE_LEN, file);
485 if (strcmp(cookie, SCORE_COOKIE) != 0)
487 Error(ERR_WARN, "wrong format of score file '%s'", filename);
492 for(i=0; i<MAX_SCORE_ENTRIES; i++)
494 fscanf(file, "%d", &highscore[i].Score);
495 fgets(line, MAX_LINE_LEN, file);
497 if (line[strlen(line)-1] == '\n')
498 line[strlen(line)-1] = '\0';
500 for (line_ptr = line; *line_ptr; line_ptr++)
502 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
504 strncpy(highscore[i].Name, line_ptr, MAX_NAMELEN - 1);
505 highscore[i].Name[MAX_NAMELEN - 1] = '\0';
514 void SaveScore(int level_nr)
517 char filename[MAX_FILENAME_LEN];
520 InitScoreDirectory(leveldir[leveldir_nr].filename);
522 sprintf(filename, "%s/%d.%s",
523 getScoreDir(leveldir[leveldir_nr].filename),
524 level_nr, SCOREFILE_EXTENSION);
526 if (!(file = fopen(filename, "w")))
528 Error(ERR_WARN, "cannot save score for level %d", level_nr);
532 fprintf(file, "%s\n\n", SCORE_COOKIE);
534 for(i=0; i<MAX_SCORE_ENTRIES; i++)
535 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
539 chmod(filename, SCORE_PERMS);
542 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
543 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
544 #define TOKEN_STR_PLAYER_PREFIX "player_"
546 #define TOKEN_VALUE_POSITION 30
549 #define SETUP_TOKEN_PLAYER_NAME 0
550 #define SETUP_TOKEN_SOUND 1
551 #define SETUP_TOKEN_SOUND_LOOPS 2
552 #define SETUP_TOKEN_SOUND_MUSIC 3
553 #define SETUP_TOKEN_SOUND_SIMPLE 4
554 #define SETUP_TOKEN_TOONS 5
555 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
556 #define SETUP_TOKEN_SCROLL_DELAY 7
557 #define SETUP_TOKEN_SOFT_SCROLLING 8
558 #define SETUP_TOKEN_FADING 9
559 #define SETUP_TOKEN_AUTORECORD 10
560 #define SETUP_TOKEN_QUICK_DOORS 11
561 #define SETUP_TOKEN_TEAM_MODE 12
564 #define SETUP_TOKEN_USE_JOYSTICK 13
565 #define SETUP_TOKEN_JOY_DEVICE_NAME 14
566 #define SETUP_TOKEN_JOY_XLEFT 15
567 #define SETUP_TOKEN_JOY_XMIDDLE 16
568 #define SETUP_TOKEN_JOY_XRIGHT 17
569 #define SETUP_TOKEN_JOY_YUPPER 18
570 #define SETUP_TOKEN_JOY_YMIDDLE 19
571 #define SETUP_TOKEN_JOY_YLOWER 20
572 #define SETUP_TOKEN_JOY_SNAP 21
573 #define SETUP_TOKEN_JOY_BOMB 22
574 #define SETUP_TOKEN_KEY_LEFT 23
575 #define SETUP_TOKEN_KEY_RIGHT 24
576 #define SETUP_TOKEN_KEY_UP 25
577 #define SETUP_TOKEN_KEY_DOWN 26
578 #define SETUP_TOKEN_KEY_SNAP 27
579 #define SETUP_TOKEN_KEY_BOMB 28
581 /* level directory info */
582 #define LEVELINFO_TOKEN_NAME 29
583 #define LEVELINFO_TOKEN_LEVELS 30
584 #define LEVELINFO_TOKEN_SORT_PRIORITY 31
585 #define LEVELINFO_TOKEN_READONLY 32
587 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
588 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_TEAM_MODE
590 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
591 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
593 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
594 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
596 #define TYPE_BOOLEAN 1
597 #define TYPE_SWITCH 2
598 #define TYPE_KEYSYM 3
599 #define TYPE_INTEGER 4
600 #define TYPE_STRING 5
602 static struct SetupInfo si;
603 static struct SetupInputInfo sii;
604 static struct LevelDirInfo ldi;
613 { TYPE_STRING, &si.player_name, "player_name" },
614 { TYPE_SWITCH, &si.sound, "sound" },
615 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
616 { TYPE_SWITCH, &si.sound_music, "background_music" },
617 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
618 { TYPE_SWITCH, &si.toons, "toons" },
619 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
620 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
621 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
622 { TYPE_SWITCH, &si.fading, "screen_fading" },
623 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
624 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
625 { TYPE_SWITCH, &si.team_mode, "team_mode" },
628 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
629 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
630 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
631 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
632 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
633 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
634 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
635 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
636 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
637 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
638 { TYPE_KEYSYM, &sii.key.left, ".key.move_left" },
639 { TYPE_KEYSYM, &sii.key.right, ".key.move_right" },
640 { TYPE_KEYSYM, &sii.key.up, ".key.move_up" },
641 { TYPE_KEYSYM, &sii.key.down, ".key.move_down" },
642 { TYPE_KEYSYM, &sii.key.snap, ".key.snap_field" },
643 { TYPE_KEYSYM, &sii.key.bomb, ".key.place_bomb" },
645 /* level directory info */
646 { TYPE_STRING, &ldi.name, "name" },
647 { TYPE_INTEGER, &ldi.levels, "levels" },
648 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
649 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
652 static char *string_tolower(char *s)
654 static char s_lower[100];
657 if (strlen(s) >= 100)
662 for (i=0; i<strlen(s_lower); i++)
663 s_lower[i] = tolower(s_lower[i]);
668 static int get_string_integer_value(char *s)
670 static char *number_text[][3] =
672 { "0", "zero", "null", },
673 { "1", "one", "first" },
674 { "2", "two", "second" },
675 { "3", "three", "third" },
676 { "4", "four", "fourth" },
677 { "5", "five", "fifth" },
678 { "6", "six", "sixth" },
679 { "7", "seven", "seventh" },
680 { "8", "eight", "eighth" },
681 { "9", "nine", "ninth" },
682 { "10", "ten", "tenth" },
683 { "11", "eleven", "eleventh" },
684 { "12", "twelve", "twelfth" },
691 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
697 static boolean get_string_boolean_value(char *s)
699 if (strcmp(string_tolower(s), "true") == 0 ||
700 strcmp(string_tolower(s), "yes") == 0 ||
701 strcmp(string_tolower(s), "on") == 0 ||
702 get_string_integer_value(s) == 1)
708 static char *getFormattedSetupEntry(char *token, char *value)
711 static char entry[MAX_LINE_LEN];
713 sprintf(entry, "%s:", token);
714 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
718 strcat(entry, value);
723 static void freeSetupFileList(struct SetupFileList *setup_file_list)
725 if (!setup_file_list)
728 if (setup_file_list->token)
729 free(setup_file_list->token);
730 if (setup_file_list->value)
731 free(setup_file_list->value);
732 if (setup_file_list->next)
733 freeSetupFileList(setup_file_list->next);
734 free(setup_file_list);
737 static struct SetupFileList *newSetupFileList(char *token, char *value)
739 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
741 new->token = checked_malloc(strlen(token) + 1);
742 strcpy(new->token, token);
744 new->value = checked_malloc(strlen(value) + 1);
745 strcpy(new->value, value);
752 static char *getTokenValue(struct SetupFileList *setup_file_list,
755 if (!setup_file_list)
758 if (strcmp(setup_file_list->token, token) == 0)
759 return setup_file_list->value;
761 return getTokenValue(setup_file_list->next, token);
764 static void setTokenValue(struct SetupFileList *setup_file_list,
765 char *token, char *value)
767 if (!setup_file_list)
770 if (strcmp(setup_file_list->token, token) == 0)
772 free(setup_file_list->value);
773 setup_file_list->value = checked_malloc(strlen(value) + 1);
774 strcpy(setup_file_list->value, value);
776 else if (setup_file_list->next == NULL)
777 setup_file_list->next = newSetupFileList(token, value);
779 setTokenValue(setup_file_list->next, token, value);
783 static void printSetupFileList(struct SetupFileList *setup_file_list)
785 if (!setup_file_list)
788 printf("token: '%s'\n", setup_file_list->token);
789 printf("value: '%s'\n", setup_file_list->value);
791 printSetupFileList(setup_file_list->next);
795 static struct SetupFileList *loadSetupFileList(char *filename)
798 char line[MAX_LINE_LEN];
799 char *token, *value, *line_ptr;
800 struct SetupFileList *setup_file_list = newSetupFileList("", "");
801 struct SetupFileList *first_valid_list_entry;
805 if (!(file = fopen(filename, "r")))
807 Error(ERR_WARN, "cannot open setup/info file '%s'", filename);
813 /* read next line of input file */
814 if (!fgets(line, MAX_LINE_LEN, file))
817 /* cut trailing comment or whitespace from input line */
818 for (line_ptr = line; *line_ptr; line_ptr++)
820 if (*line_ptr == '#' || *line_ptr == '\n')
827 /* cut trailing whitespaces from input line */
828 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
829 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
832 /* ignore empty lines */
836 line_len = strlen(line);
838 /* cut leading whitespaces from token */
839 for (token = line; *token; token++)
840 if (*token != ' ' && *token != '\t')
843 /* find end of token */
844 for (line_ptr = token; *line_ptr; line_ptr++)
846 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
853 if (line_ptr < line + line_len)
854 value = line_ptr + 1;
858 /* cut leading whitespaces from value */
859 for (; *value; value++)
860 if (*value != ' ' && *value != '\t')
863 if (*token && *value)
864 setTokenValue(setup_file_list, token, value);
869 first_valid_list_entry = setup_file_list->next;
871 /* free empty list header */
872 setup_file_list->next = NULL;
873 freeSetupFileList(setup_file_list);
875 if (first_valid_list_entry == NULL)
876 Error(ERR_WARN, "setup/info file '%s' is empty", filename);
878 return first_valid_list_entry;
881 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
884 if (!setup_file_list)
887 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
889 if (strcmp(setup_file_list->value, identifier) != 0)
891 Error(ERR_WARN, "setup/info file has wrong version");
898 if (setup_file_list->next)
899 checkSetupFileListIdentifier(setup_file_list->next, identifier);
902 Error(ERR_WARN, "setup/info file has no version information");
907 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
909 ldi->name = getStringCopy("non-existing");
911 ldi->sort_priority = 999; /* default: least priority */
912 ldi->readonly = TRUE;
915 static void setSetupInfoToDefaults(struct SetupInfo *si)
919 si->player_name = getStringCopy(getLoginName());
922 si->sound_loops = FALSE;
923 si->sound_music = FALSE;
924 si->sound_simple = FALSE;
926 si->double_buffering = TRUE;
927 si->direct_draw = !si->double_buffering;
928 si->scroll_delay = FALSE;
929 si->soft_scrolling = TRUE;
931 si->autorecord = FALSE;
932 si->quick_doors = FALSE;
934 for (i=0; i<MAX_PLAYERS; i++)
936 si->input[i].use_joystick = FALSE;
937 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
938 si->input[i].joy.xleft = JOYSTICK_XLEFT;
939 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
940 si->input[i].joy.xright = JOYSTICK_XRIGHT;
941 si->input[i].joy.yupper = JOYSTICK_YUPPER;
942 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
943 si->input[i].joy.ylower = JOYSTICK_YLOWER;
944 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
945 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
946 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KEY_UNDEFINDED);
947 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KEY_UNDEFINDED);
948 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KEY_UNDEFINDED);
949 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KEY_UNDEFINDED);
950 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KEY_UNDEFINDED);
951 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KEY_UNDEFINDED);
955 static void setSetupInfo(int token_nr, char *token_value)
957 int token_type = token_info[token_nr].type;
958 void *setup_value = token_info[token_nr].value;
960 if (token_value == NULL)
963 /* set setup field to corresponding token value */
968 *(boolean *)setup_value = get_string_boolean_value(token_value);
972 *(KeySym *)setup_value = getKeySymFromX11KeyName(token_value);
976 *(int *)setup_value = get_string_integer_value(token_value);
980 if (*(char **)setup_value != NULL)
981 free(*(char **)setup_value);
982 *(char **)setup_value = getStringCopy(token_value);
990 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
994 if (!setup_file_list)
997 /* handle global setup values */
999 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1000 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1003 /* handle player specific setup values */
1004 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1008 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1010 sii = setup.input[pnr];
1011 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1013 char full_token[100];
1015 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1016 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1018 setup.input[pnr] = sii;
1022 int getLevelSeriesNrFromLevelSeriesName(char *level_series_name)
1026 if (!level_series_name)
1029 for (i=0; i<num_leveldirs; i++)
1030 if (strcmp(level_series_name, leveldir[i].filename) == 0)
1036 int getLastPlayedLevelOfLevelSeries(char *level_series_name)
1039 int level_series_nr = getLevelSeriesNrFromLevelSeriesName(level_series_name);
1040 int last_level_nr = 0;
1042 if (!level_series_name)
1045 token_value = getTokenValue(level_setup_list, level_series_name);
1049 int highest_level_nr = leveldir[level_series_nr].levels - 1;
1051 last_level_nr = atoi(token_value);
1053 if (last_level_nr < 0)
1055 if (last_level_nr > highest_level_nr)
1056 last_level_nr = highest_level_nr;
1059 return last_level_nr;
1062 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1064 const struct LevelDirInfo *entry1 = object1;
1065 const struct LevelDirInfo *entry2 = object2;
1068 if (entry1->sort_priority != entry2->sort_priority)
1069 compare_result = entry1->sort_priority - entry2->sort_priority;
1072 char *name1 = getStringToLower(entry1->name);
1073 char *name2 = getStringToLower(entry2->name);
1075 compare_result = strcmp(name1, name2);
1081 return compare_result;
1084 static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
1087 struct stat file_status;
1088 char *directory = NULL;
1089 char *filename = NULL;
1090 struct SetupFileList *setup_file_list = NULL;
1091 struct dirent *dir_entry;
1092 int i, current_entry = start_entry;
1094 if ((dir = opendir(level_directory)) == NULL)
1095 Error(ERR_EXIT, "cannot read level directory '%s'", level_directory);
1097 while (current_entry < MAX_LEVDIR_ENTRIES)
1099 if ((dir_entry = readdir(dir)) == NULL) /* last directory entry */
1102 /* skip entries for current and parent directory */
1103 if (strcmp(dir_entry->d_name, ".") == 0 ||
1104 strcmp(dir_entry->d_name, "..") == 0)
1107 /* find out if directory entry is itself a directory */
1108 directory = getPath2(level_directory, dir_entry->d_name);
1109 if (stat(directory, &file_status) != 0 || /* cannot stat file */
1110 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1116 if (strlen(dir_entry->d_name) >= MAX_LEVDIR_FILENAME)
1118 Error(ERR_WARN, "filename of level directory '%s' too long -- ignoring",
1123 filename = getPath2(directory, LEVELINFO_FILENAME);
1124 setup_file_list = loadSetupFileList(filename);
1126 if (setup_file_list)
1128 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1129 setLevelDirInfoToDefaults(&leveldir[current_entry]);
1131 ldi = leveldir[current_entry];
1132 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1133 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1134 leveldir[current_entry] = ldi;
1136 leveldir[current_entry].filename = getStringCopy(dir_entry->d_name);
1137 leveldir[current_entry].user_defined =
1138 (level_directory == options.level_directory ? FALSE : TRUE);
1140 freeSetupFileList(setup_file_list);
1144 Error(ERR_WARN, "ignoring level directory '%s'", directory);
1150 if (current_entry == MAX_LEVDIR_ENTRIES)
1151 Error(ERR_WARN, "using %d level directories -- ignoring the rest",
1156 if (current_entry == start_entry && start_entry != -1)
1157 Error(ERR_EXIT, "cannot find any valid level series in directory '%s'",
1160 return current_entry;
1163 void LoadLevelInfo()
1165 InitUserLevelDirectory(getLoginName());
1170 num_leveldirs = LoadLevelInfoFromLevelDir(options.level_directory,
1172 num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""),
1174 if (num_leveldirs > 1)
1175 qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo),
1176 compareLevelDirInfoEntries);
1179 static void SaveUserLevelInfo()
1181 char filename[MAX_FILENAME_LEN];
1185 sprintf(filename, "%s/%s",
1186 getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1188 if (!(file = fopen(filename, "w")))
1190 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1194 ldi.name = getLoginName();
1196 ldi.sort_priority = 300;
1197 ldi.readonly = FALSE;
1199 fprintf(file, "%s\n\n",
1200 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1202 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1203 fprintf(file, "%s\n", getSetupLine("", i));
1207 chmod(filename, SETUP_PERMS);
1212 char filename[MAX_FILENAME_LEN];
1213 struct SetupFileList *setup_file_list = NULL;
1215 /* always start with reliable default setup values */
1216 setSetupInfoToDefaults(&setup);
1218 sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME);
1220 setup_file_list = loadSetupFileList(filename);
1222 if (setup_file_list)
1224 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1225 decodeSetupFileList(setup_file_list);
1227 setup.direct_draw = !setup.double_buffering;
1229 freeSetupFileList(setup_file_list);
1231 /* needed to work around problems with fixed length strings */
1232 if (strlen(setup.player_name) >= MAX_NAMELEN)
1233 setup.player_name[MAX_NAMELEN - 1] = '\0';
1234 else if (strlen(setup.player_name) < MAX_NAMELEN - 1)
1236 char *new_name = checked_malloc(MAX_NAMELEN);
1238 strcpy(new_name, setup.player_name);
1239 free(setup.player_name);
1240 setup.player_name = new_name;
1244 Error(ERR_WARN, "using default setup values");
1247 static char *getSetupLine(char *prefix, int token_nr)
1250 static char entry[MAX_LINE_LEN];
1251 int token_type = token_info[token_nr].type;
1252 void *setup_value = token_info[token_nr].value;
1253 char *token_text = token_info[token_nr].text;
1255 /* start with the prefix, token and some spaces to format output line */
1256 sprintf(entry, "%s%s:", prefix, token_text);
1257 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1260 /* continue with the token's value (which can have different types) */
1264 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1268 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1273 KeySym keysym = *(KeySym *)setup_value;
1274 char *keyname = getKeyNameFromKeySym(keysym);
1276 strcat(entry, getX11KeyNameFromKeySym(keysym));
1277 for (i=strlen(entry); i<50; i++)
1280 /* add comment, if useful */
1281 if (strcmp(keyname, "(undefined)") != 0 &&
1282 strcmp(keyname, "(unknown)") != 0)
1284 strcat(entry, "# ");
1285 strcat(entry, keyname);
1292 char buffer[MAX_LINE_LEN];
1294 sprintf(buffer, "%d", *(int *)setup_value);
1295 strcat(entry, buffer);
1300 strcat(entry, *(char **)setup_value);
1313 char filename[MAX_FILENAME_LEN];
1316 InitUserDataDirectory();
1318 sprintf(filename, "%s/%s", getSetupDir(), SETUP_FILENAME);
1320 if (!(file = fopen(filename, "w")))
1322 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1326 fprintf(file, "%s\n",
1327 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1328 fprintf(file, "\n");
1330 /* handle global setup values */
1332 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1334 fprintf(file, "%s\n", getSetupLine("", i));
1336 /* just to make things nicer :) */
1337 if (i == SETUP_TOKEN_PLAYER_NAME)
1338 fprintf(file, "\n");
1341 /* handle player specific setup values */
1342 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1346 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1347 fprintf(file, "\n");
1349 sii = setup.input[pnr];
1350 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1351 fprintf(file, "%s\n", getSetupLine(prefix, i));
1356 chmod(filename, SETUP_PERMS);
1359 void LoadLevelSetup()
1361 char filename[MAX_FILENAME_LEN];
1363 /* always start with reliable default setup values */
1368 sprintf(filename, "%s/%s", getSetupDir(), LEVELSETUP_FILENAME);
1370 if (level_setup_list)
1371 freeSetupFileList(level_setup_list);
1373 level_setup_list = loadSetupFileList(filename);
1375 if (level_setup_list)
1377 char *last_level_series =
1378 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1380 leveldir_nr = getLevelSeriesNrFromLevelSeriesName(last_level_series);
1381 level_nr = getLastPlayedLevelOfLevelSeries(last_level_series);
1383 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1387 level_setup_list = newSetupFileList(TOKEN_STR_FILE_IDENTIFIER,
1389 Error(ERR_WARN, "using default setup values");
1393 void SaveLevelSetup()
1395 char filename[MAX_FILENAME_LEN];
1396 struct SetupFileList *list_entry = level_setup_list;
1399 InitUserDataDirectory();
1401 setTokenValue(level_setup_list,
1402 TOKEN_STR_LAST_LEVEL_SERIES, leveldir[leveldir_nr].filename);
1404 setTokenValue(level_setup_list,
1405 leveldir[leveldir_nr].filename, int2str(level_nr, 0));
1407 sprintf(filename, "%s/%s", getSetupDir(), LEVELSETUP_FILENAME);
1409 if (!(file = fopen(filename, "w")))
1411 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1415 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1416 LEVELSETUP_COOKIE));
1419 if (strcmp(list_entry->token, TOKEN_STR_FILE_IDENTIFIER) != 0)
1420 fprintf(file, "%s\n",
1421 getFormattedSetupEntry(list_entry->token, list_entry->value));
1423 /* just to make things nicer :) */
1424 if (strcmp(list_entry->token, TOKEN_STR_LAST_LEVEL_SERIES) == 0)
1425 fprintf(file, "\n");
1427 list_entry = list_entry->next;
1432 chmod(filename, SETUP_PERMS);