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 *node->node_top;
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(TreeInfo *ti, char *filename)
539 return getTreeInfoFromFilenameExt(ti, 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->type is expected to be already set! */
1082 Error(ERR_EXIT, "ldi->type == 0");
1084 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1085 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1086 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1087 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1090 ldi->node_parent = NULL;
1091 ldi->node_group = NULL;
1095 ldi->cl_cursor = -1;
1097 ldi->filename = NULL;
1098 ldi->fullpath = NULL;
1099 ldi->basepath = NULL;
1100 ldi->name = getStringCopy(ANONYMOUS_NAME);
1101 ldi->name_short = NULL;
1102 ldi->name_sorting = NULL;
1103 ldi->author = getStringCopy(ANONYMOUS_NAME);
1105 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1106 ldi->parent_link = FALSE;
1107 ldi->user_defined = FALSE;
1109 ldi->class_desc = NULL;
1111 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1113 ldi->imported_from = NULL;
1115 ldi->first_level = 0;
1116 ldi->last_level = 0;
1117 ldi->level_group = FALSE;
1118 ldi->handicap_level = 0;
1119 ldi->readonly = TRUE;
1123 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1127 setTreeInfoToDefaults(ldi);
1131 /* first copy all values from the parent structure ... */
1134 /* ... then set all fields to default that cannot be inherited from parent.
1135 This is especially important for all those fields that can be set from
1136 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1137 calls 'free()' for all already set token values which requires that no
1138 other structure's pointer may point to them!
1141 ldi->filename = NULL;
1142 ldi->fullpath = NULL;
1143 ldi->basepath = NULL;
1144 ldi->name = getStringCopy(ANONYMOUS_NAME);
1145 ldi->name_short = NULL;
1146 ldi->name_sorting = NULL;
1147 ldi->author = getStringCopy(parent->author);
1148 ldi->imported_from = getStringCopy(parent->imported_from);
1150 ldi->level_group = FALSE;
1151 ldi->parent_link = FALSE;
1153 ldi->node_top = parent->node_top;
1154 ldi->node_parent = parent;
1155 ldi->node_group = NULL;
1159 void setSetupInfo(struct TokenInfo *token_info,
1160 int token_nr, char *token_value)
1162 int token_type = token_info[token_nr].type;
1163 void *setup_value = token_info[token_nr].value;
1165 if (token_value == NULL)
1168 /* set setup field to corresponding token value */
1173 *(boolean *)setup_value = get_string_boolean_value(token_value);
1177 *(Key *)setup_value = getKeyFromKeyName(token_value);
1181 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1185 *(int *)setup_value = get_string_integer_value(token_value);
1189 if (*(char **)setup_value != NULL)
1190 free(*(char **)setup_value);
1191 *(char **)setup_value = getStringCopy(token_value);
1199 static int compareTreeInfoEntries(const void *object1, const void *object2)
1201 const TreeInfo *entry1 = *((TreeInfo **)object1);
1202 const TreeInfo *entry2 = *((TreeInfo **)object2);
1205 if (entry1->parent_link || entry2->parent_link)
1206 compare_result = (entry1->parent_link ? -1 : +1);
1207 else if (entry1->sort_priority == entry2->sort_priority)
1209 char *name1 = getStringToLower(entry1->name_sorting);
1210 char *name2 = getStringToLower(entry2->name_sorting);
1212 compare_result = strcmp(name1, name2);
1217 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1218 compare_result = entry1->sort_priority - entry2->sort_priority;
1220 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1222 return compare_result;
1225 static void createParentTreeInfoNode(TreeInfo *node_parent)
1229 if (node_parent == NULL)
1232 ti_new = newTreeInfo();
1233 ti_new->type = node_parent->type;
1235 setTreeInfoToDefaults(ti_new);
1237 ti_new->node_parent = node_parent;
1238 ti_new->parent_link = TRUE;
1240 ti_new->name = ".. (parent directory)";
1241 ti_new->name_short = getStringCopy(ti_new->name);
1242 ti_new->name_sorting = getStringCopy(ti_new->name);
1244 ti_new->filename = "..";
1245 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1247 ti_new->sort_priority = node_parent->sort_priority;
1248 ti_new->class_desc = getLevelClassDescription(ti_new);
1250 pushTreeInfo(&node_parent->node_group, ti_new);
1253 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1254 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1256 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1257 TreeInfo *node_parent,
1258 char *level_directory,
1259 char *directory_name)
1261 char *directory_path = getPath2(level_directory, directory_name);
1262 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1263 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1264 LevelDirTree *leveldir_new = NULL;
1267 if (setup_file_list == NULL)
1269 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1271 free(directory_path);
1277 leveldir_new = newTreeInfo();
1278 leveldir_new->type = TREE_TYPE_LEVEL_DIR;
1280 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1281 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1283 /* set all structure fields according to the token/value pairs */
1284 ldi = *leveldir_new;
1285 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1286 setSetupInfo(levelinfo_tokens, i,
1287 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1288 *leveldir_new = ldi;
1290 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1292 if (leveldir_new->name_short == NULL)
1293 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1295 if (leveldir_new->name_sorting == NULL)
1296 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1298 leveldir_new->filename = getStringCopy(directory_name);
1300 if (node_parent == NULL) /* top level group */
1302 leveldir_new->basepath = level_directory;
1303 leveldir_new->fullpath = leveldir_new->filename;
1305 else /* sub level group */
1307 leveldir_new->basepath = node_parent->basepath;
1308 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1311 if (leveldir_new->levels < 1)
1312 leveldir_new->levels = 1;
1314 leveldir_new->last_level =
1315 leveldir_new->first_level + leveldir_new->levels - 1;
1317 leveldir_new->user_defined =
1318 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1320 leveldir_new->color = LEVELCOLOR(leveldir_new);
1321 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1323 leveldir_new->handicap_level = /* set handicap to default value */
1324 (leveldir_new->user_defined ?
1325 leveldir_new->last_level :
1326 leveldir_new->first_level);
1328 pushTreeInfo(node_first, leveldir_new);
1330 freeSetupFileList(setup_file_list);
1332 if (leveldir_new->level_group)
1334 /* create node to link back to current level directory */
1335 createParentTreeInfoNode(leveldir_new);
1337 /* step into sub-directory and look for more level series */
1338 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1339 leveldir_new, directory_path);
1342 free(directory_path);
1348 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1349 TreeInfo *node_parent,
1350 char *level_directory)
1353 struct dirent *dir_entry;
1354 boolean valid_entry_found = FALSE;
1356 if ((dir = opendir(level_directory)) == NULL)
1358 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1362 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1364 struct stat file_status;
1365 char *directory_name = dir_entry->d_name;
1366 char *directory_path = getPath2(level_directory, directory_name);
1368 /* skip entries for current and parent directory */
1369 if (strcmp(directory_name, ".") == 0 ||
1370 strcmp(directory_name, "..") == 0)
1372 free(directory_path);
1376 /* find out if directory entry is itself a directory */
1377 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1378 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1380 free(directory_path);
1384 free(directory_path);
1386 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1393 if (!valid_entry_found)
1395 /* check if this directory directly contains a file "levelinfo.conf" */
1396 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1397 level_directory, ".");
1400 if (!valid_entry_found)
1401 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1405 void LoadLevelInfo()
1407 InitUserLevelDirectory(getLoginName());
1409 DrawInitText("Loading level series:", 120, FC_GREEN);
1411 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1412 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1414 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1416 if (leveldir_first == NULL)
1417 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1419 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1422 dumpTreeInfo(leveldir_first, 0);
1426 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1427 TreeInfo *node_parent,
1428 char *base_directory,
1429 char *directory_name, int type)
1431 char *directory_path = getPath2(base_directory, directory_name);
1433 getPath2(directory_path,
1434 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1435 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1436 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1437 struct SetupFileList *setup_file_list = NULL;
1438 TreeInfo *artwork_new = NULL;
1439 char *check_dir = NULL;
1442 if (access(getUserLevelDir(filename), F_OK) == 0) /* file exists */
1443 loadSetupFileList(filename);
1445 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1448 struct dirent *dir_entry;
1449 boolean valid_file_found = FALSE;
1451 if ((dir = opendir(base_directory)) != NULL)
1453 while ((dir_entry = readdir(dir)) != NULL)
1455 char *entry_name = dir_entry->d_name;
1457 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1458 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1459 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1461 valid_file_found = TRUE;
1469 if (!valid_file_found)
1471 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1473 free(directory_path);
1480 artwork_new = newTreeInfo();
1481 artwork_new->type = type;
1483 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1485 artwork_new->filename = getStringCopy(directory_name);
1487 if (setup_file_list)
1490 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1493 /* set all structure fields according to the token/value pairs */
1495 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1496 setSetupInfo(levelinfo_tokens, i,
1497 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1500 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1502 if (artwork_new->name_short == NULL)
1503 artwork_new->name_short = getStringCopy(artwork_new->name);
1505 if (artwork_new->name_sorting == NULL)
1506 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1510 if (artwork_new->name != NULL)
1511 free(artwork_new->name);
1513 if (strcmp(artwork_new->filename, ".") == 0)
1514 artwork_new->name = getStringCopy("default");
1516 artwork_new->name = getStringCopy(artwork_new->filename);
1518 artwork_new->name_short = getStringCopy(artwork_new->name);
1519 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1522 if (node_parent == NULL) /* top level group */
1524 artwork_new->basepath = base_directory;
1525 artwork_new->fullpath = artwork_new->filename;
1527 else /* sub level group */
1529 artwork_new->basepath = node_parent->basepath;
1530 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1533 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1534 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1535 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1536 artwork_new->user_defined =
1537 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1540 artwork_new->color = LEVELCOLOR(artwork_new);
1541 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1544 pushTreeInfo(node_first, artwork_new);
1546 freeSetupFileList(setup_file_list);
1548 free(directory_path);
1554 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1555 TreeInfo *node_parent,
1556 char *base_directory, int type)
1559 struct dirent *dir_entry;
1560 boolean valid_entry_found = FALSE;
1562 if ((dir = opendir(base_directory)) == NULL)
1564 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1565 base_directory == options.graphics_directory) ||
1566 (type == TREE_TYPE_SOUNDS_DIR &&
1567 base_directory == options.sounds_directory) ||
1568 (type == TREE_TYPE_MUSIC_DIR &&
1569 base_directory == options.music_directory))
1570 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1574 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1576 struct stat file_status;
1577 char *directory_name = dir_entry->d_name;
1578 char *directory_path = getPath2(base_directory, directory_name);
1580 /* skip entries for current and parent directory */
1581 if (strcmp(directory_name, ".") == 0 ||
1582 strcmp(directory_name, "..") == 0)
1584 free(directory_path);
1588 /* find out if directory entry is itself a directory */
1589 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1590 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1592 free(directory_path);
1596 free(directory_path);
1598 /* check if this directory contains artwork with or without config file */
1599 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1601 directory_name, type);
1606 /* check if this directory directly contains artwork itself */
1607 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1608 base_directory, ".",
1610 if (!valid_entry_found)
1611 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1615 void LoadArtworkInfo()
1617 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1619 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1620 options.graphics_directory,
1621 TREE_TYPE_GRAPHICS_DIR);
1622 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1623 getUserGraphicsDir(NULL),
1624 TREE_TYPE_GRAPHICS_DIR);
1626 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1627 options.sounds_directory,
1628 TREE_TYPE_SOUNDS_DIR);
1629 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1630 getUserSoundsDir(NULL),
1631 TREE_TYPE_SOUNDS_DIR);
1633 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1634 options.music_directory,
1635 TREE_TYPE_MUSIC_DIR);
1636 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1637 getUserMusicDir(NULL),
1638 TREE_TYPE_MUSIC_DIR);
1640 artwork.gfx_current = artwork.gfx_first;
1641 artwork.snd_current = artwork.snd_first;
1642 artwork.mus_current = artwork.mus_first;
1644 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1645 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1646 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1649 dumpTreeInfo(artwork.gfx_first, 0);
1650 dumpTreeInfo(artwork.snd_first, 0);
1651 dumpTreeInfo(artwork.mus_first, 0);
1655 static void SaveUserLevelInfo()
1661 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1663 if (!(file = fopen(filename, MODE_WRITE)))
1665 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1670 /* always start with reliable default values */
1671 setTreeInfoToDefaults(&ldi);
1673 ldi.name = getLoginName();
1674 ldi.author = getRealName();
1676 ldi.first_level = 1;
1677 ldi.sort_priority = LEVELCLASS_USER_START;
1678 ldi.readonly = FALSE;
1680 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1681 getCookie("LEVELINFO")));
1683 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1684 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1685 i != LEVELINFO_TOKEN_NAME_SORTING &&
1686 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1687 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1692 SetFilePermissions(filename, PERMS_PRIVATE);
1695 char *getSetupValue(int type, void *value)
1697 static char value_string[MAX_LINE_LEN];
1702 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1706 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1710 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1714 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1718 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1722 sprintf(value_string, "%d", *(int *)value);
1726 strcpy(value_string, *(char **)value);
1730 value_string[0] = '\0';
1734 return value_string;
1737 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1740 static char entry[MAX_LINE_LEN];
1741 int token_type = token_info[token_nr].type;
1742 void *setup_value = token_info[token_nr].value;
1743 char *token_text = token_info[token_nr].text;
1744 char *value_string = getSetupValue(token_type, setup_value);
1746 /* start with the prefix, token and some spaces to format output line */
1747 sprintf(entry, "%s%s:", prefix, token_text);
1748 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1751 /* continue with the token's value (which can have different types) */
1752 strcat(entry, value_string);
1754 if (token_type == TYPE_KEY_X11)
1756 Key key = *(Key *)setup_value;
1757 char *keyname = getKeyNameFromKey(key);
1759 /* add comment, if useful */
1760 if (strcmp(keyname, "(undefined)") != 0 &&
1761 strcmp(keyname, "(unknown)") != 0)
1763 for (i=strlen(entry); i<50; i++)
1766 strcat(entry, "# ");
1767 strcat(entry, keyname);
1774 void LoadLevelSetup_LastSeries()
1777 struct SetupFileList *level_setup_list = NULL;
1779 /* always start with reliable default values */
1780 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1782 /* ----------------------------------------------------------------------- */
1783 /* ~/.<program>/levelsetup.conf */
1784 /* ----------------------------------------------------------------------- */
1786 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1788 if ((level_setup_list = loadSetupFileList(filename)))
1790 char *last_level_series =
1791 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1793 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1795 if (leveldir_current == NULL)
1796 leveldir_current = leveldir_first;
1798 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1800 freeSetupFileList(level_setup_list);
1803 Error(ERR_WARN, "using default setup values");
1808 void SaveLevelSetup_LastSeries()
1811 char *level_subdir = leveldir_current->filename;
1814 /* ----------------------------------------------------------------------- */
1815 /* ~/.<program>/levelsetup.conf */
1816 /* ----------------------------------------------------------------------- */
1818 InitUserDataDirectory();
1820 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1822 if (!(file = fopen(filename, MODE_WRITE)))
1824 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1829 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1830 getCookie("LEVELSETUP")));
1831 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1837 SetFilePermissions(filename, PERMS_PRIVATE);
1840 static void checkSeriesInfo()
1842 static char *level_directory = NULL;
1844 struct dirent *dir_entry;
1846 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1848 level_directory = getPath2((leveldir_current->user_defined ?
1849 getUserLevelDir(NULL) :
1850 options.level_directory),
1851 leveldir_current->fullpath);
1853 if ((dir = opendir(level_directory)) == NULL)
1855 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1859 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1861 if (strlen(dir_entry->d_name) > 4 &&
1862 dir_entry->d_name[3] == '.' &&
1863 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1865 char levelnum_str[4];
1868 strncpy(levelnum_str, dir_entry->d_name, 3);
1869 levelnum_str[3] = '\0';
1871 levelnum_value = atoi(levelnum_str);
1873 if (levelnum_value < leveldir_current->first_level)
1875 Error(ERR_WARN, "additional level %d found", levelnum_value);
1876 leveldir_current->first_level = levelnum_value;
1878 else if (levelnum_value > leveldir_current->last_level)
1880 Error(ERR_WARN, "additional level %d found", levelnum_value);
1881 leveldir_current->last_level = levelnum_value;
1889 void LoadLevelSetup_SeriesInfo()
1892 struct SetupFileList *level_setup_list = NULL;
1893 char *level_subdir = leveldir_current->filename;
1895 /* always start with reliable default values */
1896 level_nr = leveldir_current->first_level;
1898 checkSeriesInfo(leveldir_current);
1900 /* ----------------------------------------------------------------------- */
1901 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1902 /* ----------------------------------------------------------------------- */
1904 level_subdir = leveldir_current->filename;
1906 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1908 if ((level_setup_list = loadSetupFileList(filename)))
1912 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
1916 level_nr = atoi(token_value);
1918 if (level_nr < leveldir_current->first_level)
1919 level_nr = leveldir_current->first_level;
1920 if (level_nr > leveldir_current->last_level)
1921 level_nr = leveldir_current->last_level;
1924 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
1928 int level_nr = atoi(token_value);
1930 if (level_nr < leveldir_current->first_level)
1931 level_nr = leveldir_current->first_level;
1932 if (level_nr > leveldir_current->last_level + 1)
1933 level_nr = leveldir_current->last_level;
1935 if (leveldir_current->user_defined)
1936 level_nr = leveldir_current->last_level;
1938 leveldir_current->handicap_level = level_nr;
1941 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1943 freeSetupFileList(level_setup_list);
1946 Error(ERR_WARN, "using default setup values");
1951 void SaveLevelSetup_SeriesInfo()
1954 char *level_subdir = leveldir_current->filename;
1955 char *level_nr_str = int2str(level_nr, 0);
1956 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
1959 /* ----------------------------------------------------------------------- */
1960 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1961 /* ----------------------------------------------------------------------- */
1963 InitLevelSetupDirectory(level_subdir);
1965 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1967 if (!(file = fopen(filename, MODE_WRITE)))
1969 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1974 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1975 getCookie("LEVELSETUP")));
1976 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
1978 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
1979 handicap_level_str));
1984 SetFilePermissions(filename, PERMS_PRIVATE);