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 *getCurrentLevelDir()
171 static char *level_dir = NULL;
176 if (leveldir_current == NULL)
177 return options.level_directory;
179 level_dir = getPath2((leveldir_current->user_defined ?
180 getUserLevelDir(NULL) : options.level_directory),
181 leveldir_current->fullpath);
186 static char *getDefaultGraphicsDir(char *graphics_subdir)
188 static char *graphics_dir = NULL;
190 if (graphics_subdir == NULL)
191 return options.graphics_directory;
196 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
201 static char *getDefaultSoundsDir(char *sounds_subdir)
203 static char *sounds_dir = NULL;
205 if (sounds_subdir == NULL)
206 return options.sounds_directory;
211 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
216 static char *getDefaultMusicDir(char *music_subdir)
218 static char *music_dir = NULL;
220 if (music_subdir == NULL)
221 return options.music_directory;
226 music_dir = getPath2(options.music_directory, music_subdir);
231 static char *getUserGraphicsDir()
233 static char *usergraphics_dir = NULL;
235 if (usergraphics_dir == NULL)
236 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
238 return usergraphics_dir;
241 static char *getUserSoundsDir()
243 static char *usersounds_dir = NULL;
245 if (usersounds_dir == NULL)
246 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
248 return usersounds_dir;
251 static char *getUserMusicDir()
253 static char *usermusic_dir = NULL;
255 if (usermusic_dir == NULL)
256 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
258 return usermusic_dir;
261 char *getLevelFilename(int nr)
263 static char *filename = NULL;
264 char basename[MAX_FILENAME_LEN];
266 if (filename != NULL)
269 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
270 filename = getPath2(getCurrentLevelDir(), basename);
275 char *getTapeFilename(int nr)
277 static char *filename = NULL;
278 char basename[MAX_FILENAME_LEN];
280 if (filename != NULL)
283 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
284 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
289 char *getScoreFilename(int nr)
291 static char *filename = NULL;
292 char basename[MAX_FILENAME_LEN];
294 if (filename != NULL)
297 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
298 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
303 char *getSetupFilename()
305 static char *filename = NULL;
307 if (filename != NULL)
310 filename = getPath2(getSetupDir(), SETUP_FILENAME);
315 static char *getSetupArtworkDir(TreeInfo *ti)
317 static char *artwork_dir = NULL;
319 if (artwork_dir != NULL)
322 artwork_dir = getPath2(ti->basepath, ti->fullpath);
327 static char *getCorrectedImageBasename(char *basename)
329 char *result = basename;
331 #if defined(PLATFORM_MSDOS)
332 if (program.filename_prefix != NULL)
334 int prefix_len = strlen(program.filename_prefix);
336 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
337 result = &basename[prefix_len];
344 static boolean fileExists(char *filename)
347 printf("checking file '%s'\n", filename);
350 return (access(filename, F_OK) == 0);
353 char *getCustomImageFilename(char *basename)
355 static char *filename = NULL;
357 if (filename != NULL)
360 basename = getCorrectedImageBasename(basename);
362 /* 1st try: look for special artwork in current level series directory */
363 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
364 if (fileExists(filename))
367 /* 2nd try: look for special artwork in private artwork directory */
368 filename = getPath2(getUserGraphicsDir(), basename);
369 if (fileExists(filename))
372 /* 3rd try: look for special artwork in configured artwork directory */
373 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
374 if (fileExists(filename))
377 /* 4th try: look for default artwork in new default artwork directory */
378 filename = getPath2(getDefaultGraphicsDir("gfx_classic"), basename);
379 if (fileExists(filename))
382 /* 5th try: look for default artwork in old default artwork directory */
383 filename = getPath2(options.graphics_directory, basename);
384 if (fileExists(filename))
387 return NULL; /* cannot find image file */
390 void InitTapeDirectory(char *level_subdir)
392 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
393 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
394 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
397 void InitScoreDirectory(char *level_subdir)
399 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
400 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
403 static void SaveUserLevelInfo();
405 void InitUserLevelDirectory(char *level_subdir)
407 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
409 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
410 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
411 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
417 void InitLevelSetupDirectory(char *level_subdir)
419 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
420 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
421 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
424 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
426 int file_version_major, file_version_minor, file_version_patch;
427 int game_version_major, game_version_minor, game_version_patch;
429 file_version_major = fgetc(file);
430 file_version_minor = fgetc(file);
431 file_version_patch = fgetc(file);
432 fgetc(file); /* not used */
434 game_version_major = fgetc(file);
435 game_version_minor = fgetc(file);
436 game_version_patch = fgetc(file);
437 fgetc(file); /* not used */
439 *file_version = VERSION_IDENT(file_version_major,
443 *game_version = VERSION_IDENT(game_version_major,
448 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
450 int file_version_major = VERSION_MAJOR(file_version);
451 int file_version_minor = VERSION_MINOR(file_version);
452 int file_version_patch = VERSION_PATCH(file_version);
453 int game_version_major = VERSION_MAJOR(game_version);
454 int game_version_minor = VERSION_MINOR(game_version);
455 int game_version_patch = VERSION_PATCH(game_version);
457 fputc(file_version_major, file);
458 fputc(file_version_minor, file);
459 fputc(file_version_patch, file);
460 fputc(0, file); /* not used */
462 fputc(game_version_major, file);
463 fputc(game_version_minor, file);
464 fputc(game_version_patch, file);
465 fputc(0, file); /* not used */
469 /* ------------------------------------------------------------------------- */
470 /* some functions to handle lists of level directories */
471 /* ------------------------------------------------------------------------- */
473 TreeInfo *newTreeInfo()
475 return checked_calloc(sizeof(TreeInfo));
478 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
480 node_new->next = *node_first;
481 *node_first = node_new;
484 int numTreeInfo(TreeInfo *node)
497 boolean validLevelSeries(TreeInfo *node)
499 return (node != NULL && !node->node_group && !node->parent_link);
502 TreeInfo *getFirstValidLevelSeries(TreeInfo *node)
506 if (leveldir_first) /* start with first level directory entry */
507 return getFirstValidLevelSeries(leveldir_first);
511 else if (node->node_group) /* enter level group (step down into tree) */
512 return getFirstValidLevelSeries(node->node_group);
513 else if (node->parent_link) /* skip start entry of level group */
515 if (node->next) /* get first real level series entry */
516 return getFirstValidLevelSeries(node->next);
517 else /* leave empty level group and go on */
518 return getFirstValidLevelSeries(node->node_parent->next);
520 else /* this seems to be a regular level series */
524 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
529 if (node->node_parent == NULL) /* top level group */
530 return *node->node_top;
531 else /* sub level group */
532 return node->node_parent->node_group;
535 int numTreeInfoInGroup(TreeInfo *node)
537 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
540 int posTreeInfo(TreeInfo *node)
542 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
547 if (node_cmp == node)
551 node_cmp = node_cmp->next;
557 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
559 TreeInfo *node_default = node;
574 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
576 if (filename == NULL)
581 if (node->node_group)
583 TreeInfo *node_group;
585 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
590 else if (!node->parent_link)
592 if (strcmp(filename, node->filename) == 0)
602 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
604 return getTreeInfoFromFilenameExt(ti, filename);
607 void dumpTreeInfo(TreeInfo *node, int depth)
611 printf("Dumping TreeInfo:\n");
615 for (i=0; i<(depth + 1) * 3; i++)
618 printf("filename == '%s' [%s]\n", node->filename, node->name);
620 if (node->node_group != NULL)
621 dumpTreeInfo(node->node_group, depth + 1);
627 void sortTreeInfo(TreeInfo **node_first,
628 int (*compare_function)(const void *, const void *))
630 int num_nodes = numTreeInfo(*node_first);
631 TreeInfo **sort_array;
632 TreeInfo *node = *node_first;
638 /* allocate array for sorting structure pointers */
639 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
641 /* writing structure pointers to sorting array */
642 while (i < num_nodes && node) /* double boundary check... */
644 sort_array[i] = node;
650 /* sorting the structure pointers in the sorting array */
651 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
654 /* update the linkage of list elements with the sorted node array */
655 for (i=0; i<num_nodes - 1; i++)
656 sort_array[i]->next = sort_array[i + 1];
657 sort_array[num_nodes - 1]->next = NULL;
659 /* update the linkage of the main list anchor pointer */
660 *node_first = sort_array[0];
664 /* now recursively sort the level group structures */
668 if (node->node_group != NULL)
669 sortTreeInfo(&node->node_group, compare_function);
676 /* ========================================================================= */
677 /* some stuff from "files.c" */
678 /* ========================================================================= */
680 #if defined(PLATFORM_WIN32)
682 #define S_IRGRP S_IRUSR
685 #define S_IROTH S_IRUSR
688 #define S_IWGRP S_IWUSR
691 #define S_IWOTH S_IWUSR
694 #define S_IXGRP S_IXUSR
697 #define S_IXOTH S_IXUSR
700 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
705 #endif /* PLATFORM_WIN32 */
707 /* file permissions for newly written files */
708 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
709 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
710 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
712 #define MODE_W_PRIVATE (S_IWUSR)
713 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
714 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
716 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
717 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
719 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
720 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
722 char *getUserDataDir(void)
724 static char *userdata_dir = NULL;
728 char *home_dir = getHomeDir();
729 char *data_dir = program.userdata_directory;
731 userdata_dir = getPath2(home_dir, data_dir);
739 return getUserDataDir();
742 static mode_t posix_umask(mode_t mask)
744 #if defined(PLATFORM_UNIX)
751 static int posix_mkdir(const char *pathname, mode_t mode)
753 #if defined(PLATFORM_WIN32)
754 return mkdir(pathname);
756 return mkdir(pathname, mode);
760 void createDirectory(char *dir, char *text, int permission_class)
762 /* leave "other" permissions in umask untouched, but ensure group parts
763 of USERDATA_DIR_MODE are not masked */
764 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
765 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
766 mode_t normal_umask = posix_umask(0);
767 mode_t group_umask = ~(dir_mode & S_IRWXG);
768 posix_umask(normal_umask & group_umask);
770 if (access(dir, F_OK) != 0)
771 if (posix_mkdir(dir, dir_mode) != 0)
772 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
774 posix_umask(normal_umask); /* reset normal umask */
777 void InitUserDataDirectory()
779 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
782 void SetFilePermissions(char *filename, int permission_class)
784 chmod(filename, (permission_class == PERMS_PRIVATE ?
785 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
788 char *getCookie(char *file_type)
790 static char cookie[MAX_COOKIE_LEN + 1];
792 if (strlen(program.cookie_prefix) + 1 +
793 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
794 return "[COOKIE ERROR]"; /* should never happen */
796 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
797 program.cookie_prefix, file_type,
798 program.version_major, program.version_minor);
803 int getFileVersionFromCookieString(const char *cookie)
805 const char *ptr_cookie1, *ptr_cookie2;
806 const char *pattern1 = "_FILE_VERSION_";
807 const char *pattern2 = "?.?";
808 const int len_cookie = strlen(cookie);
809 const int len_pattern1 = strlen(pattern1);
810 const int len_pattern2 = strlen(pattern2);
811 const int len_pattern = len_pattern1 + len_pattern2;
812 int version_major, version_minor;
814 if (len_cookie <= len_pattern)
817 ptr_cookie1 = &cookie[len_cookie - len_pattern];
818 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
820 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
823 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
824 ptr_cookie2[1] != '.' ||
825 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
828 version_major = ptr_cookie2[0] - '0';
829 version_minor = ptr_cookie2[2] - '0';
831 return VERSION_IDENT(version_major, version_minor, 0);
834 boolean checkCookieString(const char *cookie, const char *template)
836 const char *pattern = "_FILE_VERSION_?.?";
837 const int len_cookie = strlen(cookie);
838 const int len_template = strlen(template);
839 const int len_pattern = strlen(pattern);
841 if (len_cookie != len_template)
844 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
850 /* ------------------------------------------------------------------------- */
851 /* setup file list handling functions */
852 /* ------------------------------------------------------------------------- */
854 int get_string_integer_value(char *s)
856 static char *number_text[][3] =
858 { "0", "zero", "null", },
859 { "1", "one", "first" },
860 { "2", "two", "second" },
861 { "3", "three", "third" },
862 { "4", "four", "fourth" },
863 { "5", "five", "fifth" },
864 { "6", "six", "sixth" },
865 { "7", "seven", "seventh" },
866 { "8", "eight", "eighth" },
867 { "9", "nine", "ninth" },
868 { "10", "ten", "tenth" },
869 { "11", "eleven", "eleventh" },
870 { "12", "twelve", "twelfth" },
874 char *s_lower = getStringToLower(s);
879 if (strcmp(s_lower, number_text[i][j]) == 0)
890 boolean get_string_boolean_value(char *s)
892 char *s_lower = getStringToLower(s);
893 boolean result = FALSE;
895 if (strcmp(s_lower, "true") == 0 ||
896 strcmp(s_lower, "yes") == 0 ||
897 strcmp(s_lower, "on") == 0 ||
898 get_string_integer_value(s) == 1)
906 char *getFormattedSetupEntry(char *token, char *value)
909 static char entry[MAX_LINE_LEN];
911 sprintf(entry, "%s:", token);
912 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
916 strcat(entry, value);
921 void freeSetupFileList(struct SetupFileList *setup_file_list)
923 if (!setup_file_list)
926 if (setup_file_list->token)
927 free(setup_file_list->token);
928 if (setup_file_list->value)
929 free(setup_file_list->value);
930 if (setup_file_list->next)
931 freeSetupFileList(setup_file_list->next);
932 free(setup_file_list);
935 static struct SetupFileList *newSetupFileList(char *token, char *value)
937 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
939 new->token = checked_malloc(strlen(token) + 1);
940 strcpy(new->token, token);
942 new->value = checked_malloc(strlen(value) + 1);
943 strcpy(new->value, value);
950 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
952 if (!setup_file_list)
955 if (strcmp(setup_file_list->token, token) == 0)
956 return setup_file_list->value;
958 return getTokenValue(setup_file_list->next, token);
961 static void setTokenValue(struct SetupFileList *setup_file_list,
962 char *token, char *value)
964 if (!setup_file_list)
967 if (strcmp(setup_file_list->token, token) == 0)
969 free(setup_file_list->value);
970 setup_file_list->value = checked_malloc(strlen(value) + 1);
971 strcpy(setup_file_list->value, value);
973 else if (setup_file_list->next == NULL)
974 setup_file_list->next = newSetupFileList(token, value);
976 setTokenValue(setup_file_list->next, token, value);
980 static void printSetupFileList(struct SetupFileList *setup_file_list)
982 if (!setup_file_list)
985 printf("token: '%s'\n", setup_file_list->token);
986 printf("value: '%s'\n", setup_file_list->value);
988 printSetupFileList(setup_file_list->next);
992 struct SetupFileList *loadSetupFileList(char *filename)
995 char line[MAX_LINE_LEN];
996 char *token, *value, *line_ptr;
997 struct SetupFileList *setup_file_list = newSetupFileList("", "");
998 struct SetupFileList *first_valid_list_entry;
1002 if (!(file = fopen(filename, MODE_READ)))
1004 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1010 /* read next line of input file */
1011 if (!fgets(line, MAX_LINE_LEN, file))
1014 /* cut trailing comment or whitespace from input line */
1015 for (line_ptr = line; *line_ptr; line_ptr++)
1017 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1024 /* cut trailing whitespaces from input line */
1025 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1026 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1029 /* ignore empty lines */
1033 line_len = strlen(line);
1035 /* cut leading whitespaces from token */
1036 for (token = line; *token; token++)
1037 if (*token != ' ' && *token != '\t')
1040 /* find end of token */
1041 for (line_ptr = token; *line_ptr; line_ptr++)
1043 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1050 if (line_ptr < line + line_len)
1051 value = line_ptr + 1;
1055 /* cut leading whitespaces from value */
1056 for (; *value; value++)
1057 if (*value != ' ' && *value != '\t')
1060 if (*token && *value)
1061 setTokenValue(setup_file_list, token, value);
1066 first_valid_list_entry = setup_file_list->next;
1068 /* free empty list header */
1069 setup_file_list->next = NULL;
1070 freeSetupFileList(setup_file_list);
1072 if (first_valid_list_entry == NULL)
1073 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1075 return first_valid_list_entry;
1078 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1081 if (!setup_file_list)
1084 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1086 if (!checkCookieString(setup_file_list->value, identifier))
1088 Error(ERR_WARN, "configuration file has wrong file identifier");
1095 if (setup_file_list->next)
1096 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1099 Error(ERR_WARN, "configuration file has no file identifier");
1105 /* ========================================================================= */
1106 /* setup file stuff */
1107 /* ========================================================================= */
1109 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1110 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1111 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1113 /* level directory info */
1114 #define LEVELINFO_TOKEN_NAME 0
1115 #define LEVELINFO_TOKEN_NAME_SHORT 1
1116 #define LEVELINFO_TOKEN_NAME_SORTING 2
1117 #define LEVELINFO_TOKEN_AUTHOR 3
1118 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1119 #define LEVELINFO_TOKEN_LEVELS 5
1120 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1121 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1122 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1123 #define LEVELINFO_TOKEN_READONLY 9
1125 #define NUM_LEVELINFO_TOKENS 10
1127 static LevelDirTree ldi;
1129 static struct TokenInfo levelinfo_tokens[] =
1131 /* level directory info */
1132 { TYPE_STRING, &ldi.name, "name" },
1133 { TYPE_STRING, &ldi.name_short, "name_short" },
1134 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1135 { TYPE_STRING, &ldi.author, "author" },
1136 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1137 { TYPE_INTEGER, &ldi.levels, "levels" },
1138 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1139 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1140 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1141 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1144 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1148 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1149 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1150 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1151 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1154 ldi->node_parent = NULL;
1155 ldi->node_group = NULL;
1159 ldi->cl_cursor = -1;
1161 ldi->filename = NULL;
1162 ldi->fullpath = NULL;
1163 ldi->basepath = NULL;
1164 ldi->name = getStringCopy(ANONYMOUS_NAME);
1165 ldi->name_short = NULL;
1166 ldi->name_sorting = NULL;
1167 ldi->author = getStringCopy(ANONYMOUS_NAME);
1169 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1170 ldi->parent_link = FALSE;
1171 ldi->user_defined = FALSE;
1173 ldi->class_desc = NULL;
1175 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1177 ldi->imported_from = NULL;
1179 ldi->first_level = 0;
1180 ldi->last_level = 0;
1181 ldi->level_group = FALSE;
1182 ldi->handicap_level = 0;
1183 ldi->readonly = TRUE;
1187 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1191 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1193 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1197 /* first copy all values from the parent structure ... */
1200 /* ... then set all fields to default that cannot be inherited from parent.
1201 This is especially important for all those fields that can be set from
1202 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1203 calls 'free()' for all already set token values which requires that no
1204 other structure's pointer may point to them!
1207 ldi->filename = NULL;
1208 ldi->fullpath = NULL;
1209 ldi->basepath = NULL;
1210 ldi->name = getStringCopy(ANONYMOUS_NAME);
1211 ldi->name_short = NULL;
1212 ldi->name_sorting = NULL;
1213 ldi->author = getStringCopy(parent->author);
1214 ldi->imported_from = getStringCopy(parent->imported_from);
1216 ldi->level_group = FALSE;
1217 ldi->parent_link = FALSE;
1219 ldi->node_top = parent->node_top;
1220 ldi->node_parent = parent;
1221 ldi->node_group = NULL;
1225 void setSetupInfo(struct TokenInfo *token_info,
1226 int token_nr, char *token_value)
1228 int token_type = token_info[token_nr].type;
1229 void *setup_value = token_info[token_nr].value;
1231 if (token_value == NULL)
1234 /* set setup field to corresponding token value */
1239 *(boolean *)setup_value = get_string_boolean_value(token_value);
1243 *(Key *)setup_value = getKeyFromKeyName(token_value);
1247 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1251 *(int *)setup_value = get_string_integer_value(token_value);
1255 if (*(char **)setup_value != NULL)
1256 free(*(char **)setup_value);
1257 *(char **)setup_value = getStringCopy(token_value);
1265 static int compareTreeInfoEntries(const void *object1, const void *object2)
1267 const TreeInfo *entry1 = *((TreeInfo **)object1);
1268 const TreeInfo *entry2 = *((TreeInfo **)object2);
1271 if (entry1->parent_link || entry2->parent_link)
1272 compare_result = (entry1->parent_link ? -1 : +1);
1273 else if (entry1->sort_priority == entry2->sort_priority)
1275 char *name1 = getStringToLower(entry1->name_sorting);
1276 char *name2 = getStringToLower(entry2->name_sorting);
1278 compare_result = strcmp(name1, name2);
1283 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1284 compare_result = entry1->sort_priority - entry2->sort_priority;
1286 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1288 return compare_result;
1291 static void createParentTreeInfoNode(TreeInfo *node_parent)
1295 if (node_parent == NULL)
1298 ti_new = newTreeInfo();
1299 setTreeInfoToDefaults(ti_new, node_parent->type);
1301 ti_new->node_parent = node_parent;
1302 ti_new->parent_link = TRUE;
1304 ti_new->name = ".. (parent directory)";
1305 ti_new->name_short = getStringCopy(ti_new->name);
1306 ti_new->name_sorting = getStringCopy(ti_new->name);
1308 ti_new->filename = "..";
1309 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1311 ti_new->sort_priority = node_parent->sort_priority;
1312 ti_new->class_desc = getLevelClassDescription(ti_new);
1314 pushTreeInfo(&node_parent->node_group, ti_new);
1317 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1318 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1320 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1321 TreeInfo *node_parent,
1322 char *level_directory,
1323 char *directory_name)
1325 char *directory_path = getPath2(level_directory, directory_name);
1326 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1327 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1328 LevelDirTree *leveldir_new = NULL;
1331 if (setup_file_list == NULL)
1333 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1335 free(directory_path);
1341 leveldir_new = newTreeInfo();
1344 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1346 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1348 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1350 /* set all structure fields according to the token/value pairs */
1351 ldi = *leveldir_new;
1352 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1353 setSetupInfo(levelinfo_tokens, i,
1354 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1355 *leveldir_new = ldi;
1357 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1359 if (leveldir_new->name_short == NULL)
1360 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1362 if (leveldir_new->name_sorting == NULL)
1363 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1365 leveldir_new->filename = getStringCopy(directory_name);
1367 if (node_parent == NULL) /* top level group */
1369 leveldir_new->basepath = level_directory;
1370 leveldir_new->fullpath = leveldir_new->filename;
1372 else /* sub level group */
1374 leveldir_new->basepath = node_parent->basepath;
1375 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1378 if (leveldir_new->levels < 1)
1379 leveldir_new->levels = 1;
1381 leveldir_new->last_level =
1382 leveldir_new->first_level + leveldir_new->levels - 1;
1384 leveldir_new->user_defined =
1385 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1387 leveldir_new->color = LEVELCOLOR(leveldir_new);
1388 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1390 leveldir_new->handicap_level = /* set handicap to default value */
1391 (leveldir_new->user_defined ?
1392 leveldir_new->last_level :
1393 leveldir_new->first_level);
1395 pushTreeInfo(node_first, leveldir_new);
1397 freeSetupFileList(setup_file_list);
1399 if (leveldir_new->level_group)
1401 /* create node to link back to current level directory */
1402 createParentTreeInfoNode(leveldir_new);
1404 /* step into sub-directory and look for more level series */
1405 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1406 leveldir_new, directory_path);
1409 free(directory_path);
1415 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1416 TreeInfo *node_parent,
1417 char *level_directory)
1420 struct dirent *dir_entry;
1421 boolean valid_entry_found = FALSE;
1423 if ((dir = opendir(level_directory)) == NULL)
1425 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1429 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1431 struct stat file_status;
1432 char *directory_name = dir_entry->d_name;
1433 char *directory_path = getPath2(level_directory, directory_name);
1435 /* skip entries for current and parent directory */
1436 if (strcmp(directory_name, ".") == 0 ||
1437 strcmp(directory_name, "..") == 0)
1439 free(directory_path);
1443 /* find out if directory entry is itself a directory */
1444 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1445 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1447 free(directory_path);
1451 free(directory_path);
1453 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1460 if (!valid_entry_found)
1462 /* check if this directory directly contains a file "levelinfo.conf" */
1463 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1464 level_directory, ".");
1467 if (!valid_entry_found)
1468 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1472 void LoadLevelInfo()
1474 InitUserLevelDirectory(getLoginName());
1476 DrawInitText("Loading level series:", 120, FC_GREEN);
1478 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1479 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1481 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1483 if (leveldir_first == NULL)
1484 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1486 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1489 dumpTreeInfo(leveldir_first, 0);
1493 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1494 TreeInfo *node_parent,
1495 char *base_directory,
1496 char *directory_name, int type)
1498 char *directory_path = getPath2(base_directory, directory_name);
1500 getPath2(directory_path,
1501 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1502 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1503 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1504 struct SetupFileList *setup_file_list = NULL;
1505 TreeInfo *artwork_new = NULL;
1506 char *check_dir = NULL;
1509 if (access(getUserLevelDir(filename), F_OK) == 0) /* file exists */
1510 loadSetupFileList(filename);
1512 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1515 struct dirent *dir_entry;
1516 boolean valid_file_found = FALSE;
1518 if ((dir = opendir(base_directory)) != NULL)
1520 while ((dir_entry = readdir(dir)) != NULL)
1522 char *entry_name = dir_entry->d_name;
1524 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1525 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1526 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1528 valid_file_found = TRUE;
1536 if (!valid_file_found)
1538 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1540 free(directory_path);
1547 artwork_new = newTreeInfo();
1550 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1552 setTreeInfoToDefaults(artwork_new, type);
1554 artwork_new->filename = getStringCopy(directory_name);
1556 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1559 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1562 /* set all structure fields according to the token/value pairs */
1564 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1565 setSetupInfo(levelinfo_tokens, i,
1566 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1569 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1571 if (artwork_new->name_short == NULL)
1572 artwork_new->name_short = getStringCopy(artwork_new->name);
1574 if (artwork_new->name_sorting == NULL)
1575 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1578 if (node_parent == NULL) /* top level group */
1580 artwork_new->basepath = base_directory;
1581 artwork_new->fullpath = artwork_new->filename;
1583 else /* sub level group */
1585 artwork_new->basepath = node_parent->basepath;
1586 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1589 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1590 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1591 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1592 artwork_new->user_defined =
1593 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1595 /* (may use ".sort_priority" from "setup_file_list" above) */
1596 artwork_new->color = LEVELCOLOR(artwork_new);
1597 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1599 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1601 if (artwork_new->name != NULL)
1602 free(artwork_new->name);
1604 if (strcmp(artwork_new->filename, ".") == 0)
1606 if (artwork_new->user_defined)
1607 artwork_new->name = getStringCopy("private");
1609 artwork_new->name = getStringCopy("default");
1612 artwork_new->name = getStringCopy(artwork_new->filename);
1614 artwork_new->name_short = getStringCopy(artwork_new->name);
1615 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1618 pushTreeInfo(node_first, artwork_new);
1620 freeSetupFileList(setup_file_list);
1622 free(directory_path);
1628 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1629 TreeInfo *node_parent,
1630 char *base_directory, int type)
1633 struct dirent *dir_entry;
1634 boolean valid_entry_found = FALSE;
1636 if ((dir = opendir(base_directory)) == NULL)
1638 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1639 base_directory == options.graphics_directory) ||
1640 (type == TREE_TYPE_SOUNDS_DIR &&
1641 base_directory == options.sounds_directory) ||
1642 (type == TREE_TYPE_MUSIC_DIR &&
1643 base_directory == options.music_directory))
1644 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1648 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1650 struct stat file_status;
1651 char *directory_name = dir_entry->d_name;
1652 char *directory_path = getPath2(base_directory, directory_name);
1654 /* skip entries for current and parent directory */
1655 if (strcmp(directory_name, ".") == 0 ||
1656 strcmp(directory_name, "..") == 0)
1658 free(directory_path);
1662 /* find out if directory entry is itself a directory */
1663 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1664 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1666 free(directory_path);
1670 free(directory_path);
1672 /* check if this directory contains artwork with or without config file */
1673 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1675 directory_name, type);
1680 /* check if this directory directly contains artwork itself */
1681 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1682 base_directory, ".",
1684 if (!valid_entry_found)
1685 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1689 void LoadArtworkInfo()
1691 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1693 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1694 options.graphics_directory,
1695 TREE_TYPE_GRAPHICS_DIR);
1696 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1697 getUserGraphicsDir(),
1698 TREE_TYPE_GRAPHICS_DIR);
1700 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1701 options.sounds_directory,
1702 TREE_TYPE_SOUNDS_DIR);
1703 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1705 TREE_TYPE_SOUNDS_DIR);
1707 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1708 options.music_directory,
1709 TREE_TYPE_MUSIC_DIR);
1710 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1712 TREE_TYPE_MUSIC_DIR);
1714 /* before sorting, the first entries will be from the user directory */
1715 artwork.gfx_current = artwork.gfx_first;
1716 artwork.snd_current = artwork.snd_first;
1717 artwork.mus_current = artwork.mus_first;
1719 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1720 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1721 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1724 dumpTreeInfo(artwork.gfx_first, 0);
1725 dumpTreeInfo(artwork.snd_first, 0);
1726 dumpTreeInfo(artwork.mus_first, 0);
1730 static void SaveUserLevelInfo()
1736 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1738 if (!(file = fopen(filename, MODE_WRITE)))
1740 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1745 /* always start with reliable default values */
1746 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1748 ldi.name = getLoginName();
1749 ldi.author = getRealName();
1751 ldi.first_level = 1;
1752 ldi.sort_priority = LEVELCLASS_USER_START;
1753 ldi.readonly = FALSE;
1755 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1756 getCookie("LEVELINFO")));
1758 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1759 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1760 i != LEVELINFO_TOKEN_NAME_SORTING &&
1761 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1762 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1767 SetFilePermissions(filename, PERMS_PRIVATE);
1770 char *getSetupValue(int type, void *value)
1772 static char value_string[MAX_LINE_LEN];
1777 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1781 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1785 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1789 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1793 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1797 sprintf(value_string, "%d", *(int *)value);
1801 strcpy(value_string, *(char **)value);
1805 value_string[0] = '\0';
1809 return value_string;
1812 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1815 static char entry[MAX_LINE_LEN];
1816 int token_type = token_info[token_nr].type;
1817 void *setup_value = token_info[token_nr].value;
1818 char *token_text = token_info[token_nr].text;
1819 char *value_string = getSetupValue(token_type, setup_value);
1821 /* start with the prefix, token and some spaces to format output line */
1822 sprintf(entry, "%s%s:", prefix, token_text);
1823 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1826 /* continue with the token's value (which can have different types) */
1827 strcat(entry, value_string);
1829 if (token_type == TYPE_KEY_X11)
1831 Key key = *(Key *)setup_value;
1832 char *keyname = getKeyNameFromKey(key);
1834 /* add comment, if useful */
1835 if (strcmp(keyname, "(undefined)") != 0 &&
1836 strcmp(keyname, "(unknown)") != 0)
1838 for (i=strlen(entry); i<50; i++)
1841 strcat(entry, "# ");
1842 strcat(entry, keyname);
1849 void LoadLevelSetup_LastSeries()
1852 struct SetupFileList *level_setup_list = NULL;
1854 /* always start with reliable default values */
1855 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1857 /* ----------------------------------------------------------------------- */
1858 /* ~/.<program>/levelsetup.conf */
1859 /* ----------------------------------------------------------------------- */
1861 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1863 if ((level_setup_list = loadSetupFileList(filename)))
1865 char *last_level_series =
1866 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1868 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1870 if (leveldir_current == NULL)
1871 leveldir_current = leveldir_first;
1873 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1875 freeSetupFileList(level_setup_list);
1878 Error(ERR_WARN, "using default setup values");
1883 void SaveLevelSetup_LastSeries()
1886 char *level_subdir = leveldir_current->filename;
1889 /* ----------------------------------------------------------------------- */
1890 /* ~/.<program>/levelsetup.conf */
1891 /* ----------------------------------------------------------------------- */
1893 InitUserDataDirectory();
1895 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1897 if (!(file = fopen(filename, MODE_WRITE)))
1899 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1904 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1905 getCookie("LEVELSETUP")));
1906 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1912 SetFilePermissions(filename, PERMS_PRIVATE);
1915 static void checkSeriesInfo()
1917 static char *level_directory = NULL;
1919 struct dirent *dir_entry;
1921 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1923 level_directory = getPath2((leveldir_current->user_defined ?
1924 getUserLevelDir(NULL) :
1925 options.level_directory),
1926 leveldir_current->fullpath);
1928 if ((dir = opendir(level_directory)) == NULL)
1930 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1934 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1936 if (strlen(dir_entry->d_name) > 4 &&
1937 dir_entry->d_name[3] == '.' &&
1938 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1940 char levelnum_str[4];
1943 strncpy(levelnum_str, dir_entry->d_name, 3);
1944 levelnum_str[3] = '\0';
1946 levelnum_value = atoi(levelnum_str);
1948 if (levelnum_value < leveldir_current->first_level)
1950 Error(ERR_WARN, "additional level %d found", levelnum_value);
1951 leveldir_current->first_level = levelnum_value;
1953 else if (levelnum_value > leveldir_current->last_level)
1955 Error(ERR_WARN, "additional level %d found", levelnum_value);
1956 leveldir_current->last_level = levelnum_value;
1964 void LoadLevelSetup_SeriesInfo()
1967 struct SetupFileList *level_setup_list = NULL;
1968 char *level_subdir = leveldir_current->filename;
1970 /* always start with reliable default values */
1971 level_nr = leveldir_current->first_level;
1973 checkSeriesInfo(leveldir_current);
1975 /* ----------------------------------------------------------------------- */
1976 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1977 /* ----------------------------------------------------------------------- */
1979 level_subdir = leveldir_current->filename;
1981 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
1983 if ((level_setup_list = loadSetupFileList(filename)))
1987 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
1991 level_nr = atoi(token_value);
1993 if (level_nr < leveldir_current->first_level)
1994 level_nr = leveldir_current->first_level;
1995 if (level_nr > leveldir_current->last_level)
1996 level_nr = leveldir_current->last_level;
1999 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2003 int level_nr = atoi(token_value);
2005 if (level_nr < leveldir_current->first_level)
2006 level_nr = leveldir_current->first_level;
2007 if (level_nr > leveldir_current->last_level + 1)
2008 level_nr = leveldir_current->last_level;
2010 if (leveldir_current->user_defined)
2011 level_nr = leveldir_current->last_level;
2013 leveldir_current->handicap_level = level_nr;
2016 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2018 freeSetupFileList(level_setup_list);
2021 Error(ERR_WARN, "using default setup values");
2026 void SaveLevelSetup_SeriesInfo()
2029 char *level_subdir = leveldir_current->filename;
2030 char *level_nr_str = int2str(level_nr, 0);
2031 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2034 /* ----------------------------------------------------------------------- */
2035 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2036 /* ----------------------------------------------------------------------- */
2038 InitLevelSetupDirectory(level_subdir);
2040 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2042 if (!(file = fopen(filename, MODE_WRITE)))
2044 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2049 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2050 getCookie("LEVELSETUP")));
2051 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2053 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2054 handicap_level_str));
2059 SetFilePermissions(filename, PERMS_PRIVATE);