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(GRAPHICS_SUBDIR), 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 *getFirstValidTreeInfoEntry(TreeInfo *node)
506 if (node->node_top) /* start with first tree entry */
507 return getFirstValidTreeInfoEntry(*node->node_top);
511 else if (node->node_group) /* enter level group (step down into tree) */
512 return getFirstValidTreeInfoEntry(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 getFirstValidTreeInfoEntry(node->next);
517 else /* leave empty level group and go on */
518 return getFirstValidTreeInfoEntry(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 /* before sorting, the first entries will be from the user directory */
1482 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1484 if (leveldir_first == NULL)
1485 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1487 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1490 dumpTreeInfo(leveldir_first, 0);
1494 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1495 TreeInfo *node_parent,
1496 char *base_directory,
1497 char *directory_name, int type)
1499 char *directory_path = getPath2(base_directory, directory_name);
1501 getPath2(directory_path,
1502 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1503 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1504 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1505 struct SetupFileList *setup_file_list = NULL;
1506 TreeInfo *artwork_new = NULL;
1507 char *check_dir = NULL;
1510 if (access(filename, F_OK) == 0) /* file exists */
1511 loadSetupFileList(filename);
1513 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1516 struct dirent *dir_entry;
1517 boolean valid_file_found = FALSE;
1519 if ((dir = opendir(directory_path)) != NULL)
1521 while ((dir_entry = readdir(dir)) != NULL)
1523 char *entry_name = dir_entry->d_name;
1525 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1526 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1527 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1529 valid_file_found = TRUE;
1537 if (!valid_file_found)
1540 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1542 free(directory_path);
1549 artwork_new = newTreeInfo();
1552 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1554 setTreeInfoToDefaults(artwork_new, type);
1556 artwork_new->filename = getStringCopy(directory_name);
1558 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1561 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1564 /* set all structure fields according to the token/value pairs */
1566 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1567 setSetupInfo(levelinfo_tokens, i,
1568 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1571 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1573 if (artwork_new->name_short == NULL)
1574 artwork_new->name_short = getStringCopy(artwork_new->name);
1576 if (artwork_new->name_sorting == NULL)
1577 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1580 if (node_parent == NULL) /* top level group */
1582 artwork_new->basepath = base_directory;
1583 artwork_new->fullpath = artwork_new->filename;
1585 else /* sub level group */
1587 artwork_new->basepath = node_parent->basepath;
1588 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1591 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1592 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1593 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1594 artwork_new->user_defined =
1595 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1597 /* (may use ".sort_priority" from "setup_file_list" above) */
1598 artwork_new->color = LEVELCOLOR(artwork_new);
1599 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1601 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1603 if (artwork_new->name != NULL)
1604 free(artwork_new->name);
1606 if (strcmp(artwork_new->filename, ".") == 0)
1608 if (artwork_new->user_defined)
1609 artwork_new->name = getStringCopy("private");
1611 artwork_new->name = getStringCopy("default");
1614 artwork_new->name = getStringCopy(artwork_new->filename);
1616 artwork_new->name_short = getStringCopy(artwork_new->name);
1617 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1620 pushTreeInfo(node_first, artwork_new);
1622 freeSetupFileList(setup_file_list);
1624 free(directory_path);
1630 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1631 TreeInfo *node_parent,
1632 char *base_directory, int type)
1635 struct dirent *dir_entry;
1636 boolean valid_entry_found = FALSE;
1638 if ((dir = opendir(base_directory)) == NULL)
1640 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1641 base_directory == options.graphics_directory) ||
1642 (type == TREE_TYPE_SOUNDS_DIR &&
1643 base_directory == options.sounds_directory) ||
1644 (type == TREE_TYPE_MUSIC_DIR &&
1645 base_directory == options.music_directory))
1646 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1650 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1652 struct stat file_status;
1653 char *directory_name = dir_entry->d_name;
1654 char *directory_path = getPath2(base_directory, directory_name);
1656 /* skip entries for current and parent directory */
1657 if (strcmp(directory_name, ".") == 0 ||
1658 strcmp(directory_name, "..") == 0)
1660 free(directory_path);
1664 /* find out if directory entry is itself a directory */
1665 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1666 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1668 free(directory_path);
1672 free(directory_path);
1674 /* check if this directory contains artwork with or without config file */
1675 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1677 directory_name, type);
1682 /* check if this directory directly contains artwork itself */
1683 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1684 base_directory, ".",
1686 if (!valid_entry_found)
1687 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1691 void LoadArtworkInfo()
1693 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1695 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1696 options.graphics_directory,
1697 TREE_TYPE_GRAPHICS_DIR);
1698 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1699 getUserGraphicsDir(),
1700 TREE_TYPE_GRAPHICS_DIR);
1702 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1703 options.sounds_directory,
1704 TREE_TYPE_SOUNDS_DIR);
1705 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1707 TREE_TYPE_SOUNDS_DIR);
1709 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1710 options.music_directory,
1711 TREE_TYPE_MUSIC_DIR);
1712 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1714 TREE_TYPE_MUSIC_DIR);
1716 /* before sorting, the first entries will be from the user directory */
1717 artwork.gfx_current =
1718 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1719 if (artwork.gfx_current == NULL)
1720 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1722 artwork.snd_current =
1723 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1724 if (artwork.snd_current == NULL)
1725 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1727 artwork.mus_current =
1728 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1729 if (artwork.mus_current == NULL)
1730 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1732 artwork.graphics_set_current = artwork.gfx_current->name;
1733 artwork.sounds_set_current = artwork.snd_current->name;
1734 artwork.music_set_current = artwork.mus_current->name;
1736 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1737 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1738 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1741 dumpTreeInfo(artwork.gfx_first, 0);
1742 dumpTreeInfo(artwork.snd_first, 0);
1743 dumpTreeInfo(artwork.mus_first, 0);
1747 static void SaveUserLevelInfo()
1753 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1755 if (!(file = fopen(filename, MODE_WRITE)))
1757 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1762 /* always start with reliable default values */
1763 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1765 ldi.name = getLoginName();
1766 ldi.author = getRealName();
1768 ldi.first_level = 1;
1769 ldi.sort_priority = LEVELCLASS_USER_START;
1770 ldi.readonly = FALSE;
1772 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1773 getCookie("LEVELINFO")));
1775 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1776 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1777 i != LEVELINFO_TOKEN_NAME_SORTING &&
1778 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1779 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1784 SetFilePermissions(filename, PERMS_PRIVATE);
1787 char *getSetupValue(int type, void *value)
1789 static char value_string[MAX_LINE_LEN];
1794 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1798 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1802 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1806 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1810 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1814 sprintf(value_string, "%d", *(int *)value);
1818 strcpy(value_string, *(char **)value);
1822 value_string[0] = '\0';
1826 return value_string;
1829 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1832 static char entry[MAX_LINE_LEN];
1833 int token_type = token_info[token_nr].type;
1834 void *setup_value = token_info[token_nr].value;
1835 char *token_text = token_info[token_nr].text;
1836 char *value_string = getSetupValue(token_type, setup_value);
1838 /* start with the prefix, token and some spaces to format output line */
1839 sprintf(entry, "%s%s:", prefix, token_text);
1840 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1843 /* continue with the token's value (which can have different types) */
1844 strcat(entry, value_string);
1846 if (token_type == TYPE_KEY_X11)
1848 Key key = *(Key *)setup_value;
1849 char *keyname = getKeyNameFromKey(key);
1851 /* add comment, if useful */
1852 if (strcmp(keyname, "(undefined)") != 0 &&
1853 strcmp(keyname, "(unknown)") != 0)
1855 for (i=strlen(entry); i<50; i++)
1858 strcat(entry, "# ");
1859 strcat(entry, keyname);
1866 void LoadLevelSetup_LastSeries()
1869 struct SetupFileList *level_setup_list = NULL;
1871 /* always start with reliable default values */
1872 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1874 /* ----------------------------------------------------------------------- */
1875 /* ~/.<program>/levelsetup.conf */
1876 /* ----------------------------------------------------------------------- */
1878 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1880 if ((level_setup_list = loadSetupFileList(filename)))
1882 char *last_level_series =
1883 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1885 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1887 if (leveldir_current == NULL)
1888 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1890 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1892 freeSetupFileList(level_setup_list);
1895 Error(ERR_WARN, "using default setup values");
1900 void SaveLevelSetup_LastSeries()
1903 char *level_subdir = leveldir_current->filename;
1906 /* ----------------------------------------------------------------------- */
1907 /* ~/.<program>/levelsetup.conf */
1908 /* ----------------------------------------------------------------------- */
1910 InitUserDataDirectory();
1912 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1914 if (!(file = fopen(filename, MODE_WRITE)))
1916 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1921 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1922 getCookie("LEVELSETUP")));
1923 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1929 SetFilePermissions(filename, PERMS_PRIVATE);
1932 static void checkSeriesInfo()
1934 static char *level_directory = NULL;
1936 struct dirent *dir_entry;
1938 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1940 level_directory = getPath2((leveldir_current->user_defined ?
1941 getUserLevelDir(NULL) :
1942 options.level_directory),
1943 leveldir_current->fullpath);
1945 if ((dir = opendir(level_directory)) == NULL)
1947 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1951 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1953 if (strlen(dir_entry->d_name) > 4 &&
1954 dir_entry->d_name[3] == '.' &&
1955 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1957 char levelnum_str[4];
1960 strncpy(levelnum_str, dir_entry->d_name, 3);
1961 levelnum_str[3] = '\0';
1963 levelnum_value = atoi(levelnum_str);
1965 if (levelnum_value < leveldir_current->first_level)
1967 Error(ERR_WARN, "additional level %d found", levelnum_value);
1968 leveldir_current->first_level = levelnum_value;
1970 else if (levelnum_value > leveldir_current->last_level)
1972 Error(ERR_WARN, "additional level %d found", levelnum_value);
1973 leveldir_current->last_level = levelnum_value;
1981 void LoadLevelSetup_SeriesInfo()
1984 struct SetupFileList *level_setup_list = NULL;
1985 char *level_subdir = leveldir_current->filename;
1987 /* always start with reliable default values */
1988 level_nr = leveldir_current->first_level;
1990 checkSeriesInfo(leveldir_current);
1992 /* ----------------------------------------------------------------------- */
1993 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
1994 /* ----------------------------------------------------------------------- */
1996 level_subdir = leveldir_current->filename;
1998 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2000 if ((level_setup_list = loadSetupFileList(filename)))
2004 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2008 level_nr = atoi(token_value);
2010 if (level_nr < leveldir_current->first_level)
2011 level_nr = leveldir_current->first_level;
2012 if (level_nr > leveldir_current->last_level)
2013 level_nr = leveldir_current->last_level;
2016 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2020 int level_nr = atoi(token_value);
2022 if (level_nr < leveldir_current->first_level)
2023 level_nr = leveldir_current->first_level;
2024 if (level_nr > leveldir_current->last_level + 1)
2025 level_nr = leveldir_current->last_level;
2027 if (leveldir_current->user_defined)
2028 level_nr = leveldir_current->last_level;
2030 leveldir_current->handicap_level = level_nr;
2033 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2035 freeSetupFileList(level_setup_list);
2038 Error(ERR_WARN, "using default setup values");
2043 void SaveLevelSetup_SeriesInfo()
2046 char *level_subdir = leveldir_current->filename;
2047 char *level_nr_str = int2str(level_nr, 0);
2048 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2051 /* ----------------------------------------------------------------------- */
2052 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2053 /* ----------------------------------------------------------------------- */
2055 InitLevelSetupDirectory(level_subdir);
2057 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2059 if (!(file = fopen(filename, MODE_WRITE)))
2061 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2066 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2067 getCookie("LEVELSETUP")));
2068 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2070 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2071 handicap_level_str));
2076 SetFilePermissions(filename, PERMS_PRIVATE);