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)
546 printf("Dumping TreeInfo:\n");
550 for (i=0; i<(depth + 1) * 3; i++)
553 printf("filename == '%s' [%s]\n", node->filename, node->name);
555 if (node->node_group != NULL)
556 dumpTreeInfo(node->node_group, depth + 1);
562 void sortTreeInfo(TreeInfo **node_first,
563 int (*compare_function)(const void *, const void *))
565 int num_nodes = numTreeInfo(*node_first);
566 TreeInfo **sort_array;
567 TreeInfo *node = *node_first;
573 /* allocate array for sorting structure pointers */
574 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
576 /* writing structure pointers to sorting array */
577 while (i < num_nodes && node) /* double boundary check... */
579 sort_array[i] = node;
585 /* sorting the structure pointers in the sorting array */
586 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
589 /* update the linkage of list elements with the sorted node array */
590 for (i=0; i<num_nodes - 1; i++)
591 sort_array[i]->next = sort_array[i + 1];
592 sort_array[num_nodes - 1]->next = NULL;
594 /* update the linkage of the main list anchor pointer */
595 *node_first = sort_array[0];
599 /* now recursively sort the level group structures */
603 if (node->node_group != NULL)
604 sortTreeInfo(&node->node_group, compare_function);
611 /* ========================================================================= */
612 /* some stuff from "files.c" */
613 /* ========================================================================= */
615 #if defined(PLATFORM_WIN32)
617 #define S_IRGRP S_IRUSR
620 #define S_IROTH S_IRUSR
623 #define S_IWGRP S_IWUSR
626 #define S_IWOTH S_IWUSR
629 #define S_IXGRP S_IXUSR
632 #define S_IXOTH S_IXUSR
635 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
640 #endif /* PLATFORM_WIN32 */
642 /* file permissions for newly written files */
643 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
644 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
645 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
647 #define MODE_W_PRIVATE (S_IWUSR)
648 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
649 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
651 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
652 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
654 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
655 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
657 char *getUserDataDir(void)
659 static char *userdata_dir = NULL;
663 char *home_dir = getHomeDir();
664 char *data_dir = program.userdata_directory;
666 userdata_dir = getPath2(home_dir, data_dir);
674 return getUserDataDir();
677 static mode_t posix_umask(mode_t mask)
679 #if defined(PLATFORM_UNIX)
686 static int posix_mkdir(const char *pathname, mode_t mode)
688 #if defined(PLATFORM_WIN32)
689 return mkdir(pathname);
691 return mkdir(pathname, mode);
695 void createDirectory(char *dir, char *text, int permission_class)
697 /* leave "other" permissions in umask untouched, but ensure group parts
698 of USERDATA_DIR_MODE are not masked */
699 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
700 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
701 mode_t normal_umask = posix_umask(0);
702 mode_t group_umask = ~(dir_mode & S_IRWXG);
703 posix_umask(normal_umask & group_umask);
705 if (access(dir, F_OK) != 0)
706 if (posix_mkdir(dir, dir_mode) != 0)
707 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
709 posix_umask(normal_umask); /* reset normal umask */
712 void InitUserDataDirectory()
714 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
717 void SetFilePermissions(char *filename, int permission_class)
719 chmod(filename, (permission_class == PERMS_PRIVATE ?
720 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
723 char *getCookie(char *file_type)
725 static char cookie[MAX_COOKIE_LEN + 1];
727 if (strlen(program.cookie_prefix) + 1 +
728 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
729 return "[COOKIE ERROR]"; /* should never happen */
731 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
732 program.cookie_prefix, file_type,
733 program.version_major, program.version_minor);
738 int getFileVersionFromCookieString(const char *cookie)
740 const char *ptr_cookie1, *ptr_cookie2;
741 const char *pattern1 = "_FILE_VERSION_";
742 const char *pattern2 = "?.?";
743 const int len_cookie = strlen(cookie);
744 const int len_pattern1 = strlen(pattern1);
745 const int len_pattern2 = strlen(pattern2);
746 const int len_pattern = len_pattern1 + len_pattern2;
747 int version_major, version_minor;
749 if (len_cookie <= len_pattern)
752 ptr_cookie1 = &cookie[len_cookie - len_pattern];
753 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
755 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
758 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
759 ptr_cookie2[1] != '.' ||
760 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
763 version_major = ptr_cookie2[0] - '0';
764 version_minor = ptr_cookie2[2] - '0';
766 return VERSION_IDENT(version_major, version_minor, 0);
769 boolean checkCookieString(const char *cookie, const char *template)
771 const char *pattern = "_FILE_VERSION_?.?";
772 const int len_cookie = strlen(cookie);
773 const int len_template = strlen(template);
774 const int len_pattern = strlen(pattern);
776 if (len_cookie != len_template)
779 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
785 /* ------------------------------------------------------------------------- */
786 /* setup file list handling functions */
787 /* ------------------------------------------------------------------------- */
789 int get_string_integer_value(char *s)
791 static char *number_text[][3] =
793 { "0", "zero", "null", },
794 { "1", "one", "first" },
795 { "2", "two", "second" },
796 { "3", "three", "third" },
797 { "4", "four", "fourth" },
798 { "5", "five", "fifth" },
799 { "6", "six", "sixth" },
800 { "7", "seven", "seventh" },
801 { "8", "eight", "eighth" },
802 { "9", "nine", "ninth" },
803 { "10", "ten", "tenth" },
804 { "11", "eleven", "eleventh" },
805 { "12", "twelve", "twelfth" },
809 char *s_lower = getStringToLower(s);
814 if (strcmp(s_lower, number_text[i][j]) == 0)
825 boolean get_string_boolean_value(char *s)
827 char *s_lower = getStringToLower(s);
828 boolean result = FALSE;
830 if (strcmp(s_lower, "true") == 0 ||
831 strcmp(s_lower, "yes") == 0 ||
832 strcmp(s_lower, "on") == 0 ||
833 get_string_integer_value(s) == 1)
841 char *getFormattedSetupEntry(char *token, char *value)
844 static char entry[MAX_LINE_LEN];
846 sprintf(entry, "%s:", token);
847 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
851 strcat(entry, value);
856 void freeSetupFileList(struct SetupFileList *setup_file_list)
858 if (!setup_file_list)
861 if (setup_file_list->token)
862 free(setup_file_list->token);
863 if (setup_file_list->value)
864 free(setup_file_list->value);
865 if (setup_file_list->next)
866 freeSetupFileList(setup_file_list->next);
867 free(setup_file_list);
870 static struct SetupFileList *newSetupFileList(char *token, char *value)
872 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
874 new->token = checked_malloc(strlen(token) + 1);
875 strcpy(new->token, token);
877 new->value = checked_malloc(strlen(value) + 1);
878 strcpy(new->value, value);
885 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
887 if (!setup_file_list)
890 if (strcmp(setup_file_list->token, token) == 0)
891 return setup_file_list->value;
893 return getTokenValue(setup_file_list->next, token);
896 static void setTokenValue(struct SetupFileList *setup_file_list,
897 char *token, char *value)
899 if (!setup_file_list)
902 if (strcmp(setup_file_list->token, token) == 0)
904 free(setup_file_list->value);
905 setup_file_list->value = checked_malloc(strlen(value) + 1);
906 strcpy(setup_file_list->value, value);
908 else if (setup_file_list->next == NULL)
909 setup_file_list->next = newSetupFileList(token, value);
911 setTokenValue(setup_file_list->next, token, value);
915 static void printSetupFileList(struct SetupFileList *setup_file_list)
917 if (!setup_file_list)
920 printf("token: '%s'\n", setup_file_list->token);
921 printf("value: '%s'\n", setup_file_list->value);
923 printSetupFileList(setup_file_list->next);
927 struct SetupFileList *loadSetupFileList(char *filename)
930 char line[MAX_LINE_LEN];
931 char *token, *value, *line_ptr;
932 struct SetupFileList *setup_file_list = newSetupFileList("", "");
933 struct SetupFileList *first_valid_list_entry;
937 if (!(file = fopen(filename, MODE_READ)))
939 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
945 /* read next line of input file */
946 if (!fgets(line, MAX_LINE_LEN, file))
949 /* cut trailing comment or whitespace from input line */
950 for (line_ptr = line; *line_ptr; line_ptr++)
952 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
959 /* cut trailing whitespaces from input line */
960 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
961 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
964 /* ignore empty lines */
968 line_len = strlen(line);
970 /* cut leading whitespaces from token */
971 for (token = line; *token; token++)
972 if (*token != ' ' && *token != '\t')
975 /* find end of token */
976 for (line_ptr = token; *line_ptr; line_ptr++)
978 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
985 if (line_ptr < line + line_len)
986 value = line_ptr + 1;
990 /* cut leading whitespaces from value */
991 for (; *value; value++)
992 if (*value != ' ' && *value != '\t')
995 if (*token && *value)
996 setTokenValue(setup_file_list, token, value);
1001 first_valid_list_entry = setup_file_list->next;
1003 /* free empty list header */
1004 setup_file_list->next = NULL;
1005 freeSetupFileList(setup_file_list);
1007 if (first_valid_list_entry == NULL)
1008 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1010 return first_valid_list_entry;
1013 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1016 if (!setup_file_list)
1019 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1021 if (!checkCookieString(setup_file_list->value, identifier))
1023 Error(ERR_WARN, "configuration file has wrong file identifier");
1030 if (setup_file_list->next)
1031 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1034 Error(ERR_WARN, "configuration file has no file identifier");
1040 /* ========================================================================= */
1041 /* setup file stuff */
1042 /* ========================================================================= */
1044 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1045 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1046 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1048 /* level directory info */
1049 #define LEVELINFO_TOKEN_NAME 0
1050 #define LEVELINFO_TOKEN_NAME_SHORT 1
1051 #define LEVELINFO_TOKEN_NAME_SORTING 2
1052 #define LEVELINFO_TOKEN_AUTHOR 3
1053 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1054 #define LEVELINFO_TOKEN_LEVELS 5
1055 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1056 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1057 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1058 #define LEVELINFO_TOKEN_READONLY 9
1060 #define NUM_LEVELINFO_TOKENS 10
1062 static LevelDirTree ldi;
1064 static struct TokenInfo levelinfo_tokens[] =
1066 /* level directory info */
1067 { TYPE_STRING, &ldi.name, "name" },
1068 { TYPE_STRING, &ldi.name_short, "name_short" },
1069 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1070 { TYPE_STRING, &ldi.author, "author" },
1071 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1072 { TYPE_INTEGER, &ldi.levels, "levels" },
1073 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1074 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1075 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1076 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1079 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1083 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1084 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1085 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1086 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1089 ldi->node_parent = NULL;
1090 ldi->node_group = NULL;
1094 ldi->cl_cursor = -1;
1096 ldi->filename = NULL;
1097 ldi->fullpath = NULL;
1098 ldi->basepath = NULL;
1099 ldi->name = getStringCopy(ANONYMOUS_NAME);
1100 ldi->name_short = NULL;
1101 ldi->name_sorting = NULL;
1102 ldi->author = getStringCopy(ANONYMOUS_NAME);
1104 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1105 ldi->parent_link = FALSE;
1106 ldi->user_defined = FALSE;
1108 ldi->class_desc = NULL;
1110 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1112 ldi->imported_from = NULL;
1114 ldi->first_level = 0;
1115 ldi->last_level = 0;
1116 ldi->level_group = FALSE;
1117 ldi->handicap_level = 0;
1118 ldi->readonly = TRUE;
1122 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1126 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1128 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1132 /* first copy all values from the parent structure ... */
1135 /* ... then set all fields to default that cannot be inherited from parent.
1136 This is especially important for all those fields that can be set from
1137 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1138 calls 'free()' for all already set token values which requires that no
1139 other structure's pointer may point to them!
1142 ldi->filename = NULL;
1143 ldi->fullpath = NULL;
1144 ldi->basepath = NULL;
1145 ldi->name = getStringCopy(ANONYMOUS_NAME);
1146 ldi->name_short = NULL;
1147 ldi->name_sorting = NULL;
1148 ldi->author = getStringCopy(parent->author);
1149 ldi->imported_from = getStringCopy(parent->imported_from);
1151 ldi->level_group = FALSE;
1152 ldi->parent_link = FALSE;
1154 ldi->node_top = parent->node_top;
1155 ldi->node_parent = parent;
1156 ldi->node_group = NULL;
1160 void setSetupInfo(struct TokenInfo *token_info,
1161 int token_nr, char *token_value)
1163 int token_type = token_info[token_nr].type;
1164 void *setup_value = token_info[token_nr].value;
1166 if (token_value == NULL)
1169 /* set setup field to corresponding token value */
1174 *(boolean *)setup_value = get_string_boolean_value(token_value);
1178 *(Key *)setup_value = getKeyFromKeyName(token_value);
1182 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1186 *(int *)setup_value = get_string_integer_value(token_value);
1190 if (*(char **)setup_value != NULL)
1191 free(*(char **)setup_value);
1192 *(char **)setup_value = getStringCopy(token_value);
1200 static int compareTreeInfoEntries(const void *object1, const void *object2)
1202 const TreeInfo *entry1 = *((TreeInfo **)object1);
1203 const TreeInfo *entry2 = *((TreeInfo **)object2);
1206 if (entry1->parent_link || entry2->parent_link)
1207 compare_result = (entry1->parent_link ? -1 : +1);
1208 else if (entry1->sort_priority == entry2->sort_priority)
1210 char *name1 = getStringToLower(entry1->name_sorting);
1211 char *name2 = getStringToLower(entry2->name_sorting);
1213 compare_result = strcmp(name1, name2);
1218 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1219 compare_result = entry1->sort_priority - entry2->sort_priority;
1221 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1223 return compare_result;
1226 static void createParentTreeInfoNode(TreeInfo *node_parent)
1230 if (node_parent == NULL)
1233 ti_new = newTreeInfo();
1234 setTreeInfoToDefaults(ti_new, node_parent->type);
1236 ti_new->node_parent = node_parent;
1237 ti_new->parent_link = TRUE;
1239 ti_new->name = ".. (parent directory)";
1240 ti_new->name_short = getStringCopy(ti_new->name);
1241 ti_new->name_sorting = getStringCopy(ti_new->name);
1243 ti_new->filename = "..";
1244 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1246 ti_new->sort_priority = node_parent->sort_priority;
1247 ti_new->class_desc = getLevelClassDescription(ti_new);
1249 pushTreeInfo(&node_parent->node_group, ti_new);
1252 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1253 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1255 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1256 TreeInfo *node_parent,
1257 char *level_directory,
1258 char *directory_name)
1260 char *directory_path = getPath2(level_directory, directory_name);
1261 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1262 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1263 LevelDirTree *leveldir_new = NULL;
1266 if (setup_file_list == NULL)
1268 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1270 free(directory_path);
1276 leveldir_new = newTreeInfo();
1279 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1281 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1283 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1285 /* set all structure fields according to the token/value pairs */
1286 ldi = *leveldir_new;
1287 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1288 setSetupInfo(levelinfo_tokens, i,
1289 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1290 *leveldir_new = ldi;
1292 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1294 if (leveldir_new->name_short == NULL)
1295 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1297 if (leveldir_new->name_sorting == NULL)
1298 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1300 leveldir_new->filename = getStringCopy(directory_name);
1302 if (node_parent == NULL) /* top level group */
1304 leveldir_new->basepath = level_directory;
1305 leveldir_new->fullpath = leveldir_new->filename;
1307 else /* sub level group */
1309 leveldir_new->basepath = node_parent->basepath;
1310 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1313 if (leveldir_new->levels < 1)
1314 leveldir_new->levels = 1;
1316 leveldir_new->last_level =
1317 leveldir_new->first_level + leveldir_new->levels - 1;
1319 leveldir_new->user_defined =
1320 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1322 leveldir_new->color = LEVELCOLOR(leveldir_new);
1323 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1325 leveldir_new->handicap_level = /* set handicap to default value */
1326 (leveldir_new->user_defined ?
1327 leveldir_new->last_level :
1328 leveldir_new->first_level);
1330 pushTreeInfo(node_first, leveldir_new);
1332 freeSetupFileList(setup_file_list);
1334 if (leveldir_new->level_group)
1336 /* create node to link back to current level directory */
1337 createParentTreeInfoNode(leveldir_new);
1339 /* step into sub-directory and look for more level series */
1340 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1341 leveldir_new, directory_path);
1344 free(directory_path);
1350 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1351 TreeInfo *node_parent,
1352 char *level_directory)
1355 struct dirent *dir_entry;
1356 boolean valid_entry_found = FALSE;
1358 if ((dir = opendir(level_directory)) == NULL)
1360 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1364 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1366 struct stat file_status;
1367 char *directory_name = dir_entry->d_name;
1368 char *directory_path = getPath2(level_directory, directory_name);
1370 /* skip entries for current and parent directory */
1371 if (strcmp(directory_name, ".") == 0 ||
1372 strcmp(directory_name, "..") == 0)
1374 free(directory_path);
1378 /* find out if directory entry is itself a directory */
1379 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1380 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1382 free(directory_path);
1386 free(directory_path);
1388 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1395 if (!valid_entry_found)
1397 /* check if this directory directly contains a file "levelinfo.conf" */
1398 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1399 level_directory, ".");
1402 if (!valid_entry_found)
1403 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1407 void LoadLevelInfo()
1409 InitUserLevelDirectory(getLoginName());
1411 DrawInitText("Loading level series:", 120, FC_GREEN);
1413 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1414 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1416 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1418 if (leveldir_first == NULL)
1419 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1421 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1424 dumpTreeInfo(leveldir_first, 0);
1428 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1429 TreeInfo *node_parent,
1430 char *base_directory,
1431 char *directory_name, int type)
1433 char *directory_path = getPath2(base_directory, directory_name);
1435 getPath2(directory_path,
1436 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1437 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1438 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1439 struct SetupFileList *setup_file_list = NULL;
1440 TreeInfo *artwork_new = NULL;
1441 char *check_dir = NULL;
1444 if (access(getUserLevelDir(filename), F_OK) == 0) /* file exists */
1445 loadSetupFileList(filename);
1447 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1450 struct dirent *dir_entry;
1451 boolean valid_file_found = FALSE;
1453 if ((dir = opendir(base_directory)) != NULL)
1455 while ((dir_entry = readdir(dir)) != NULL)
1457 char *entry_name = dir_entry->d_name;
1459 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1460 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1461 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1463 valid_file_found = TRUE;
1471 if (!valid_file_found)
1473 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1475 free(directory_path);
1482 artwork_new = newTreeInfo();
1485 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1487 setTreeInfoToDefaults(artwork_new, type);
1489 artwork_new->filename = getStringCopy(directory_name);
1491 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1494 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1497 /* set all structure fields according to the token/value pairs */
1499 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1500 setSetupInfo(levelinfo_tokens, i,
1501 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1504 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1506 if (artwork_new->name_short == NULL)
1507 artwork_new->name_short = getStringCopy(artwork_new->name);
1509 if (artwork_new->name_sorting == NULL)
1510 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1513 if (node_parent == NULL) /* top level group */
1515 artwork_new->basepath = base_directory;
1516 artwork_new->fullpath = artwork_new->filename;
1518 else /* sub level group */
1520 artwork_new->basepath = node_parent->basepath;
1521 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1524 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1525 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1526 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1527 artwork_new->user_defined =
1528 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1530 /* (may use ".sort_priority" from "setup_file_list" above) */
1531 artwork_new->color = LEVELCOLOR(artwork_new);
1532 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1534 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1536 if (artwork_new->name != NULL)
1537 free(artwork_new->name);
1539 if (strcmp(artwork_new->filename, ".") == 0)
1541 if (artwork_new->user_defined)
1542 artwork_new->name = getStringCopy("private");
1544 artwork_new->name = getStringCopy("default");
1547 artwork_new->name = getStringCopy(artwork_new->filename);
1549 artwork_new->name_short = getStringCopy(artwork_new->name);
1550 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1553 pushTreeInfo(node_first, artwork_new);
1555 freeSetupFileList(setup_file_list);
1557 free(directory_path);
1563 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1564 TreeInfo *node_parent,
1565 char *base_directory, int type)
1568 struct dirent *dir_entry;
1569 boolean valid_entry_found = FALSE;
1571 if ((dir = opendir(base_directory)) == NULL)
1573 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1574 base_directory == options.graphics_directory) ||
1575 (type == TREE_TYPE_SOUNDS_DIR &&
1576 base_directory == options.sounds_directory) ||
1577 (type == TREE_TYPE_MUSIC_DIR &&
1578 base_directory == options.music_directory))
1579 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1583 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1585 struct stat file_status;
1586 char *directory_name = dir_entry->d_name;
1587 char *directory_path = getPath2(base_directory, directory_name);
1589 /* skip entries for current and parent directory */
1590 if (strcmp(directory_name, ".") == 0 ||
1591 strcmp(directory_name, "..") == 0)
1593 free(directory_path);
1597 /* find out if directory entry is itself a directory */
1598 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1599 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1601 free(directory_path);
1605 free(directory_path);
1607 /* check if this directory contains artwork with or without config file */
1608 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1610 directory_name, type);
1615 /* check if this directory directly contains artwork itself */
1616 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1617 base_directory, ".",
1619 if (!valid_entry_found)
1620 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1624 void LoadArtworkInfo()
1626 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1628 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1629 options.graphics_directory,
1630 TREE_TYPE_GRAPHICS_DIR);
1631 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1632 getUserGraphicsDir(NULL),
1633 TREE_TYPE_GRAPHICS_DIR);
1635 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1636 options.sounds_directory,
1637 TREE_TYPE_SOUNDS_DIR);
1638 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1639 getUserSoundsDir(NULL),
1640 TREE_TYPE_SOUNDS_DIR);
1642 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1643 options.music_directory,
1644 TREE_TYPE_MUSIC_DIR);
1645 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1646 getUserMusicDir(NULL),
1647 TREE_TYPE_MUSIC_DIR);
1649 /* before sorting, the first entries will be from the user directory */
1650 artwork.gfx_current = artwork.gfx_first;
1651 artwork.snd_current = artwork.snd_first;
1652 artwork.mus_current = artwork.mus_first;
1654 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1655 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1656 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1659 dumpTreeInfo(artwork.gfx_first, 0);
1660 dumpTreeInfo(artwork.snd_first, 0);
1661 dumpTreeInfo(artwork.mus_first, 0);
1665 static void SaveUserLevelInfo()
1671 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1673 if (!(file = fopen(filename, MODE_WRITE)))
1675 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1680 /* always start with reliable default values */
1681 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1683 ldi.name = getLoginName();
1684 ldi.author = getRealName();
1686 ldi.first_level = 1;
1687 ldi.sort_priority = LEVELCLASS_USER_START;
1688 ldi.readonly = FALSE;
1690 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1691 getCookie("LEVELINFO")));
1693 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1694 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1695 i != LEVELINFO_TOKEN_NAME_SORTING &&
1696 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1697 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1702 SetFilePermissions(filename, PERMS_PRIVATE);
1705 char *getSetupValue(int type, void *value)
1707 static char value_string[MAX_LINE_LEN];
1712 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1716 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1720 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1724 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1728 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1732 sprintf(value_string, "%d", *(int *)value);
1736 strcpy(value_string, *(char **)value);
1740 value_string[0] = '\0';
1744 return value_string;
1747 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1750 static char entry[MAX_LINE_LEN];
1751 int token_type = token_info[token_nr].type;
1752 void *setup_value = token_info[token_nr].value;
1753 char *token_text = token_info[token_nr].text;
1754 char *value_string = getSetupValue(token_type, setup_value);
1756 /* start with the prefix, token and some spaces to format output line */
1757 sprintf(entry, "%s%s:", prefix, token_text);
1758 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1761 /* continue with the token's value (which can have different types) */
1762 strcat(entry, value_string);
1764 if (token_type == TYPE_KEY_X11)
1766 Key key = *(Key *)setup_value;
1767 char *keyname = getKeyNameFromKey(key);
1769 /* add comment, if useful */
1770 if (strcmp(keyname, "(undefined)") != 0 &&
1771 strcmp(keyname, "(unknown)") != 0)
1773 for (i=strlen(entry); i<50; i++)
1776 strcat(entry, "# ");
1777 strcat(entry, keyname);
1784 void LoadLevelSetup_LastSeries()
1787 struct SetupFileList *level_setup_list = NULL;
1789 /* always start with reliable default values */
1790 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1792 /* ----------------------------------------------------------------------- */
1793 /* ~/.<program>/levelsetup.conf */
1794 /* ----------------------------------------------------------------------- */
1796 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1798 if ((level_setup_list = loadSetupFileList(filename)))
1800 char *last_level_series =
1801 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1803 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1805 if (leveldir_current == NULL)
1806 leveldir_current = leveldir_first;
1808 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1810 freeSetupFileList(level_setup_list);
1813 Error(ERR_WARN, "using default setup values");
1818 void SaveLevelSetup_LastSeries()
1821 char *level_subdir = leveldir_current->filename;
1824 /* ----------------------------------------------------------------------- */
1825 /* ~/.<program>/levelsetup.conf */
1826 /* ----------------------------------------------------------------------- */
1828 InitUserDataDirectory();
1830 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1832 if (!(file = fopen(filename, MODE_WRITE)))
1834 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1839 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1840 getCookie("LEVELSETUP")));
1841 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1847 SetFilePermissions(filename, PERMS_PRIVATE);
1850 static void checkSeriesInfo()
1852 static char *level_directory = NULL;
1854 struct dirent *dir_entry;
1856 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1858 level_directory = getPath2((leveldir_current->user_defined ?
1859 getUserLevelDir(NULL) :
1860 options.level_directory),
1861 leveldir_current->fullpath);
1863 if ((dir = opendir(level_directory)) == NULL)
1865 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1869 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1871 if (strlen(dir_entry->d_name) > 4 &&
1872 dir_entry->d_name[3] == '.' &&
1873 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1875 char levelnum_str[4];
1878 strncpy(levelnum_str, dir_entry->d_name, 3);
1879 levelnum_str[3] = '\0';
1881 levelnum_value = atoi(levelnum_str);
1883 if (levelnum_value < leveldir_current->first_level)
1885 Error(ERR_WARN, "additional level %d found", levelnum_value);
1886 leveldir_current->first_level = levelnum_value;
1888 else if (levelnum_value > leveldir_current->last_level)
1890 Error(ERR_WARN, "additional level %d found", levelnum_value);
1891 leveldir_current->last_level = levelnum_value;
1899 void LoadLevelSetup_SeriesInfo()
1902 struct SetupFileList *level_setup_list = NULL;
1903 char *level_subdir = leveldir_current->filename;
1905 /* always start with reliable default values */
1906 level_nr = leveldir_current->first_level;
1908 checkSeriesInfo(leveldir_current);
1910 /* ----------------------------------------------------------------------- */
1911 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1912 /* ----------------------------------------------------------------------- */
1914 level_subdir = leveldir_current->filename;
1916 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1918 if ((level_setup_list = loadSetupFileList(filename)))
1922 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
1926 level_nr = atoi(token_value);
1928 if (level_nr < leveldir_current->first_level)
1929 level_nr = leveldir_current->first_level;
1930 if (level_nr > leveldir_current->last_level)
1931 level_nr = leveldir_current->last_level;
1934 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
1938 int level_nr = atoi(token_value);
1940 if (level_nr < leveldir_current->first_level)
1941 level_nr = leveldir_current->first_level;
1942 if (level_nr > leveldir_current->last_level + 1)
1943 level_nr = leveldir_current->last_level;
1945 if (leveldir_current->user_defined)
1946 level_nr = leveldir_current->last_level;
1948 leveldir_current->handicap_level = level_nr;
1951 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1953 freeSetupFileList(level_setup_list);
1956 Error(ERR_WARN, "using default setup values");
1961 void SaveLevelSetup_SeriesInfo()
1964 char *level_subdir = leveldir_current->filename;
1965 char *level_nr_str = int2str(level_nr, 0);
1966 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
1969 /* ----------------------------------------------------------------------- */
1970 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1971 /* ----------------------------------------------------------------------- */
1973 InitLevelSetupDirectory(level_subdir);
1975 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1977 if (!(file = fopen(filename, MODE_WRITE)))
1979 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1984 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1985 getCookie("LEVELSETUP")));
1986 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
1988 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
1989 handicap_level_str));
1994 SetFilePermissions(filename, PERMS_PRIVATE);