1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
25 /* file identifier strings */
26 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
27 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
28 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
30 /* file names and filename extensions */
31 #if !defined(PLATFORM_MSDOS)
32 #define LEVELSETUP_DIRECTORY "levelsetup"
33 #define SETUP_FILENAME "setup.conf"
34 #define LEVELSETUP_FILENAME "levelsetup.conf"
35 #define LEVELINFO_FILENAME "levelinfo.conf"
36 #define LEVELFILE_EXTENSION "level"
37 #define TAPEFILE_EXTENSION "tape"
38 #define SCOREFILE_EXTENSION "score"
40 #define LEVELSETUP_DIRECTORY "lvlsetup"
41 #define SETUP_FILENAME "setup.cnf"
42 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
43 #define LEVELINFO_FILENAME "lvlinfo.cnf"
44 #define LEVELFILE_EXTENSION "lvl"
45 #define TAPEFILE_EXTENSION "tap"
46 #define SCOREFILE_EXTENSION "sco"
49 #define NUM_LEVELCLASS_DESC 8
50 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
62 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
63 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
64 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
65 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
66 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
67 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
68 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
69 IS_LEVELCLASS_USER(n) ? FC_RED : \
72 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
73 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
74 IS_LEVELCLASS_BD(n) ? 2 : \
75 IS_LEVELCLASS_EM(n) ? 3 : \
76 IS_LEVELCLASS_SP(n) ? 4 : \
77 IS_LEVELCLASS_DX(n) ? 5 : \
78 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
79 IS_LEVELCLASS_USER(n) ? 7 : \
82 /* values for setup file stuff */
83 #define TYPE_BOOLEAN 1
86 #define TYPE_INTEGER 4
89 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
91 #define TOKEN_VALUE_POSITION 30
97 struct SetupFileList *next;
108 /* ------------------------------------------------------------------------- */
110 /* ------------------------------------------------------------------------- */
113 int get_string_integer_value(char *);
114 boolean get_string_boolean_value(char *);
115 char *getFormattedSetupEntry(char *, char *);
116 void freeSetupFileList(struct SetupFileList *);
117 char *getTokenValue(struct SetupFileList *, char *);
118 struct SetupFileList *loadSetupFileList(char *);
119 void checkSetupFileListIdentifier(struct SetupFileList *, char *);
122 char *getLevelClassDescription(struct LevelDirInfo *ldi)
124 int position = ldi->sort_priority / 100;
126 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
127 return levelclass_desc[position];
129 return "Unknown Level Class";
132 /* for 'InitUserLevelDir()' */
133 static void SaveUserLevelInfo();
135 /* for 'SaveUserLevelInfo()' */
136 static char *getSetupLine(struct TokenInfo *, char *, int);
138 static char *getUserLevelDir(char *level_subdir)
140 static char *userlevel_dir = NULL;
141 char *data_dir = getUserDataDir();
142 char *userlevel_subdir = LEVELS_DIRECTORY;
147 if (strlen(level_subdir) > 0)
148 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
150 userlevel_dir = getPath2(data_dir, userlevel_subdir);
152 return userlevel_dir;
155 static char *getTapeDir(char *level_subdir)
157 static char *tape_dir = NULL;
158 char *data_dir = getUserDataDir();
159 char *tape_subdir = TAPES_DIRECTORY;
164 if (strlen(level_subdir) > 0)
165 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
167 tape_dir = getPath2(data_dir, tape_subdir);
172 static char *getScoreDir(char *level_subdir)
174 static char *score_dir = NULL;
175 char *data_dir = options.rw_base_directory;
176 char *score_subdir = SCORES_DIRECTORY;
181 if (strlen(level_subdir) > 0)
182 score_dir = getPath3(data_dir, score_subdir, level_subdir);
184 score_dir = getPath2(data_dir, score_subdir);
189 static char *getLevelSetupDir(char *level_subdir)
191 static char *levelsetup_dir = NULL;
192 char *data_dir = getUserDataDir();
193 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
196 free(levelsetup_dir);
198 if (strlen(level_subdir) > 0)
199 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
201 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
203 return levelsetup_dir;
206 char *getLevelFilename(int nr)
208 static char *filename = NULL;
209 char basename[MAX_FILENAME_LEN];
211 if (filename != NULL)
214 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
215 filename = getPath3((leveldir_current->user_defined ?
216 getUserLevelDir("") :
217 options.level_directory),
218 leveldir_current->fullpath,
224 char *getTapeFilename(int nr)
226 static char *filename = NULL;
227 char basename[MAX_FILENAME_LEN];
229 if (filename != NULL)
232 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
233 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
238 char *getScoreFilename(int nr)
240 static char *filename = NULL;
241 char basename[MAX_FILENAME_LEN];
243 if (filename != NULL)
246 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
247 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
252 void InitTapeDirectory(char *level_subdir)
254 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
255 createDirectory(getTapeDir(""), "main tape", PERMS_PRIVATE);
256 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
259 void InitScoreDirectory(char *level_subdir)
261 createDirectory(getScoreDir(""), "main score", PERMS_PUBLIC);
262 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
265 void InitUserLevelDirectory(char *level_subdir)
267 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
269 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
270 createDirectory(getUserLevelDir(""), "main user level", PERMS_PRIVATE);
271 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
277 void InitLevelSetupDirectory(char *level_subdir)
279 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
280 createDirectory(getLevelSetupDir(""), "main level setup", PERMS_PRIVATE);
281 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
284 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
286 int file_version_major, file_version_minor, file_version_patch;
287 int game_version_major, game_version_minor, game_version_patch;
289 file_version_major = fgetc(file);
290 file_version_minor = fgetc(file);
291 file_version_patch = fgetc(file);
292 fgetc(file); /* not used */
294 game_version_major = fgetc(file);
295 game_version_minor = fgetc(file);
296 game_version_patch = fgetc(file);
297 fgetc(file); /* not used */
299 *file_version = VERSION_IDENT(file_version_major,
303 *game_version = VERSION_IDENT(game_version_major,
308 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
310 int file_version_major = VERSION_MAJOR(file_version);
311 int file_version_minor = VERSION_MINOR(file_version);
312 int file_version_patch = VERSION_PATCH(file_version);
313 int game_version_major = VERSION_MAJOR(game_version);
314 int game_version_minor = VERSION_MINOR(game_version);
315 int game_version_patch = VERSION_PATCH(game_version);
317 fputc(file_version_major, file);
318 fputc(file_version_minor, file);
319 fputc(file_version_patch, file);
320 fputc(0, file); /* not used */
322 fputc(game_version_major, file);
323 fputc(game_version_minor, file);
324 fputc(game_version_patch, file);
325 fputc(0, file); /* not used */
329 /* ------------------------------------------------------------------------- */
330 /* some functions to handle lists of level directories */
331 /* ------------------------------------------------------------------------- */
333 struct LevelDirInfo *newLevelDirInfo()
335 return checked_calloc(sizeof(struct LevelDirInfo));
338 void pushLevelDirInfo(struct LevelDirInfo **node_first,
339 struct LevelDirInfo *node_new)
341 node_new->next = *node_first;
342 *node_first = node_new;
345 int numLevelDirInfo(struct LevelDirInfo *node)
358 boolean validLevelSeries(struct LevelDirInfo *node)
360 return (node != NULL && !node->node_group && !node->parent_link);
363 struct LevelDirInfo *getFirstValidLevelSeries(struct LevelDirInfo *node)
367 if (leveldir_first) /* start with first level directory entry */
368 return getFirstValidLevelSeries(leveldir_first);
372 else if (node->node_group) /* enter level group (step down into tree) */
373 return getFirstValidLevelSeries(node->node_group);
374 else if (node->parent_link) /* skip start entry of level group */
376 if (node->next) /* get first real level series entry */
377 return getFirstValidLevelSeries(node->next);
378 else /* leave empty level group and go on */
379 return getFirstValidLevelSeries(node->node_parent->next);
381 else /* this seems to be a regular level series */
385 struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *node)
390 if (node->node_parent == NULL) /* top level group */
391 return leveldir_first;
392 else /* sub level group */
393 return node->node_parent->node_group;
396 int numLevelDirInfoInGroup(struct LevelDirInfo *node)
398 return numLevelDirInfo(getLevelDirInfoFirstGroupEntry(node));
401 int posLevelDirInfo(struct LevelDirInfo *node)
403 struct LevelDirInfo *node_cmp = getLevelDirInfoFirstGroupEntry(node);
408 if (node_cmp == node)
412 node_cmp = node_cmp->next;
418 struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *node, int pos)
420 struct LevelDirInfo *node_default = node;
435 struct LevelDirInfo *getLevelDirInfoFromFilenameExt(struct LevelDirInfo *node,
438 if (filename == NULL)
443 if (node->node_group)
445 struct LevelDirInfo *node_group;
447 node_group = getLevelDirInfoFromFilenameExt(node->node_group, filename);
452 else if (!node->parent_link)
454 if (strcmp(filename, node->filename) == 0)
464 struct LevelDirInfo *getLevelDirInfoFromFilename(char *filename)
466 return getLevelDirInfoFromFilenameExt(leveldir_first, filename);
469 void dumpLevelDirInfo(struct LevelDirInfo *node, int depth)
475 for (i=0; i<depth * 3; i++)
478 printf("filename == '%s'\n", node->filename);
480 if (node->node_group != NULL)
481 dumpLevelDirInfo(node->node_group, depth + 1);
487 void sortLevelDirInfo(struct LevelDirInfo **node_first,
488 int (*compare_function)(const void *, const void *))
490 int num_nodes = numLevelDirInfo(*node_first);
491 struct LevelDirInfo **sort_array;
492 struct LevelDirInfo *node = *node_first;
498 /* allocate array for sorting structure pointers */
499 sort_array = checked_calloc(num_nodes * sizeof(struct LevelDirInfo *));
501 /* writing structure pointers to sorting array */
502 while (i < num_nodes && node) /* double boundary check... */
504 sort_array[i] = node;
510 /* sorting the structure pointers in the sorting array */
511 qsort(sort_array, num_nodes, sizeof(struct LevelDirInfo *),
514 /* update the linkage of list elements with the sorted node array */
515 for (i=0; i<num_nodes - 1; i++)
516 sort_array[i]->next = sort_array[i + 1];
517 sort_array[num_nodes - 1]->next = NULL;
519 /* update the linkage of the main list anchor pointer */
520 *node_first = sort_array[0];
524 /* now recursively sort the level group structures */
528 if (node->node_group != NULL)
529 sortLevelDirInfo(&node->node_group, compare_function);
536 /* ========================================================================= */
537 /* some stuff from "files.c" */
538 /* ========================================================================= */
540 #if defined(PLATFORM_WIN32)
542 #define S_IRGRP S_IRUSR
545 #define S_IROTH S_IRUSR
548 #define S_IWGRP S_IWUSR
551 #define S_IWOTH S_IWUSR
554 #define S_IXGRP S_IXUSR
557 #define S_IXOTH S_IXUSR
560 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
565 #endif /* PLATFORM_WIN32 */
567 /* file permissions for newly written files */
568 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
569 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
570 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
572 #define MODE_W_PRIVATE (S_IWUSR)
573 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
574 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
576 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
577 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
579 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
580 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
582 char *getUserDataDir(void)
584 static char *userdata_dir = NULL;
588 char *home_dir = getHomeDir();
589 char *data_dir = program.userdata_directory;
591 userdata_dir = getPath2(home_dir, data_dir);
599 return getUserDataDir();
602 static mode_t posix_umask(mode_t mask)
604 #if defined(PLATFORM_UNIX)
611 static int posix_mkdir(const char *pathname, mode_t mode)
613 #if defined(PLATFORM_WIN32)
614 return mkdir(pathname);
616 return mkdir(pathname, mode);
620 void createDirectory(char *dir, char *text, int permission_class)
622 /* leave "other" permissions in umask untouched, but ensure group parts
623 of USERDATA_DIR_MODE are not masked */
624 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
625 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
626 mode_t normal_umask = posix_umask(0);
627 mode_t group_umask = ~(dir_mode & S_IRWXG);
628 posix_umask(normal_umask & group_umask);
630 if (access(dir, F_OK) != 0)
631 if (posix_mkdir(dir, dir_mode) != 0)
632 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
634 posix_umask(normal_umask); /* reset normal umask */
637 void InitUserDataDirectory()
639 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
642 void SetFilePermissions(char *filename, int permission_class)
644 chmod(filename, (permission_class == PERMS_PRIVATE ?
645 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
648 int getFileVersionFromCookieString(const char *cookie)
650 const char *ptr_cookie1, *ptr_cookie2;
651 const char *pattern1 = "_FILE_VERSION_";
652 const char *pattern2 = "?.?";
653 const int len_cookie = strlen(cookie);
654 const int len_pattern1 = strlen(pattern1);
655 const int len_pattern2 = strlen(pattern2);
656 const int len_pattern = len_pattern1 + len_pattern2;
657 int version_major, version_minor;
659 if (len_cookie <= len_pattern)
662 ptr_cookie1 = &cookie[len_cookie - len_pattern];
663 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
665 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
668 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
669 ptr_cookie2[1] != '.' ||
670 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
673 version_major = ptr_cookie2[0] - '0';
674 version_minor = ptr_cookie2[2] - '0';
676 return VERSION_IDENT(version_major, version_minor, 0);
679 boolean checkCookieString(const char *cookie, const char *template)
681 const char *pattern = "_FILE_VERSION_?.?";
682 const int len_cookie = strlen(cookie);
683 const int len_template = strlen(template);
684 const int len_pattern = strlen(pattern);
686 if (len_cookie != len_template)
689 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
695 /* ------------------------------------------------------------------------- */
696 /* setup file list handling functions */
697 /* ------------------------------------------------------------------------- */
699 int get_string_integer_value(char *s)
701 static char *number_text[][3] =
703 { "0", "zero", "null", },
704 { "1", "one", "first" },
705 { "2", "two", "second" },
706 { "3", "three", "third" },
707 { "4", "four", "fourth" },
708 { "5", "five", "fifth" },
709 { "6", "six", "sixth" },
710 { "7", "seven", "seventh" },
711 { "8", "eight", "eighth" },
712 { "9", "nine", "ninth" },
713 { "10", "ten", "tenth" },
714 { "11", "eleven", "eleventh" },
715 { "12", "twelve", "twelfth" },
719 char *s_lower = getStringToLower(s);
724 if (strcmp(s_lower, number_text[i][j]) == 0)
735 boolean get_string_boolean_value(char *s)
737 char *s_lower = getStringToLower(s);
738 boolean result = FALSE;
740 if (strcmp(s_lower, "true") == 0 ||
741 strcmp(s_lower, "yes") == 0 ||
742 strcmp(s_lower, "on") == 0 ||
743 get_string_integer_value(s) == 1)
751 char *getFormattedSetupEntry(char *token, char *value)
754 static char entry[MAX_LINE_LEN];
756 sprintf(entry, "%s:", token);
757 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
761 strcat(entry, value);
766 void freeSetupFileList(struct SetupFileList *setup_file_list)
768 if (!setup_file_list)
771 if (setup_file_list->token)
772 free(setup_file_list->token);
773 if (setup_file_list->value)
774 free(setup_file_list->value);
775 if (setup_file_list->next)
776 freeSetupFileList(setup_file_list->next);
777 free(setup_file_list);
780 static struct SetupFileList *newSetupFileList(char *token, char *value)
782 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
784 new->token = checked_malloc(strlen(token) + 1);
785 strcpy(new->token, token);
787 new->value = checked_malloc(strlen(value) + 1);
788 strcpy(new->value, value);
795 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
797 if (!setup_file_list)
800 if (strcmp(setup_file_list->token, token) == 0)
801 return setup_file_list->value;
803 return getTokenValue(setup_file_list->next, token);
806 static void setTokenValue(struct SetupFileList *setup_file_list,
807 char *token, char *value)
809 if (!setup_file_list)
812 if (strcmp(setup_file_list->token, token) == 0)
814 free(setup_file_list->value);
815 setup_file_list->value = checked_malloc(strlen(value) + 1);
816 strcpy(setup_file_list->value, value);
818 else if (setup_file_list->next == NULL)
819 setup_file_list->next = newSetupFileList(token, value);
821 setTokenValue(setup_file_list->next, token, value);
825 static void printSetupFileList(struct SetupFileList *setup_file_list)
827 if (!setup_file_list)
830 printf("token: '%s'\n", setup_file_list->token);
831 printf("value: '%s'\n", setup_file_list->value);
833 printSetupFileList(setup_file_list->next);
837 struct SetupFileList *loadSetupFileList(char *filename)
840 char line[MAX_LINE_LEN];
841 char *token, *value, *line_ptr;
842 struct SetupFileList *setup_file_list = newSetupFileList("", "");
843 struct SetupFileList *first_valid_list_entry;
847 if (!(file = fopen(filename, MODE_READ)))
849 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
855 /* read next line of input file */
856 if (!fgets(line, MAX_LINE_LEN, file))
859 /* cut trailing comment or whitespace from input line */
860 for (line_ptr = line; *line_ptr; line_ptr++)
862 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
869 /* cut trailing whitespaces from input line */
870 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
871 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
874 /* ignore empty lines */
878 line_len = strlen(line);
880 /* cut leading whitespaces from token */
881 for (token = line; *token; token++)
882 if (*token != ' ' && *token != '\t')
885 /* find end of token */
886 for (line_ptr = token; *line_ptr; line_ptr++)
888 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
895 if (line_ptr < line + line_len)
896 value = line_ptr + 1;
900 /* cut leading whitespaces from value */
901 for (; *value; value++)
902 if (*value != ' ' && *value != '\t')
905 if (*token && *value)
906 setTokenValue(setup_file_list, token, value);
911 first_valid_list_entry = setup_file_list->next;
913 /* free empty list header */
914 setup_file_list->next = NULL;
915 freeSetupFileList(setup_file_list);
917 if (first_valid_list_entry == NULL)
918 Error(ERR_WARN, "configuration file '%s' is empty", filename);
920 return first_valid_list_entry;
923 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
926 if (!setup_file_list)
929 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
931 if (strcmp(setup_file_list->value, identifier) != 0)
933 Error(ERR_WARN, "configuration file has wrong version");
940 if (setup_file_list->next)
941 checkSetupFileListIdentifier(setup_file_list->next, identifier);
944 Error(ERR_WARN, "configuration file has no version information");
950 /* ========================================================================= */
951 /* setup file stuff */
952 /* ========================================================================= */
954 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
955 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
956 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
957 #define TOKEN_STR_PLAYER_PREFIX "player_"
960 #define SETUP_TOKEN_PLAYER_NAME 0
961 #define SETUP_TOKEN_SOUND 1
962 #define SETUP_TOKEN_SOUND_LOOPS 2
963 #define SETUP_TOKEN_SOUND_MUSIC 3
964 #define SETUP_TOKEN_SOUND_SIMPLE 4
965 #define SETUP_TOKEN_SCROLL_DELAY 5
966 #define SETUP_TOKEN_SOFT_SCROLLING 6
967 #define SETUP_TOKEN_FADING 7
968 #define SETUP_TOKEN_AUTORECORD 8
969 #define SETUP_TOKEN_QUICK_DOORS 9
970 #define SETUP_TOKEN_TEAM_MODE 10
971 #define SETUP_TOKEN_HANDICAP 11
972 #define SETUP_TOKEN_TIME_LIMIT 12
973 #define SETUP_TOKEN_FULLSCREEN 13
976 #define SETUP_TOKEN_USE_JOYSTICK 0
977 #define SETUP_TOKEN_JOY_DEVICE_NAME 1
978 #define SETUP_TOKEN_JOY_XLEFT 2
979 #define SETUP_TOKEN_JOY_XMIDDLE 3
980 #define SETUP_TOKEN_JOY_XRIGHT 4
981 #define SETUP_TOKEN_JOY_YUPPER 5
982 #define SETUP_TOKEN_JOY_YMIDDLE 6
983 #define SETUP_TOKEN_JOY_YLOWER 7
984 #define SETUP_TOKEN_JOY_SNAP 8
985 #define SETUP_TOKEN_JOY_BOMB 9
986 #define SETUP_TOKEN_KEY_LEFT 10
987 #define SETUP_TOKEN_KEY_RIGHT 11
988 #define SETUP_TOKEN_KEY_UP 12
989 #define SETUP_TOKEN_KEY_DOWN 13
990 #define SETUP_TOKEN_KEY_SNAP 14
991 #define SETUP_TOKEN_KEY_BOMB 15
993 /* level directory info */
994 #define LEVELINFO_TOKEN_NAME 0
995 #define LEVELINFO_TOKEN_NAME_SHORT 1
996 #define LEVELINFO_TOKEN_NAME_SORTING 2
997 #define LEVELINFO_TOKEN_AUTHOR 3
998 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
999 #define LEVELINFO_TOKEN_LEVELS 5
1000 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1001 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1002 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1003 #define LEVELINFO_TOKEN_READONLY 9
1005 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1006 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1008 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1009 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1011 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1012 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1014 static struct SetupInfo si;
1015 static struct SetupInputInfo sii;
1016 static struct LevelDirInfo ldi;
1017 static struct TokenInfo global_setup_tokens[] =
1020 { TYPE_STRING, &si.player_name, "player_name" },
1021 { TYPE_SWITCH, &si.sound, "sound" },
1022 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1023 { TYPE_SWITCH, &si.sound_music, "background_music" },
1024 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1025 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1026 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1027 { TYPE_SWITCH, &si.fading, "screen_fading" },
1028 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1029 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1030 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1031 { TYPE_SWITCH, &si.handicap, "handicap" },
1032 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1033 { TYPE_SWITCH, &si.fullscreen, "fullscreen" }
1036 static struct TokenInfo player_setup_tokens[] =
1039 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1040 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1041 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1042 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1043 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1044 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1045 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1046 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1047 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1048 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1049 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1050 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1051 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1052 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1053 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1054 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" }
1057 static struct TokenInfo levelinfo_tokens[] =
1059 /* level directory info */
1060 { TYPE_STRING, &ldi.name, "name" },
1061 { TYPE_STRING, &ldi.name_short, "name_short" },
1062 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1063 { TYPE_STRING, &ldi.author, "author" },
1064 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1065 { TYPE_INTEGER, &ldi.levels, "levels" },
1066 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1067 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1068 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1069 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1072 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1074 ldi->filename = NULL;
1075 ldi->fullpath = NULL;
1076 ldi->basepath = NULL;
1077 ldi->name = getStringCopy(ANONYMOUS_NAME);
1078 ldi->name_short = NULL;
1079 ldi->name_sorting = NULL;
1080 ldi->author = getStringCopy(ANONYMOUS_NAME);
1081 ldi->imported_from = NULL;
1083 ldi->first_level = 0;
1084 ldi->last_level = 0;
1085 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1086 ldi->level_group = FALSE;
1087 ldi->parent_link = FALSE;
1088 ldi->user_defined = FALSE;
1089 ldi->readonly = TRUE;
1091 ldi->class_desc = NULL;
1092 ldi->handicap_level = 0;
1094 ldi->cl_cursor = -1;
1096 ldi->node_parent = NULL;
1097 ldi->node_group = NULL;
1101 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1102 struct LevelDirInfo *parent)
1106 setLevelDirInfoToDefaults(ldi);
1110 /* first copy all values from the parent structure ... */
1113 /* ... then set all fields to default that cannot be inherited from parent.
1114 This is especially important for all those fields that can be set from
1115 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1116 calls 'free()' for all already set token values which requires that no
1117 other structure's pointer may point to them!
1120 ldi->filename = NULL;
1121 ldi->fullpath = NULL;
1122 ldi->basepath = NULL;
1123 ldi->name = getStringCopy(ANONYMOUS_NAME);
1124 ldi->name_short = NULL;
1125 ldi->name_sorting = NULL;
1126 ldi->author = getStringCopy(parent->author);
1127 ldi->imported_from = getStringCopy(parent->imported_from);
1129 ldi->level_group = FALSE;
1130 ldi->parent_link = FALSE;
1132 ldi->node_parent = parent;
1133 ldi->node_group = NULL;
1137 static void setSetupInfoToDefaults(struct SetupInfo *si)
1141 si->player_name = getStringCopy(getLoginName());
1144 si->sound_loops = TRUE;
1145 si->sound_music = TRUE;
1146 si->sound_simple = TRUE;
1148 si->double_buffering = TRUE;
1149 si->direct_draw = !si->double_buffering;
1150 si->scroll_delay = TRUE;
1151 si->soft_scrolling = TRUE;
1153 si->autorecord = TRUE;
1154 si->quick_doors = FALSE;
1155 si->team_mode = FALSE;
1156 si->handicap = TRUE;
1157 si->time_limit = TRUE;
1158 si->fullscreen = FALSE;
1160 for (i=0; i<MAX_PLAYERS; i++)
1162 si->input[i].use_joystick = FALSE;
1163 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1164 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1165 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1166 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1167 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1168 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1169 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1170 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1171 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1172 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1173 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1174 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1175 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1176 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1177 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1181 static void setSetupInfo(struct TokenInfo *token_info,
1182 int token_nr, char *token_value)
1184 int token_type = token_info[token_nr].type;
1185 void *setup_value = token_info[token_nr].value;
1187 if (token_value == NULL)
1190 /* set setup field to corresponding token value */
1195 *(boolean *)setup_value = get_string_boolean_value(token_value);
1199 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1203 *(int *)setup_value = get_string_integer_value(token_value);
1207 if (*(char **)setup_value != NULL)
1208 free(*(char **)setup_value);
1209 *(char **)setup_value = getStringCopy(token_value);
1217 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1221 if (!setup_file_list)
1224 /* handle global setup values */
1226 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1227 setSetupInfo(global_setup_tokens, i,
1228 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1231 /* handle player specific setup values */
1232 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1236 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1238 sii = setup.input[pnr];
1239 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1241 char full_token[100];
1243 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1244 setSetupInfo(player_setup_tokens, i,
1245 getTokenValue(setup_file_list, full_token));
1247 setup.input[pnr] = sii;
1251 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1253 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1254 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1257 if (entry1->parent_link || entry2->parent_link)
1258 compare_result = (entry1->parent_link ? -1 : +1);
1259 else if (entry1->sort_priority == entry2->sort_priority)
1261 char *name1 = getStringToLower(entry1->name_sorting);
1262 char *name2 = getStringToLower(entry2->name_sorting);
1264 compare_result = strcmp(name1, name2);
1269 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1270 compare_result = entry1->sort_priority - entry2->sort_priority;
1272 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1274 return compare_result;
1277 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1279 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1281 setLevelDirInfoToDefaults(leveldir_new);
1283 leveldir_new->node_parent = node_parent;
1284 leveldir_new->parent_link = TRUE;
1286 leveldir_new->name = ".. (parent directory)";
1287 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1288 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1290 leveldir_new->filename = "..";
1291 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1293 leveldir_new->sort_priority = node_parent->sort_priority;
1294 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1296 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1299 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1300 struct LevelDirInfo *node_parent,
1301 char *level_directory)
1304 struct dirent *dir_entry;
1305 boolean valid_entry_found = FALSE;
1307 if ((dir = opendir(level_directory)) == NULL)
1309 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1313 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1315 struct SetupFileList *setup_file_list = NULL;
1316 struct stat file_status;
1317 char *directory_name = dir_entry->d_name;
1318 char *directory_path = getPath2(level_directory, directory_name);
1319 char *filename = NULL;
1321 /* skip entries for current and parent directory */
1322 if (strcmp(directory_name, ".") == 0 ||
1323 strcmp(directory_name, "..") == 0)
1325 free(directory_path);
1329 /* find out if directory entry is itself a directory */
1330 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1331 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1333 free(directory_path);
1337 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1338 setup_file_list = loadSetupFileList(filename);
1340 if (setup_file_list)
1342 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1345 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1346 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1348 /* set all structure fields according to the token/value pairs */
1349 ldi = *leveldir_new;
1350 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1351 setSetupInfo(levelinfo_tokens, i,
1352 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1353 *leveldir_new = ldi;
1355 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1357 if (leveldir_new->name_short == NULL)
1358 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1360 if (leveldir_new->name_sorting == NULL)
1361 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1363 leveldir_new->filename = getStringCopy(directory_name);
1365 if (node_parent == NULL) /* top level group */
1367 leveldir_new->basepath = level_directory;
1368 leveldir_new->fullpath = leveldir_new->filename;
1370 else /* sub level group */
1372 leveldir_new->basepath = node_parent->basepath;
1373 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1377 if (leveldir_new->levels < 1)
1378 leveldir_new->levels = 1;
1380 leveldir_new->last_level =
1381 leveldir_new->first_level + leveldir_new->levels - 1;
1383 leveldir_new->user_defined =
1384 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1386 leveldir_new->color = LEVELCOLOR(leveldir_new);
1387 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1389 leveldir_new->handicap_level = /* set handicap to default value */
1390 (leveldir_new->user_defined ?
1391 leveldir_new->last_level :
1392 leveldir_new->first_level);
1394 pushLevelDirInfo(node_first, leveldir_new);
1396 freeSetupFileList(setup_file_list);
1397 valid_entry_found = TRUE;
1399 if (leveldir_new->level_group)
1401 /* create node to link back to current level directory */
1402 createParentLevelDirNode(leveldir_new);
1404 /* step into sub-directory and look for more level series */
1405 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1406 leveldir_new, directory_path);
1410 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1412 free(directory_path);
1418 if (!valid_entry_found)
1419 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1423 void LoadLevelInfo()
1425 InitUserLevelDirectory(getLoginName());
1427 DrawInitText("Loading level series:", 120, FC_GREEN);
1429 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1430 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1432 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1434 if (leveldir_first == NULL)
1435 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1437 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1440 dumpLevelDirInfo(leveldir_first, 0);
1444 static void SaveUserLevelInfo()
1450 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1452 if (!(file = fopen(filename, MODE_WRITE)))
1454 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1459 /* always start with reliable default values */
1460 setLevelDirInfoToDefaults(&ldi);
1462 ldi.name = getLoginName();
1463 ldi.author = getRealName();
1465 ldi.first_level = 1;
1466 ldi.sort_priority = LEVELCLASS_USER_START;
1467 ldi.readonly = FALSE;
1469 fprintf(file, "%s\n\n",
1470 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1472 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1473 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1474 i != LEVELINFO_TOKEN_NAME_SORTING &&
1475 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1476 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1481 SetFilePermissions(filename, PERMS_PRIVATE);
1487 struct SetupFileList *setup_file_list = NULL;
1489 /* always start with reliable default values */
1490 setSetupInfoToDefaults(&setup);
1492 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1494 setup_file_list = loadSetupFileList(filename);
1496 if (setup_file_list)
1498 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1499 decodeSetupFileList(setup_file_list);
1501 setup.direct_draw = !setup.double_buffering;
1503 freeSetupFileList(setup_file_list);
1505 /* needed to work around problems with fixed length strings */
1506 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1507 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1508 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1510 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1512 strcpy(new_name, setup.player_name);
1513 free(setup.player_name);
1514 setup.player_name = new_name;
1518 Error(ERR_WARN, "using default setup values");
1523 static char *getSetupLine(struct TokenInfo *token_info,
1524 char *prefix, int token_nr)
1527 static char entry[MAX_LINE_LEN];
1528 int token_type = token_info[token_nr].type;
1529 void *setup_value = token_info[token_nr].value;
1530 char *token_text = token_info[token_nr].text;
1532 /* start with the prefix, token and some spaces to format output line */
1533 sprintf(entry, "%s%s:", prefix, token_text);
1534 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1537 /* continue with the token's value (which can have different types) */
1541 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
1545 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
1550 Key key = *(Key *)setup_value;
1551 char *keyname = getKeyNameFromKey(key);
1553 strcat(entry, getX11KeyNameFromKey(key));
1554 for (i=strlen(entry); i<50; i++)
1557 /* add comment, if useful */
1558 if (strcmp(keyname, "(undefined)") != 0 &&
1559 strcmp(keyname, "(unknown)") != 0)
1561 strcat(entry, "# ");
1562 strcat(entry, keyname);
1569 char buffer[MAX_LINE_LEN];
1571 sprintf(buffer, "%d", *(int *)setup_value);
1572 strcat(entry, buffer);
1577 strcat(entry, *(char **)setup_value);
1593 InitUserDataDirectory();
1595 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1597 if (!(file = fopen(filename, MODE_WRITE)))
1599 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1604 fprintf(file, "%s\n",
1605 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
1606 fprintf(file, "\n");
1608 /* handle global setup values */
1610 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1612 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1614 /* just to make things nicer :) */
1615 if (i == SETUP_TOKEN_PLAYER_NAME)
1616 fprintf(file, "\n");
1619 /* handle player specific setup values */
1620 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1624 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1625 fprintf(file, "\n");
1627 sii = setup.input[pnr];
1628 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1629 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1635 SetFilePermissions(filename, PERMS_PRIVATE);
1638 void LoadLevelSetup_LastSeries()
1641 struct SetupFileList *level_setup_list = NULL;
1643 /* always start with reliable default values */
1644 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1646 /* ----------------------------------------------------------------------- */
1647 /* ~/.rocksndiamonds/levelsetup.conf */
1648 /* ----------------------------------------------------------------------- */
1650 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1652 if ((level_setup_list = loadSetupFileList(filename)))
1654 char *last_level_series =
1655 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1657 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
1658 if (leveldir_current == NULL)
1659 leveldir_current = leveldir_first;
1661 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1663 freeSetupFileList(level_setup_list);
1666 Error(ERR_WARN, "using default setup values");
1671 void SaveLevelSetup_LastSeries()
1674 char *level_subdir = leveldir_current->filename;
1677 /* ----------------------------------------------------------------------- */
1678 /* ~/.rocksndiamonds/levelsetup.conf */
1679 /* ----------------------------------------------------------------------- */
1681 InitUserDataDirectory();
1683 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1685 if (!(file = fopen(filename, MODE_WRITE)))
1687 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1692 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1693 LEVELSETUP_COOKIE));
1694 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1700 SetFilePermissions(filename, PERMS_PRIVATE);
1703 static void checkSeriesInfo()
1705 static char *level_directory = NULL;
1707 struct dirent *dir_entry;
1709 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1711 level_directory = getPath2((leveldir_current->user_defined ?
1712 getUserLevelDir("") :
1713 options.level_directory),
1714 leveldir_current->fullpath);
1716 if ((dir = opendir(level_directory)) == NULL)
1718 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1722 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1724 if (strlen(dir_entry->d_name) > 4 &&
1725 dir_entry->d_name[3] == '.' &&
1726 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1728 char levelnum_str[4];
1731 strncpy(levelnum_str, dir_entry->d_name, 3);
1732 levelnum_str[3] = '\0';
1734 levelnum_value = atoi(levelnum_str);
1736 if (levelnum_value < leveldir_current->first_level)
1738 Error(ERR_WARN, "additional level %d found", levelnum_value);
1739 leveldir_current->first_level = levelnum_value;
1741 else if (levelnum_value > leveldir_current->last_level)
1743 Error(ERR_WARN, "additional level %d found", levelnum_value);
1744 leveldir_current->last_level = levelnum_value;
1752 void LoadLevelSetup_SeriesInfo()
1755 struct SetupFileList *level_setup_list = NULL;
1756 char *level_subdir = leveldir_current->filename;
1758 /* always start with reliable default values */
1759 level_nr = leveldir_current->first_level;
1761 checkSeriesInfo(leveldir_current);
1763 /* ----------------------------------------------------------------------- */
1764 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
1765 /* ----------------------------------------------------------------------- */
1767 level_subdir = leveldir_current->filename;
1769 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1771 if ((level_setup_list = loadSetupFileList(filename)))
1775 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
1779 level_nr = atoi(token_value);
1781 if (level_nr < leveldir_current->first_level)
1782 level_nr = leveldir_current->first_level;
1783 if (level_nr > leveldir_current->last_level)
1784 level_nr = leveldir_current->last_level;
1787 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
1791 int level_nr = atoi(token_value);
1793 if (level_nr < leveldir_current->first_level)
1794 level_nr = leveldir_current->first_level;
1795 if (level_nr > leveldir_current->last_level + 1)
1796 level_nr = leveldir_current->last_level;
1798 if (leveldir_current->user_defined)
1799 level_nr = leveldir_current->last_level;
1801 leveldir_current->handicap_level = level_nr;
1804 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
1806 freeSetupFileList(level_setup_list);
1809 Error(ERR_WARN, "using default setup values");
1814 void SaveLevelSetup_SeriesInfo()
1817 char *level_subdir = leveldir_current->filename;
1818 char *level_nr_str = int2str(level_nr, 0);
1819 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
1822 /* ----------------------------------------------------------------------- */
1823 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
1824 /* ----------------------------------------------------------------------- */
1826 InitLevelSetupDirectory(level_subdir);
1828 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1830 if (!(file = fopen(filename, MODE_WRITE)))
1832 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1837 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1838 LEVELSETUP_COOKIE));
1839 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
1841 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
1842 handicap_level_str));
1847 SetFilePermissions(filename, PERMS_PRIVATE);