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 ***********************************************************/
24 /* file names and filename extensions */
25 #if !defined(PLATFORM_MSDOS)
26 #define LEVELSETUP_DIRECTORY "levelsetup"
27 #define SETUP_FILENAME "setup.conf"
28 #define LEVELSETUP_FILENAME "levelsetup.conf"
29 #define LEVELINFO_FILENAME "levelinfo.conf"
30 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
31 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
32 #define MUSICINFO_FILENAME "musicinfo.conf"
33 #define LEVELFILE_EXTENSION "level"
34 #define TAPEFILE_EXTENSION "tape"
35 #define SCOREFILE_EXTENSION "score"
37 #define LEVELSETUP_DIRECTORY "lvlsetup"
38 #define SETUP_FILENAME "setup.cnf"
39 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
40 #define LEVELINFO_FILENAME "lvlinfo.cnf"
41 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
42 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
43 #define MUSICINFO_FILENAME "musinfo.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 #define TOKEN_VALUE_POSITION 30
84 #define MAX_COOKIE_LEN 256
87 /* ------------------------------------------------------------------------- */
89 /* ------------------------------------------------------------------------- */
91 static char *getLevelClassDescription(TreeInfo *ldi)
93 int position = ldi->sort_priority / 100;
95 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
96 return levelclass_desc[position];
98 return "Unknown Level Class";
101 static char *getUserLevelDir(char *level_subdir)
103 static char *userlevel_dir = NULL;
104 char *data_dir = getUserDataDir();
105 char *userlevel_subdir = LEVELS_DIRECTORY;
110 if (level_subdir != NULL)
111 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
113 userlevel_dir = getPath2(data_dir, userlevel_subdir);
115 return userlevel_dir;
118 static char *getTapeDir(char *level_subdir)
120 static char *tape_dir = NULL;
121 char *data_dir = getUserDataDir();
122 char *tape_subdir = TAPES_DIRECTORY;
127 if (level_subdir != NULL)
128 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
130 tape_dir = getPath2(data_dir, tape_subdir);
135 static char *getScoreDir(char *level_subdir)
137 static char *score_dir = NULL;
138 char *data_dir = options.rw_base_directory;
139 char *score_subdir = SCORES_DIRECTORY;
144 if (level_subdir != NULL)
145 score_dir = getPath3(data_dir, score_subdir, level_subdir);
147 score_dir = getPath2(data_dir, score_subdir);
152 static char *getLevelSetupDir(char *level_subdir)
154 static char *levelsetup_dir = NULL;
155 char *data_dir = getUserDataDir();
156 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
159 free(levelsetup_dir);
161 if (level_subdir != NULL)
162 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
164 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
166 return levelsetup_dir;
169 static char *getUserGraphicsDir(char *graphics_subdir)
171 static char *usergraphics_dir = NULL;
172 char *data_dir = getUserDataDir();
173 char *usergraphics_subdir = GRAPHICS_DIRECTORY;
175 if (usergraphics_dir)
176 free(usergraphics_dir);
178 if (graphics_subdir != NULL)
179 usergraphics_dir = getPath3(data_dir, usergraphics_subdir,graphics_subdir);
181 usergraphics_dir = getPath2(data_dir, usergraphics_subdir);
183 return usergraphics_dir;
186 static char *getUserSoundsDir(char *sounds_subdir)
188 static char *usersounds_dir = NULL;
189 char *data_dir = getUserDataDir();
190 char *usersounds_subdir = SOUNDS_DIRECTORY;
193 free(usersounds_dir);
195 if (sounds_subdir != NULL)
196 usersounds_dir = getPath3(data_dir, usersounds_subdir,sounds_subdir);
198 usersounds_dir = getPath2(data_dir, usersounds_subdir);
200 return usersounds_dir;
203 static char *getUserMusicDir(char *music_subdir)
205 static char *usermusic_dir = NULL;
206 char *data_dir = getUserDataDir();
207 char *usermusic_subdir = MUSIC_DIRECTORY;
212 if (music_subdir != NULL)
213 usermusic_dir = getPath3(data_dir, usermusic_subdir,music_subdir);
215 usermusic_dir = getPath2(data_dir, usermusic_subdir);
217 return usermusic_dir;
220 char *getLevelFilename(int nr)
222 static char *filename = NULL;
223 char basename[MAX_FILENAME_LEN];
225 if (filename != NULL)
228 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
229 filename = getPath3((leveldir_current->user_defined ?
230 getUserLevelDir(NULL) :
231 options.level_directory),
232 leveldir_current->fullpath,
238 char *getTapeFilename(int nr)
240 static char *filename = NULL;
241 char basename[MAX_FILENAME_LEN];
243 if (filename != NULL)
246 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
247 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
252 char *getScoreFilename(int nr)
254 static char *filename = NULL;
255 char basename[MAX_FILENAME_LEN];
257 if (filename != NULL)
260 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
261 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
266 char *getSetupFilename()
268 static char *filename = NULL;
270 if (filename != NULL)
273 filename = getPath2(getSetupDir(), SETUP_FILENAME);
278 static char *getImageBasename(char *basename)
280 char *result = basename;
282 #if defined(PLATFORM_MSDOS)
283 if (program.filename_prefix != NULL)
285 int prefix_len = strlen(program.filename_prefix);
287 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
288 result = &basename[prefix_len];
295 char *getImageFilename(char *basename)
297 static char *filename = NULL;
299 if (filename != NULL)
302 filename = getPath2(options.graphics_directory, getImageBasename(basename));
307 char *getCustomImageFilename(char *basename)
310 if (strcmp(basename, "RocksFont.pcx") == 0)
312 char *dir = options.graphics_directory;
314 printf("checking directory '%s' ...\n", dir);
317 dir = getPath2(options.graphics_directory);
322 return getImageFilename(basename);
325 void InitTapeDirectory(char *level_subdir)
327 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
328 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
329 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
332 void InitScoreDirectory(char *level_subdir)
334 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
335 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
338 static void SaveUserLevelInfo();
340 void InitUserLevelDirectory(char *level_subdir)
342 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
344 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
345 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
346 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
352 void InitLevelSetupDirectory(char *level_subdir)
354 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
355 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
356 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
359 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
361 int file_version_major, file_version_minor, file_version_patch;
362 int game_version_major, game_version_minor, game_version_patch;
364 file_version_major = fgetc(file);
365 file_version_minor = fgetc(file);
366 file_version_patch = fgetc(file);
367 fgetc(file); /* not used */
369 game_version_major = fgetc(file);
370 game_version_minor = fgetc(file);
371 game_version_patch = fgetc(file);
372 fgetc(file); /* not used */
374 *file_version = VERSION_IDENT(file_version_major,
378 *game_version = VERSION_IDENT(game_version_major,
383 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
385 int file_version_major = VERSION_MAJOR(file_version);
386 int file_version_minor = VERSION_MINOR(file_version);
387 int file_version_patch = VERSION_PATCH(file_version);
388 int game_version_major = VERSION_MAJOR(game_version);
389 int game_version_minor = VERSION_MINOR(game_version);
390 int game_version_patch = VERSION_PATCH(game_version);
392 fputc(file_version_major, file);
393 fputc(file_version_minor, file);
394 fputc(file_version_patch, file);
395 fputc(0, file); /* not used */
397 fputc(game_version_major, file);
398 fputc(game_version_minor, file);
399 fputc(game_version_patch, file);
400 fputc(0, file); /* not used */
404 /* ------------------------------------------------------------------------- */
405 /* some functions to handle lists of level directories */
406 /* ------------------------------------------------------------------------- */
408 TreeInfo *newTreeInfo()
410 return checked_calloc(sizeof(TreeInfo));
413 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
415 node_new->next = *node_first;
416 *node_first = node_new;
419 int numTreeInfo(TreeInfo *node)
432 boolean validLevelSeries(TreeInfo *node)
434 return (node != NULL && !node->node_group && !node->parent_link);
437 TreeInfo *getFirstValidLevelSeries(TreeInfo *node)
441 if (leveldir_first) /* start with first level directory entry */
442 return getFirstValidLevelSeries(leveldir_first);
446 else if (node->node_group) /* enter level group (step down into tree) */
447 return getFirstValidLevelSeries(node->node_group);
448 else if (node->parent_link) /* skip start entry of level group */
450 if (node->next) /* get first real level series entry */
451 return getFirstValidLevelSeries(node->next);
452 else /* leave empty level group and go on */
453 return getFirstValidLevelSeries(node->node_parent->next);
455 else /* this seems to be a regular level series */
459 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
464 if (node->node_parent == NULL) /* top level group */
465 return leveldir_first;
466 else /* sub level group */
467 return node->node_parent->node_group;
470 int numTreeInfoInGroup(TreeInfo *node)
472 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
475 int posTreeInfo(TreeInfo *node)
477 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
482 if (node_cmp == node)
486 node_cmp = node_cmp->next;
492 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
494 TreeInfo *node_default = node;
509 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
511 if (filename == NULL)
516 if (node->node_group)
518 TreeInfo *node_group;
520 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
525 else if (!node->parent_link)
527 if (strcmp(filename, node->filename) == 0)
537 TreeInfo *getTreeInfoFromFilename(char *filename)
539 return getTreeInfoFromFilenameExt(leveldir_first, filename);
542 void dumpTreeInfo(TreeInfo *node, int depth)
548 for (i=0; i<depth * 3; i++)
551 printf("filename == '%s' [%s]\n", node->filename, node->name);
553 if (node->node_group != NULL)
554 dumpTreeInfo(node->node_group, depth + 1);
560 void sortTreeInfo(TreeInfo **node_first,
561 int (*compare_function)(const void *, const void *))
563 int num_nodes = numTreeInfo(*node_first);
564 TreeInfo **sort_array;
565 TreeInfo *node = *node_first;
571 /* allocate array for sorting structure pointers */
572 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
574 /* writing structure pointers to sorting array */
575 while (i < num_nodes && node) /* double boundary check... */
577 sort_array[i] = node;
583 /* sorting the structure pointers in the sorting array */
584 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
587 /* update the linkage of list elements with the sorted node array */
588 for (i=0; i<num_nodes - 1; i++)
589 sort_array[i]->next = sort_array[i + 1];
590 sort_array[num_nodes - 1]->next = NULL;
592 /* update the linkage of the main list anchor pointer */
593 *node_first = sort_array[0];
597 /* now recursively sort the level group structures */
601 if (node->node_group != NULL)
602 sortTreeInfo(&node->node_group, compare_function);
609 /* ========================================================================= */
610 /* some stuff from "files.c" */
611 /* ========================================================================= */
613 #if defined(PLATFORM_WIN32)
615 #define S_IRGRP S_IRUSR
618 #define S_IROTH S_IRUSR
621 #define S_IWGRP S_IWUSR
624 #define S_IWOTH S_IWUSR
627 #define S_IXGRP S_IXUSR
630 #define S_IXOTH S_IXUSR
633 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
638 #endif /* PLATFORM_WIN32 */
640 /* file permissions for newly written files */
641 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
642 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
643 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
645 #define MODE_W_PRIVATE (S_IWUSR)
646 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
647 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
649 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
650 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
652 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
653 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
655 char *getUserDataDir(void)
657 static char *userdata_dir = NULL;
661 char *home_dir = getHomeDir();
662 char *data_dir = program.userdata_directory;
664 userdata_dir = getPath2(home_dir, data_dir);
672 return getUserDataDir();
675 static mode_t posix_umask(mode_t mask)
677 #if defined(PLATFORM_UNIX)
684 static int posix_mkdir(const char *pathname, mode_t mode)
686 #if defined(PLATFORM_WIN32)
687 return mkdir(pathname);
689 return mkdir(pathname, mode);
693 void createDirectory(char *dir, char *text, int permission_class)
695 /* leave "other" permissions in umask untouched, but ensure group parts
696 of USERDATA_DIR_MODE are not masked */
697 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
698 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
699 mode_t normal_umask = posix_umask(0);
700 mode_t group_umask = ~(dir_mode & S_IRWXG);
701 posix_umask(normal_umask & group_umask);
703 if (access(dir, F_OK) != 0)
704 if (posix_mkdir(dir, dir_mode) != 0)
705 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
707 posix_umask(normal_umask); /* reset normal umask */
710 void InitUserDataDirectory()
712 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
715 void SetFilePermissions(char *filename, int permission_class)
717 chmod(filename, (permission_class == PERMS_PRIVATE ?
718 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
721 char *getCookie(char *file_type)
723 static char cookie[MAX_COOKIE_LEN + 1];
725 if (strlen(program.cookie_prefix) + 1 +
726 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
727 return "[COOKIE ERROR]"; /* should never happen */
729 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
730 program.cookie_prefix, file_type,
731 program.version_major, program.version_minor);
736 int getFileVersionFromCookieString(const char *cookie)
738 const char *ptr_cookie1, *ptr_cookie2;
739 const char *pattern1 = "_FILE_VERSION_";
740 const char *pattern2 = "?.?";
741 const int len_cookie = strlen(cookie);
742 const int len_pattern1 = strlen(pattern1);
743 const int len_pattern2 = strlen(pattern2);
744 const int len_pattern = len_pattern1 + len_pattern2;
745 int version_major, version_minor;
747 if (len_cookie <= len_pattern)
750 ptr_cookie1 = &cookie[len_cookie - len_pattern];
751 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
753 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
756 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
757 ptr_cookie2[1] != '.' ||
758 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
761 version_major = ptr_cookie2[0] - '0';
762 version_minor = ptr_cookie2[2] - '0';
764 return VERSION_IDENT(version_major, version_minor, 0);
767 boolean checkCookieString(const char *cookie, const char *template)
769 const char *pattern = "_FILE_VERSION_?.?";
770 const int len_cookie = strlen(cookie);
771 const int len_template = strlen(template);
772 const int len_pattern = strlen(pattern);
774 if (len_cookie != len_template)
777 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
783 /* ------------------------------------------------------------------------- */
784 /* setup file list handling functions */
785 /* ------------------------------------------------------------------------- */
787 int get_string_integer_value(char *s)
789 static char *number_text[][3] =
791 { "0", "zero", "null", },
792 { "1", "one", "first" },
793 { "2", "two", "second" },
794 { "3", "three", "third" },
795 { "4", "four", "fourth" },
796 { "5", "five", "fifth" },
797 { "6", "six", "sixth" },
798 { "7", "seven", "seventh" },
799 { "8", "eight", "eighth" },
800 { "9", "nine", "ninth" },
801 { "10", "ten", "tenth" },
802 { "11", "eleven", "eleventh" },
803 { "12", "twelve", "twelfth" },
807 char *s_lower = getStringToLower(s);
812 if (strcmp(s_lower, number_text[i][j]) == 0)
823 boolean get_string_boolean_value(char *s)
825 char *s_lower = getStringToLower(s);
826 boolean result = FALSE;
828 if (strcmp(s_lower, "true") == 0 ||
829 strcmp(s_lower, "yes") == 0 ||
830 strcmp(s_lower, "on") == 0 ||
831 get_string_integer_value(s) == 1)
839 char *getFormattedSetupEntry(char *token, char *value)
842 static char entry[MAX_LINE_LEN];
844 sprintf(entry, "%s:", token);
845 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
849 strcat(entry, value);
854 void freeSetupFileList(struct SetupFileList *setup_file_list)
856 if (!setup_file_list)
859 if (setup_file_list->token)
860 free(setup_file_list->token);
861 if (setup_file_list->value)
862 free(setup_file_list->value);
863 if (setup_file_list->next)
864 freeSetupFileList(setup_file_list->next);
865 free(setup_file_list);
868 static struct SetupFileList *newSetupFileList(char *token, char *value)
870 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
872 new->token = checked_malloc(strlen(token) + 1);
873 strcpy(new->token, token);
875 new->value = checked_malloc(strlen(value) + 1);
876 strcpy(new->value, value);
883 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
885 if (!setup_file_list)
888 if (strcmp(setup_file_list->token, token) == 0)
889 return setup_file_list->value;
891 return getTokenValue(setup_file_list->next, token);
894 static void setTokenValue(struct SetupFileList *setup_file_list,
895 char *token, char *value)
897 if (!setup_file_list)
900 if (strcmp(setup_file_list->token, token) == 0)
902 free(setup_file_list->value);
903 setup_file_list->value = checked_malloc(strlen(value) + 1);
904 strcpy(setup_file_list->value, value);
906 else if (setup_file_list->next == NULL)
907 setup_file_list->next = newSetupFileList(token, value);
909 setTokenValue(setup_file_list->next, token, value);
913 static void printSetupFileList(struct SetupFileList *setup_file_list)
915 if (!setup_file_list)
918 printf("token: '%s'\n", setup_file_list->token);
919 printf("value: '%s'\n", setup_file_list->value);
921 printSetupFileList(setup_file_list->next);
925 struct SetupFileList *loadSetupFileList(char *filename)
928 char line[MAX_LINE_LEN];
929 char *token, *value, *line_ptr;
930 struct SetupFileList *setup_file_list = newSetupFileList("", "");
931 struct SetupFileList *first_valid_list_entry;
935 if (!(file = fopen(filename, MODE_READ)))
937 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
943 /* read next line of input file */
944 if (!fgets(line, MAX_LINE_LEN, file))
947 /* cut trailing comment or whitespace from input line */
948 for (line_ptr = line; *line_ptr; line_ptr++)
950 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
957 /* cut trailing whitespaces from input line */
958 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
959 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
962 /* ignore empty lines */
966 line_len = strlen(line);
968 /* cut leading whitespaces from token */
969 for (token = line; *token; token++)
970 if (*token != ' ' && *token != '\t')
973 /* find end of token */
974 for (line_ptr = token; *line_ptr; line_ptr++)
976 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
983 if (line_ptr < line + line_len)
984 value = line_ptr + 1;
988 /* cut leading whitespaces from value */
989 for (; *value; value++)
990 if (*value != ' ' && *value != '\t')
993 if (*token && *value)
994 setTokenValue(setup_file_list, token, value);
999 first_valid_list_entry = setup_file_list->next;
1001 /* free empty list header */
1002 setup_file_list->next = NULL;
1003 freeSetupFileList(setup_file_list);
1005 if (first_valid_list_entry == NULL)
1006 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1008 return first_valid_list_entry;
1011 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1014 if (!setup_file_list)
1017 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1019 if (!checkCookieString(setup_file_list->value, identifier))
1021 Error(ERR_WARN, "configuration file has wrong file identifier");
1028 if (setup_file_list->next)
1029 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1032 Error(ERR_WARN, "configuration file has no file identifier");
1038 /* ========================================================================= */
1039 /* setup file stuff */
1040 /* ========================================================================= */
1042 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1043 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1044 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1046 /* level directory info */
1047 #define LEVELINFO_TOKEN_NAME 0
1048 #define LEVELINFO_TOKEN_NAME_SHORT 1
1049 #define LEVELINFO_TOKEN_NAME_SORTING 2
1050 #define LEVELINFO_TOKEN_AUTHOR 3
1051 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1052 #define LEVELINFO_TOKEN_LEVELS 5
1053 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1054 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1055 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1056 #define LEVELINFO_TOKEN_READONLY 9
1058 #define NUM_LEVELINFO_TOKENS 10
1060 static LevelDirTree ldi;
1062 static struct TokenInfo levelinfo_tokens[] =
1064 /* level directory info */
1065 { TYPE_STRING, &ldi.name, "name" },
1066 { TYPE_STRING, &ldi.name_short, "name_short" },
1067 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1068 { TYPE_STRING, &ldi.author, "author" },
1069 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1070 { TYPE_INTEGER, &ldi.levels, "levels" },
1071 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1072 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1073 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1074 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1077 static void setTreeInfoToDefaults(TreeInfo *ldi)
1079 ldi->node_parent = NULL;
1080 ldi->node_group = NULL;
1084 ldi->cl_cursor = -1;
1086 /* ldi->type is expected to be already set! */
1088 ldi->filename = NULL;
1089 ldi->fullpath = NULL;
1090 ldi->basepath = NULL;
1091 ldi->name = getStringCopy(ANONYMOUS_NAME);
1092 ldi->name_short = NULL;
1093 ldi->name_sorting = NULL;
1094 ldi->author = getStringCopy(ANONYMOUS_NAME);
1096 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1097 ldi->parent_link = FALSE;
1098 ldi->user_defined = FALSE;
1100 ldi->class_desc = NULL;
1102 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1104 ldi->imported_from = NULL;
1106 ldi->first_level = 0;
1107 ldi->last_level = 0;
1108 ldi->level_group = FALSE;
1109 ldi->handicap_level = 0;
1110 ldi->readonly = TRUE;
1114 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1118 setTreeInfoToDefaults(ldi);
1122 /* first copy all values from the parent structure ... */
1125 /* ... then set all fields to default that cannot be inherited from parent.
1126 This is especially important for all those fields that can be set from
1127 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1128 calls 'free()' for all already set token values which requires that no
1129 other structure's pointer may point to them!
1132 ldi->filename = NULL;
1133 ldi->fullpath = NULL;
1134 ldi->basepath = NULL;
1135 ldi->name = getStringCopy(ANONYMOUS_NAME);
1136 ldi->name_short = NULL;
1137 ldi->name_sorting = NULL;
1138 ldi->author = getStringCopy(parent->author);
1139 ldi->imported_from = getStringCopy(parent->imported_from);
1141 ldi->level_group = FALSE;
1142 ldi->parent_link = FALSE;
1144 ldi->node_parent = parent;
1145 ldi->node_group = NULL;
1149 void setSetupInfo(struct TokenInfo *token_info,
1150 int token_nr, char *token_value)
1152 int token_type = token_info[token_nr].type;
1153 void *setup_value = token_info[token_nr].value;
1155 if (token_value == NULL)
1158 /* set setup field to corresponding token value */
1163 *(boolean *)setup_value = get_string_boolean_value(token_value);
1167 *(Key *)setup_value = getKeyFromKeyName(token_value);
1171 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1175 *(int *)setup_value = get_string_integer_value(token_value);
1179 if (*(char **)setup_value != NULL)
1180 free(*(char **)setup_value);
1181 *(char **)setup_value = getStringCopy(token_value);
1189 static int compareTreeInfoEntries(const void *object1, const void *object2)
1191 const TreeInfo *entry1 = *((TreeInfo **)object1);
1192 const TreeInfo *entry2 = *((TreeInfo **)object2);
1195 if (entry1->parent_link || entry2->parent_link)
1196 compare_result = (entry1->parent_link ? -1 : +1);
1197 else if (entry1->sort_priority == entry2->sort_priority)
1199 char *name1 = getStringToLower(entry1->name_sorting);
1200 char *name2 = getStringToLower(entry2->name_sorting);
1202 compare_result = strcmp(name1, name2);
1207 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1208 compare_result = entry1->sort_priority - entry2->sort_priority;
1210 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1212 return compare_result;
1215 static void createParentTreeInfoNode(TreeInfo *node_parent)
1219 if (node_parent == NULL)
1222 ti_new = newTreeInfo();
1223 ti_new->type = node_parent->type;
1225 setTreeInfoToDefaults(ti_new);
1227 ti_new->node_parent = node_parent;
1228 ti_new->parent_link = TRUE;
1230 ti_new->name = ".. (parent directory)";
1231 ti_new->name_short = getStringCopy(ti_new->name);
1232 ti_new->name_sorting = getStringCopy(ti_new->name);
1234 ti_new->filename = "..";
1235 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1237 ti_new->sort_priority = node_parent->sort_priority;
1238 ti_new->class_desc = getLevelClassDescription(ti_new);
1240 pushTreeInfo(&node_parent->node_group, ti_new);
1243 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1244 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1246 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1247 TreeInfo *node_parent,
1248 char *level_directory,
1249 char *directory_name)
1251 char *directory_path = getPath2(level_directory, directory_name);
1252 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1253 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1254 LevelDirTree *leveldir_new = NULL;
1257 if (setup_file_list == NULL)
1259 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1261 free(directory_path);
1267 leveldir_new = newTreeInfo();
1268 leveldir_new->type = TREE_TYPE_LEVEL_DIR;
1270 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1271 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1273 /* set all structure fields according to the token/value pairs */
1274 ldi = *leveldir_new;
1275 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1276 setSetupInfo(levelinfo_tokens, i,
1277 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1278 *leveldir_new = ldi;
1280 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1282 if (leveldir_new->name_short == NULL)
1283 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1285 if (leveldir_new->name_sorting == NULL)
1286 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1288 leveldir_new->filename = getStringCopy(directory_name);
1290 if (node_parent == NULL) /* top level group */
1292 leveldir_new->basepath = level_directory;
1293 leveldir_new->fullpath = leveldir_new->filename;
1295 else /* sub level group */
1297 leveldir_new->basepath = node_parent->basepath;
1298 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1301 if (leveldir_new->levels < 1)
1302 leveldir_new->levels = 1;
1304 leveldir_new->last_level =
1305 leveldir_new->first_level + leveldir_new->levels - 1;
1307 leveldir_new->user_defined =
1308 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1310 leveldir_new->color = LEVELCOLOR(leveldir_new);
1311 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1313 leveldir_new->handicap_level = /* set handicap to default value */
1314 (leveldir_new->user_defined ?
1315 leveldir_new->last_level :
1316 leveldir_new->first_level);
1318 pushTreeInfo(node_first, leveldir_new);
1320 freeSetupFileList(setup_file_list);
1322 if (leveldir_new->level_group)
1324 /* create node to link back to current level directory */
1325 createParentTreeInfoNode(leveldir_new);
1327 /* step into sub-directory and look for more level series */
1328 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1329 leveldir_new, directory_path);
1332 free(directory_path);
1338 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1339 TreeInfo *node_parent,
1340 char *level_directory)
1343 struct dirent *dir_entry;
1344 boolean valid_entry_found = FALSE;
1346 if ((dir = opendir(level_directory)) == NULL)
1348 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1352 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1354 struct stat file_status;
1355 char *directory_name = dir_entry->d_name;
1356 char *directory_path = getPath2(level_directory, directory_name);
1358 /* skip entries for current and parent directory */
1359 if (strcmp(directory_name, ".") == 0 ||
1360 strcmp(directory_name, "..") == 0)
1362 free(directory_path);
1366 /* find out if directory entry is itself a directory */
1367 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1368 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1370 free(directory_path);
1374 free(directory_path);
1376 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1383 if (!valid_entry_found)
1385 /* check if this directory directly contains a file "levelinfo.conf" */
1386 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1387 level_directory, ".");
1390 if (!valid_entry_found)
1391 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1395 void LoadLevelInfo()
1397 InitUserLevelDirectory(getLoginName());
1399 DrawInitText("Loading level series:", 120, FC_GREEN);
1401 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1402 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1404 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1406 if (leveldir_first == NULL)
1407 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1409 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1412 dumpTreeInfo(leveldir_first, 0);
1416 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1417 TreeInfo *node_parent,
1418 char *base_directory,
1419 char *directory_name, int type)
1421 char *directory_path = getPath2(base_directory, directory_name);
1423 getPath2(directory_path,
1424 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1425 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1426 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1427 struct SetupFileList *setup_file_list = NULL;
1428 TreeInfo *artwork_new = NULL;
1429 char *check_dir = NULL;
1432 if (access(getUserLevelDir(filename), F_OK) == 0) /* file exists */
1433 loadSetupFileList(filename);
1435 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1438 struct dirent *dir_entry;
1439 boolean valid_file_found = FALSE;
1441 if ((dir = opendir(base_directory)) != NULL)
1443 while ((dir_entry = readdir(dir)) != NULL)
1445 char *entry_name = dir_entry->d_name;
1447 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1448 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1449 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1451 valid_file_found = TRUE;
1459 if (!valid_file_found)
1461 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1463 free(directory_path);
1470 artwork_new = newTreeInfo();
1471 artwork_new->type = type;
1473 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1475 artwork_new->filename = getStringCopy(directory_name);
1477 if (setup_file_list)
1480 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1483 /* set all structure fields according to the token/value pairs */
1485 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1486 setSetupInfo(levelinfo_tokens, i,
1487 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1490 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1492 if (artwork_new->name_short == NULL)
1493 artwork_new->name_short = getStringCopy(artwork_new->name);
1495 if (artwork_new->name_sorting == NULL)
1496 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1500 if (artwork_new->name != NULL)
1501 free(artwork_new->name);
1503 if (strcmp(artwork_new->filename, ".") == 0)
1504 artwork_new->name = getStringCopy("default");
1506 artwork_new->name = getStringCopy(artwork_new->filename);
1508 artwork_new->name_short = getStringCopy(artwork_new->name);
1509 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1512 if (node_parent == NULL) /* top level group */
1514 artwork_new->basepath = base_directory;
1515 artwork_new->fullpath = artwork_new->filename;
1517 else /* sub level group */
1519 artwork_new->basepath = node_parent->basepath;
1520 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1523 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1524 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1525 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1526 artwork_new->user_defined =
1527 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1530 artwork_new->color = LEVELCOLOR(artwork_new);
1531 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1534 pushTreeInfo(node_first, artwork_new);
1536 freeSetupFileList(setup_file_list);
1538 free(directory_path);
1544 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1545 TreeInfo *node_parent,
1546 char *base_directory, int type)
1549 struct dirent *dir_entry;
1550 boolean valid_entry_found = FALSE;
1552 if ((dir = opendir(base_directory)) == NULL)
1554 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1555 base_directory == options.graphics_directory) ||
1556 (type == TREE_TYPE_SOUNDS_DIR &&
1557 base_directory == options.sounds_directory) ||
1558 (type == TREE_TYPE_MUSIC_DIR &&
1559 base_directory == options.music_directory))
1560 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1564 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1566 struct stat file_status;
1567 char *directory_name = dir_entry->d_name;
1568 char *directory_path = getPath2(base_directory, directory_name);
1570 /* skip entries for current and parent directory */
1571 if (strcmp(directory_name, ".") == 0 ||
1572 strcmp(directory_name, "..") == 0)
1574 free(directory_path);
1578 /* find out if directory entry is itself a directory */
1579 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1580 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1582 free(directory_path);
1586 free(directory_path);
1588 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1590 directory_name, type);
1595 if (!valid_entry_found)
1597 /* check if this directory directly contains an artwork config file */
1598 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1599 base_directory, ".",
1603 if (!valid_entry_found)
1604 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1608 void LoadArtworkInfo()
1610 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1612 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1613 options.graphics_directory,
1614 TREE_TYPE_GRAPHICS_DIR);
1615 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1616 getUserGraphicsDir(NULL),
1617 TREE_TYPE_GRAPHICS_DIR);
1619 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1620 options.sounds_directory,
1621 TREE_TYPE_SOUNDS_DIR);
1622 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1623 getUserSoundsDir(NULL),
1624 TREE_TYPE_SOUNDS_DIR);
1626 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1627 options.music_directory,
1628 TREE_TYPE_MUSIC_DIR);
1629 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1630 getUserMusicDir(NULL),
1631 TREE_TYPE_MUSIC_DIR);
1633 artwork.gfx_current = artwork.gfx_first;
1634 artwork.snd_current = artwork.snd_first;
1635 artwork.mus_current = artwork.mus_first;
1637 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1638 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1639 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1642 dumpTreeInfo(artwork.gfx_first, 0);
1643 dumpTreeInfo(artwork.snd_first, 0);
1644 dumpTreeInfo(artwork.mus_first, 0);
1648 static void SaveUserLevelInfo()
1654 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1656 if (!(file = fopen(filename, MODE_WRITE)))
1658 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1663 /* always start with reliable default values */
1664 setTreeInfoToDefaults(&ldi);
1666 ldi.name = getLoginName();
1667 ldi.author = getRealName();
1669 ldi.first_level = 1;
1670 ldi.sort_priority = LEVELCLASS_USER_START;
1671 ldi.readonly = FALSE;
1673 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1674 getCookie("LEVELINFO")));
1676 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1677 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1678 i != LEVELINFO_TOKEN_NAME_SORTING &&
1679 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1680 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1685 SetFilePermissions(filename, PERMS_PRIVATE);
1688 char *getSetupValue(int type, void *value)
1690 static char value_string[MAX_LINE_LEN];
1695 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1699 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1703 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1707 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1711 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1715 sprintf(value_string, "%d", *(int *)value);
1719 strcpy(value_string, *(char **)value);
1723 value_string[0] = '\0';
1727 return value_string;
1730 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1733 static char entry[MAX_LINE_LEN];
1734 int token_type = token_info[token_nr].type;
1735 void *setup_value = token_info[token_nr].value;
1736 char *token_text = token_info[token_nr].text;
1737 char *value_string = getSetupValue(token_type, setup_value);
1739 /* start with the prefix, token and some spaces to format output line */
1740 sprintf(entry, "%s%s:", prefix, token_text);
1741 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1744 /* continue with the token's value (which can have different types) */
1745 strcat(entry, value_string);
1747 if (token_type == TYPE_KEY_X11)
1749 Key key = *(Key *)setup_value;
1750 char *keyname = getKeyNameFromKey(key);
1752 /* add comment, if useful */
1753 if (strcmp(keyname, "(undefined)") != 0 &&
1754 strcmp(keyname, "(unknown)") != 0)
1756 for (i=strlen(entry); i<50; i++)
1759 strcat(entry, "# ");
1760 strcat(entry, keyname);
1767 void LoadLevelSetup_LastSeries()
1770 struct SetupFileList *level_setup_list = NULL;
1772 /* always start with reliable default values */
1773 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1775 /* ----------------------------------------------------------------------- */
1776 /* ~/.<program>/levelsetup.conf */
1777 /* ----------------------------------------------------------------------- */
1779 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1781 if ((level_setup_list = loadSetupFileList(filename)))
1783 char *last_level_series =
1784 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1786 leveldir_current = getTreeInfoFromFilename(last_level_series);
1787 if (leveldir_current == NULL)
1788 leveldir_current = leveldir_first;
1790 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1792 freeSetupFileList(level_setup_list);
1795 Error(ERR_WARN, "using default setup values");
1800 void SaveLevelSetup_LastSeries()
1803 char *level_subdir = leveldir_current->filename;
1806 /* ----------------------------------------------------------------------- */
1807 /* ~/.<program>/levelsetup.conf */
1808 /* ----------------------------------------------------------------------- */
1810 InitUserDataDirectory();
1812 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1814 if (!(file = fopen(filename, MODE_WRITE)))
1816 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1821 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1822 getCookie("LEVELSETUP")));
1823 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1829 SetFilePermissions(filename, PERMS_PRIVATE);
1832 static void checkSeriesInfo()
1834 static char *level_directory = NULL;
1836 struct dirent *dir_entry;
1838 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1840 level_directory = getPath2((leveldir_current->user_defined ?
1841 getUserLevelDir(NULL) :
1842 options.level_directory),
1843 leveldir_current->fullpath);
1845 if ((dir = opendir(level_directory)) == NULL)
1847 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1851 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1853 if (strlen(dir_entry->d_name) > 4 &&
1854 dir_entry->d_name[3] == '.' &&
1855 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1857 char levelnum_str[4];
1860 strncpy(levelnum_str, dir_entry->d_name, 3);
1861 levelnum_str[3] = '\0';
1863 levelnum_value = atoi(levelnum_str);
1865 if (levelnum_value < leveldir_current->first_level)
1867 Error(ERR_WARN, "additional level %d found", levelnum_value);
1868 leveldir_current->first_level = levelnum_value;
1870 else if (levelnum_value > leveldir_current->last_level)
1872 Error(ERR_WARN, "additional level %d found", levelnum_value);
1873 leveldir_current->last_level = levelnum_value;
1881 void LoadLevelSetup_SeriesInfo()
1884 struct SetupFileList *level_setup_list = NULL;
1885 char *level_subdir = leveldir_current->filename;
1887 /* always start with reliable default values */
1888 level_nr = leveldir_current->first_level;
1890 checkSeriesInfo(leveldir_current);
1892 /* ----------------------------------------------------------------------- */
1893 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1894 /* ----------------------------------------------------------------------- */
1896 level_subdir = leveldir_current->filename;
1898 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1900 if ((level_setup_list = loadSetupFileList(filename)))
1904 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
1908 level_nr = atoi(token_value);
1910 if (level_nr < leveldir_current->first_level)
1911 level_nr = leveldir_current->first_level;
1912 if (level_nr > leveldir_current->last_level)
1913 level_nr = leveldir_current->last_level;
1916 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
1920 int level_nr = atoi(token_value);
1922 if (level_nr < leveldir_current->first_level)
1923 level_nr = leveldir_current->first_level;
1924 if (level_nr > leveldir_current->last_level + 1)
1925 level_nr = leveldir_current->last_level;
1927 if (leveldir_current->user_defined)
1928 level_nr = leveldir_current->last_level;
1930 leveldir_current->handicap_level = level_nr;
1933 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1935 freeSetupFileList(level_setup_list);
1938 Error(ERR_WARN, "using default setup values");
1943 void SaveLevelSetup_SeriesInfo()
1946 char *level_subdir = leveldir_current->filename;
1947 char *level_nr_str = int2str(level_nr, 0);
1948 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
1951 /* ----------------------------------------------------------------------- */
1952 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1953 /* ----------------------------------------------------------------------- */
1955 InitLevelSetupDirectory(level_subdir);
1957 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1959 if (!(file = fopen(filename, MODE_WRITE)))
1961 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1966 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1967 getCookie("LEVELSETUP")));
1968 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
1970 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
1971 handicap_level_str));
1976 SetFilePermissions(filename, PERMS_PRIVATE);