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 40
83 #define TOKEN_COMMENT_POSITION 60
85 #define MAX_COOKIE_LEN 256
87 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
88 GRAPHICSINFO_FILENAME : \
89 (type) == TREE_TYPE_SOUNDS_DIR ? \
90 SOUNDSINFO_FILENAME : \
91 (type) == TREE_TYPE_MUSIC_DIR ? \
92 MUSICINFO_FILENAME : "")
94 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
95 GRAPHICS_DIRECTORY : \
96 (type) == TREE_TYPE_SOUNDS_DIR ? \
98 (type) == TREE_TYPE_MUSIC_DIR ? \
101 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
102 options.graphics_directory : \
103 (type) == TREE_TYPE_SOUNDS_DIR ? \
104 options.sounds_directory : \
105 (type) == TREE_TYPE_MUSIC_DIR ? \
106 options.music_directory : "")
109 /* ------------------------------------------------------------------------- */
111 /* ------------------------------------------------------------------------- */
113 static char *getLevelClassDescription(TreeInfo *ldi)
115 int position = ldi->sort_priority / 100;
117 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
118 return levelclass_desc[position];
120 return "Unknown Level Class";
123 static char *getUserLevelDir(char *level_subdir)
125 static char *userlevel_dir = NULL;
126 char *data_dir = getUserDataDir();
127 char *userlevel_subdir = LEVELS_DIRECTORY;
132 if (level_subdir != NULL)
133 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
135 userlevel_dir = getPath2(data_dir, userlevel_subdir);
137 return userlevel_dir;
140 static char *getTapeDir(char *level_subdir)
142 static char *tape_dir = NULL;
143 char *data_dir = getUserDataDir();
144 char *tape_subdir = TAPES_DIRECTORY;
149 if (level_subdir != NULL)
150 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
152 tape_dir = getPath2(data_dir, tape_subdir);
157 static char *getScoreDir(char *level_subdir)
159 static char *score_dir = NULL;
160 char *data_dir = options.rw_base_directory;
161 char *score_subdir = SCORES_DIRECTORY;
166 if (level_subdir != NULL)
167 score_dir = getPath3(data_dir, score_subdir, level_subdir);
169 score_dir = getPath2(data_dir, score_subdir);
174 static char *getLevelSetupDir(char *level_subdir)
176 static char *levelsetup_dir = NULL;
177 char *data_dir = getUserDataDir();
178 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
181 free(levelsetup_dir);
183 if (level_subdir != NULL)
184 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
186 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
188 return levelsetup_dir;
191 static char *getLevelDirFromTreeInfo(TreeInfo *node)
193 static char *level_dir = NULL;
196 return options.level_directory;
201 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
202 options.level_directory), node->fullpath);
207 static char *getCurrentLevelDir()
209 return getLevelDirFromTreeInfo(leveldir_current);
212 static char *getDefaultGraphicsDir(char *graphics_subdir)
214 static char *graphics_dir = NULL;
216 if (graphics_subdir == NULL)
217 return options.graphics_directory;
222 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
227 static char *getDefaultSoundsDir(char *sounds_subdir)
229 static char *sounds_dir = NULL;
231 if (sounds_subdir == NULL)
232 return options.sounds_directory;
237 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
242 static char *getDefaultMusicDir(char *music_subdir)
244 static char *music_dir = NULL;
246 if (music_subdir == NULL)
247 return options.music_directory;
252 music_dir = getPath2(options.music_directory, music_subdir);
257 static char *getUserGraphicsDir()
259 static char *usergraphics_dir = NULL;
261 if (usergraphics_dir == NULL)
262 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
264 return usergraphics_dir;
267 static char *getUserSoundsDir()
269 static char *usersounds_dir = NULL;
271 if (usersounds_dir == NULL)
272 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
274 return usersounds_dir;
277 static char *getUserMusicDir()
279 static char *usermusic_dir = NULL;
281 if (usermusic_dir == NULL)
282 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
284 return usermusic_dir;
287 char *getLevelFilename(int nr)
289 static char *filename = NULL;
290 char basename[MAX_FILENAME_LEN];
292 if (filename != NULL)
295 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
296 filename = getPath2(getCurrentLevelDir(), basename);
301 char *getTapeFilename(int nr)
303 static char *filename = NULL;
304 char basename[MAX_FILENAME_LEN];
306 if (filename != NULL)
309 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
310 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
315 char *getScoreFilename(int nr)
317 static char *filename = NULL;
318 char basename[MAX_FILENAME_LEN];
320 if (filename != NULL)
323 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
324 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
329 char *getSetupFilename()
331 static char *filename = NULL;
333 if (filename != NULL)
336 filename = getPath2(getSetupDir(), SETUP_FILENAME);
341 static char *getSetupArtworkDir(TreeInfo *ti)
343 static char *artwork_dir = NULL;
345 if (artwork_dir != NULL)
348 artwork_dir = getPath2(ti->basepath, ti->fullpath);
353 static char *getCorrectedImageBasename(char *basename)
355 char *result = basename;
357 #if defined(PLATFORM_MSDOS)
358 if (program.filename_prefix != NULL)
360 int prefix_len = strlen(program.filename_prefix);
362 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
363 result = &basename[prefix_len];
370 static boolean fileExists(char *filename)
373 printf("checking file '%s'\n", filename);
376 return (access(filename, F_OK) == 0);
379 char *getCustomImageFilename(char *basename)
381 static char *filename = NULL;
383 if (filename != NULL)
386 basename = getCorrectedImageBasename(basename);
388 if (!setup.override_level_graphics)
390 /* 1st try: look for special artwork in current level series directory */
391 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
392 if (fileExists(filename))
396 /* 2nd try: look for special artwork in configured artwork directory */
397 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
398 if (fileExists(filename))
401 /* 3rd try: look for default artwork in new default artwork directory */
402 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
403 if (fileExists(filename))
406 /* 4th try: look for default artwork in old default artwork directory */
407 filename = getPath2(options.graphics_directory, basename);
408 if (fileExists(filename))
411 return NULL; /* cannot find specified artwork file anywhere */
414 char *getCustomSoundFilename(char *basename)
416 static char *filename = NULL;
418 if (filename != NULL)
421 if (!setup.override_level_sounds)
423 /* 1st try: look for special artwork in current level series directory */
424 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
425 if (fileExists(filename))
429 /* 2nd try: look for special artwork in configured artwork directory */
430 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
431 if (fileExists(filename))
434 /* 3rd try: look for default artwork in new default artwork directory */
435 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
436 if (fileExists(filename))
439 /* 4th try: look for default artwork in old default artwork directory */
440 filename = getPath2(options.sounds_directory, basename);
441 if (fileExists(filename))
444 return NULL; /* cannot find specified artwork file anywhere */
447 char *getCustomSoundConfigFilename()
449 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
452 char *getCustomMusicDirectory(void)
454 static char *directory = NULL;
456 if (directory != NULL)
459 if (!setup.override_level_music)
461 /* 1st try: look for special artwork in current level series directory */
462 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
463 if (fileExists(directory))
467 /* 2nd try: look for special artwork in configured artwork directory */
468 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
469 if (fileExists(directory))
472 /* 3rd try: look for default artwork in new default artwork directory */
473 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
474 if (fileExists(directory))
477 /* 4th try: look for default artwork in old default artwork directory */
478 directory = getStringCopy(options.music_directory);
479 if (fileExists(directory))
482 return NULL; /* cannot find specified artwork file anywhere */
485 void InitTapeDirectory(char *level_subdir)
487 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
488 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
489 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
492 void InitScoreDirectory(char *level_subdir)
494 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
495 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
498 static void SaveUserLevelInfo();
500 void InitUserLevelDirectory(char *level_subdir)
502 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
504 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
505 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
506 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
512 void InitLevelSetupDirectory(char *level_subdir)
514 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
515 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
516 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
520 /* ------------------------------------------------------------------------- */
521 /* some functions to handle lists of level directories */
522 /* ------------------------------------------------------------------------- */
524 TreeInfo *newTreeInfo()
526 return checked_calloc(sizeof(TreeInfo));
529 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
531 node_new->next = *node_first;
532 *node_first = node_new;
535 int numTreeInfo(TreeInfo *node)
548 boolean validLevelSeries(TreeInfo *node)
550 return (node != NULL && !node->node_group && !node->parent_link);
553 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
557 if (node->node_top) /* start with first tree entry */
558 return getFirstValidTreeInfoEntry(*node->node_top);
562 else if (node->node_group) /* enter level group (step down into tree) */
563 return getFirstValidTreeInfoEntry(node->node_group);
564 else if (node->parent_link) /* skip start entry of level group */
566 if (node->next) /* get first real level series entry */
567 return getFirstValidTreeInfoEntry(node->next);
568 else /* leave empty level group and go on */
569 return getFirstValidTreeInfoEntry(node->node_parent->next);
571 else /* this seems to be a regular level series */
575 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
580 if (node->node_parent == NULL) /* top level group */
581 return *node->node_top;
582 else /* sub level group */
583 return node->node_parent->node_group;
586 int numTreeInfoInGroup(TreeInfo *node)
588 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
591 int posTreeInfo(TreeInfo *node)
593 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
598 if (node_cmp == node)
602 node_cmp = node_cmp->next;
608 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
610 TreeInfo *node_default = node;
625 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
627 if (filename == NULL)
632 if (node->node_group)
634 TreeInfo *node_group;
636 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
641 else if (!node->parent_link)
643 if (strcmp(filename, node->filename) == 0)
653 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
655 return getTreeInfoFromFilenameExt(ti, filename);
658 void dumpTreeInfo(TreeInfo *node, int depth)
662 printf("Dumping TreeInfo:\n");
666 for (i=0; i<(depth + 1) * 3; i++)
669 printf("filename == '%s' (%s) [%s]\n",
670 node->filename, node->name, node->name_short);
672 if (node->node_group != NULL)
673 dumpTreeInfo(node->node_group, depth + 1);
679 void sortTreeInfo(TreeInfo **node_first,
680 int (*compare_function)(const void *, const void *))
682 int num_nodes = numTreeInfo(*node_first);
683 TreeInfo **sort_array;
684 TreeInfo *node = *node_first;
690 /* allocate array for sorting structure pointers */
691 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
693 /* writing structure pointers to sorting array */
694 while (i < num_nodes && node) /* double boundary check... */
696 sort_array[i] = node;
702 /* sorting the structure pointers in the sorting array */
703 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
706 /* update the linkage of list elements with the sorted node array */
707 for (i=0; i<num_nodes - 1; i++)
708 sort_array[i]->next = sort_array[i + 1];
709 sort_array[num_nodes - 1]->next = NULL;
711 /* update the linkage of the main list anchor pointer */
712 *node_first = sort_array[0];
716 /* now recursively sort the level group structures */
720 if (node->node_group != NULL)
721 sortTreeInfo(&node->node_group, compare_function);
728 /* ========================================================================= */
729 /* some stuff from "files.c" */
730 /* ========================================================================= */
732 #if defined(PLATFORM_WIN32)
734 #define S_IRGRP S_IRUSR
737 #define S_IROTH S_IRUSR
740 #define S_IWGRP S_IWUSR
743 #define S_IWOTH S_IWUSR
746 #define S_IXGRP S_IXUSR
749 #define S_IXOTH S_IXUSR
752 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
757 #endif /* PLATFORM_WIN32 */
759 /* file permissions for newly written files */
760 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
761 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
762 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
764 #define MODE_W_PRIVATE (S_IWUSR)
765 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
766 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
768 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
769 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
771 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
772 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
774 char *getUserDataDir(void)
776 static char *userdata_dir = NULL;
780 char *home_dir = getHomeDir();
781 char *data_dir = program.userdata_directory;
783 userdata_dir = getPath2(home_dir, data_dir);
791 return getUserDataDir();
794 static mode_t posix_umask(mode_t mask)
796 #if defined(PLATFORM_UNIX)
803 static int posix_mkdir(const char *pathname, mode_t mode)
805 #if defined(PLATFORM_WIN32)
806 return mkdir(pathname);
808 return mkdir(pathname, mode);
812 void createDirectory(char *dir, char *text, int permission_class)
814 /* leave "other" permissions in umask untouched, but ensure group parts
815 of USERDATA_DIR_MODE are not masked */
816 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
817 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
818 mode_t normal_umask = posix_umask(0);
819 mode_t group_umask = ~(dir_mode & S_IRWXG);
820 posix_umask(normal_umask & group_umask);
822 if (access(dir, F_OK) != 0)
823 if (posix_mkdir(dir, dir_mode) != 0)
824 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
826 posix_umask(normal_umask); /* reset normal umask */
829 void InitUserDataDirectory()
831 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
834 void SetFilePermissions(char *filename, int permission_class)
836 chmod(filename, (permission_class == PERMS_PRIVATE ?
837 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
840 char *getCookie(char *file_type)
842 static char cookie[MAX_COOKIE_LEN + 1];
844 if (strlen(program.cookie_prefix) + 1 +
845 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
846 return "[COOKIE ERROR]"; /* should never happen */
848 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
849 program.cookie_prefix, file_type,
850 program.version_major, program.version_minor);
855 int getFileVersionFromCookieString(const char *cookie)
857 const char *ptr_cookie1, *ptr_cookie2;
858 const char *pattern1 = "_FILE_VERSION_";
859 const char *pattern2 = "?.?";
860 const int len_cookie = strlen(cookie);
861 const int len_pattern1 = strlen(pattern1);
862 const int len_pattern2 = strlen(pattern2);
863 const int len_pattern = len_pattern1 + len_pattern2;
864 int version_major, version_minor;
866 if (len_cookie <= len_pattern)
869 ptr_cookie1 = &cookie[len_cookie - len_pattern];
870 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
872 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
875 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
876 ptr_cookie2[1] != '.' ||
877 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
880 version_major = ptr_cookie2[0] - '0';
881 version_minor = ptr_cookie2[2] - '0';
883 return VERSION_IDENT(version_major, version_minor, 0);
886 boolean checkCookieString(const char *cookie, const char *template)
888 const char *pattern = "_FILE_VERSION_?.?";
889 const int len_cookie = strlen(cookie);
890 const int len_template = strlen(template);
891 const int len_pattern = strlen(pattern);
893 if (len_cookie != len_template)
896 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
902 /* ------------------------------------------------------------------------- */
903 /* setup file list handling functions */
904 /* ------------------------------------------------------------------------- */
906 int get_string_integer_value(char *s)
908 static char *number_text[][3] =
910 { "0", "zero", "null", },
911 { "1", "one", "first" },
912 { "2", "two", "second" },
913 { "3", "three", "third" },
914 { "4", "four", "fourth" },
915 { "5", "five", "fifth" },
916 { "6", "six", "sixth" },
917 { "7", "seven", "seventh" },
918 { "8", "eight", "eighth" },
919 { "9", "nine", "ninth" },
920 { "10", "ten", "tenth" },
921 { "11", "eleven", "eleventh" },
922 { "12", "twelve", "twelfth" },
926 char *s_lower = getStringToLower(s);
931 if (strcmp(s_lower, number_text[i][j]) == 0)
942 boolean get_string_boolean_value(char *s)
944 char *s_lower = getStringToLower(s);
945 boolean result = FALSE;
947 if (strcmp(s_lower, "true") == 0 ||
948 strcmp(s_lower, "yes") == 0 ||
949 strcmp(s_lower, "on") == 0 ||
950 get_string_integer_value(s) == 1)
958 char *getFormattedSetupEntry(char *token, char *value)
961 static char entry[MAX_LINE_LEN];
963 /* start with the token and some spaces to format output line */
964 sprintf(entry, "%s:", token);
965 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
968 /* continue with the token's value */
969 strcat(entry, value);
974 void freeSetupFileList(struct SetupFileList *setup_file_list)
976 if (!setup_file_list)
979 if (setup_file_list->token)
980 free(setup_file_list->token);
981 if (setup_file_list->value)
982 free(setup_file_list->value);
983 if (setup_file_list->next)
984 freeSetupFileList(setup_file_list->next);
985 free(setup_file_list);
988 static struct SetupFileList *newSetupFileList(char *token, char *value)
990 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
992 new->token = checked_malloc(strlen(token) + 1);
993 strcpy(new->token, token);
995 new->value = checked_malloc(strlen(value) + 1);
996 strcpy(new->value, value);
1003 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1005 if (!setup_file_list)
1008 if (strcmp(setup_file_list->token, token) == 0)
1009 return setup_file_list->value;
1011 return getTokenValue(setup_file_list->next, token);
1014 static void setTokenValue(struct SetupFileList *setup_file_list,
1015 char *token, char *value)
1017 if (!setup_file_list)
1020 if (strcmp(setup_file_list->token, token) == 0)
1022 free(setup_file_list->value);
1023 setup_file_list->value = checked_malloc(strlen(value) + 1);
1024 strcpy(setup_file_list->value, value);
1026 else if (setup_file_list->next == NULL)
1027 setup_file_list->next = newSetupFileList(token, value);
1029 setTokenValue(setup_file_list->next, token, value);
1033 static void printSetupFileList(struct SetupFileList *setup_file_list)
1035 if (!setup_file_list)
1038 printf("token: '%s'\n", setup_file_list->token);
1039 printf("value: '%s'\n", setup_file_list->value);
1041 printSetupFileList(setup_file_list->next);
1045 struct SetupFileList *loadSetupFileList(char *filename)
1048 char line[MAX_LINE_LEN];
1049 char *token, *value, *line_ptr;
1050 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1051 struct SetupFileList *first_valid_list_entry;
1055 if (!(file = fopen(filename, MODE_READ)))
1057 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1063 /* read next line of input file */
1064 if (!fgets(line, MAX_LINE_LEN, file))
1067 /* cut trailing comment or whitespace from input line */
1068 for (line_ptr = line; *line_ptr; line_ptr++)
1070 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1077 /* cut trailing whitespaces from input line */
1078 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1079 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1082 /* ignore empty lines */
1086 line_len = strlen(line);
1088 /* cut leading whitespaces from token */
1089 for (token = line; *token; token++)
1090 if (*token != ' ' && *token != '\t')
1093 /* find end of token */
1094 for (line_ptr = token; *line_ptr; line_ptr++)
1096 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1103 if (line_ptr < line + line_len)
1104 value = line_ptr + 1;
1108 /* cut leading whitespaces from value */
1109 for (; *value; value++)
1110 if (*value != ' ' && *value != '\t')
1113 if (*token && *value)
1114 setTokenValue(setup_file_list, token, value);
1119 first_valid_list_entry = setup_file_list->next;
1121 /* free empty list header */
1122 setup_file_list->next = NULL;
1123 freeSetupFileList(setup_file_list);
1125 if (first_valid_list_entry == NULL)
1126 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1128 return first_valid_list_entry;
1131 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1134 if (!setup_file_list)
1137 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1139 if (!checkCookieString(setup_file_list->value, identifier))
1141 Error(ERR_WARN, "configuration file has wrong file identifier");
1148 if (setup_file_list->next)
1149 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1152 Error(ERR_WARN, "configuration file has no file identifier");
1158 /* ========================================================================= */
1159 /* setup file stuff */
1160 /* ========================================================================= */
1162 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1163 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1164 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1166 /* level directory info */
1167 #define LEVELINFO_TOKEN_NAME 0
1168 #define LEVELINFO_TOKEN_NAME_SHORT 1
1169 #define LEVELINFO_TOKEN_NAME_SORTING 2
1170 #define LEVELINFO_TOKEN_AUTHOR 3
1171 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1172 #define LEVELINFO_TOKEN_LEVELS 5
1173 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1174 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1175 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1176 #define LEVELINFO_TOKEN_READONLY 9
1178 #define NUM_LEVELINFO_TOKENS 10
1180 static LevelDirTree ldi;
1182 static struct TokenInfo levelinfo_tokens[] =
1184 /* level directory info */
1185 { TYPE_STRING, &ldi.name, "name" },
1186 { TYPE_STRING, &ldi.name_short, "name_short" },
1187 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1188 { TYPE_STRING, &ldi.author, "author" },
1189 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1190 { TYPE_INTEGER, &ldi.levels, "levels" },
1191 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1192 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1193 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1194 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1197 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1201 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1202 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1203 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1204 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1207 ldi->node_parent = NULL;
1208 ldi->node_group = NULL;
1212 ldi->cl_cursor = -1;
1214 ldi->filename = NULL;
1215 ldi->fullpath = NULL;
1216 ldi->basepath = NULL;
1217 ldi->name = getStringCopy(ANONYMOUS_NAME);
1218 ldi->name_short = NULL;
1219 ldi->name_sorting = NULL;
1220 ldi->author = getStringCopy(ANONYMOUS_NAME);
1222 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1223 ldi->parent_link = FALSE;
1224 ldi->user_defined = FALSE;
1226 ldi->class_desc = NULL;
1228 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1230 ldi->imported_from = NULL;
1232 ldi->first_level = 0;
1233 ldi->last_level = 0;
1234 ldi->level_group = FALSE;
1235 ldi->handicap_level = 0;
1236 ldi->readonly = TRUE;
1240 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1244 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1246 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1250 /* first copy all values from the parent structure ... */
1253 /* ... then set all fields to default that cannot be inherited from parent.
1254 This is especially important for all those fields that can be set from
1255 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1256 calls 'free()' for all already set token values which requires that no
1257 other structure's pointer may point to them!
1260 ldi->filename = NULL;
1261 ldi->fullpath = NULL;
1262 ldi->basepath = NULL;
1263 ldi->name = getStringCopy(ANONYMOUS_NAME);
1264 ldi->name_short = NULL;
1265 ldi->name_sorting = NULL;
1266 ldi->author = getStringCopy(parent->author);
1267 ldi->imported_from = getStringCopy(parent->imported_from);
1269 ldi->level_group = FALSE;
1270 ldi->parent_link = FALSE;
1272 ldi->node_top = parent->node_top;
1273 ldi->node_parent = parent;
1274 ldi->node_group = NULL;
1278 void setSetupInfo(struct TokenInfo *token_info,
1279 int token_nr, char *token_value)
1281 int token_type = token_info[token_nr].type;
1282 void *setup_value = token_info[token_nr].value;
1284 if (token_value == NULL)
1287 /* set setup field to corresponding token value */
1292 *(boolean *)setup_value = get_string_boolean_value(token_value);
1296 *(Key *)setup_value = getKeyFromKeyName(token_value);
1300 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1304 *(int *)setup_value = get_string_integer_value(token_value);
1308 if (*(char **)setup_value != NULL)
1309 free(*(char **)setup_value);
1310 *(char **)setup_value = getStringCopy(token_value);
1318 static int compareTreeInfoEntries(const void *object1, const void *object2)
1320 const TreeInfo *entry1 = *((TreeInfo **)object1);
1321 const TreeInfo *entry2 = *((TreeInfo **)object2);
1324 if (entry1->parent_link || entry2->parent_link)
1325 compare_result = (entry1->parent_link ? -1 : +1);
1326 else if (entry1->sort_priority == entry2->sort_priority)
1328 char *name1 = getStringToLower(entry1->name_sorting);
1329 char *name2 = getStringToLower(entry2->name_sorting);
1331 compare_result = strcmp(name1, name2);
1336 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1337 compare_result = entry1->sort_priority - entry2->sort_priority;
1339 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1341 return compare_result;
1344 static void createParentTreeInfoNode(TreeInfo *node_parent)
1348 if (node_parent == NULL)
1351 ti_new = newTreeInfo();
1352 setTreeInfoToDefaults(ti_new, node_parent->type);
1354 ti_new->node_parent = node_parent;
1355 ti_new->parent_link = TRUE;
1357 ti_new->name = ".. (parent directory)";
1358 ti_new->name_short = getStringCopy(ti_new->name);
1359 ti_new->name_sorting = getStringCopy(ti_new->name);
1361 ti_new->filename = "..";
1362 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1364 ti_new->sort_priority = node_parent->sort_priority;
1365 ti_new->class_desc = getLevelClassDescription(ti_new);
1367 pushTreeInfo(&node_parent->node_group, ti_new);
1370 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1371 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1373 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1374 TreeInfo *node_parent,
1375 char *level_directory,
1376 char *directory_name)
1378 char *directory_path = getPath2(level_directory, directory_name);
1379 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1380 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1381 LevelDirTree *leveldir_new = NULL;
1384 if (setup_file_list == NULL)
1386 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1388 free(directory_path);
1394 leveldir_new = newTreeInfo();
1397 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1399 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1401 leveldir_new->filename = getStringCopy(directory_name);
1403 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1405 /* set all structure fields according to the token/value pairs */
1406 ldi = *leveldir_new;
1407 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1408 setSetupInfo(levelinfo_tokens, i,
1409 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1410 *leveldir_new = ldi;
1412 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1414 free(leveldir_new->name);
1415 leveldir_new->name = getStringCopy(leveldir_new->filename);
1418 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1420 if (leveldir_new->name_short == NULL)
1421 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1423 if (leveldir_new->name_sorting == NULL)
1424 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1426 if (node_parent == NULL) /* top level group */
1428 leveldir_new->basepath = level_directory;
1429 leveldir_new->fullpath = leveldir_new->filename;
1431 else /* sub level group */
1433 leveldir_new->basepath = node_parent->basepath;
1434 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1437 if (leveldir_new->levels < 1)
1438 leveldir_new->levels = 1;
1440 leveldir_new->last_level =
1441 leveldir_new->first_level + leveldir_new->levels - 1;
1443 leveldir_new->user_defined =
1444 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1446 leveldir_new->color = LEVELCOLOR(leveldir_new);
1447 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1449 leveldir_new->handicap_level = /* set handicap to default value */
1450 (leveldir_new->user_defined ?
1451 leveldir_new->last_level :
1452 leveldir_new->first_level);
1454 pushTreeInfo(node_first, leveldir_new);
1456 freeSetupFileList(setup_file_list);
1458 if (leveldir_new->level_group)
1460 /* create node to link back to current level directory */
1461 createParentTreeInfoNode(leveldir_new);
1463 /* step into sub-directory and look for more level series */
1464 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1465 leveldir_new, directory_path);
1468 free(directory_path);
1474 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1475 TreeInfo *node_parent,
1476 char *level_directory)
1479 struct dirent *dir_entry;
1480 boolean valid_entry_found = FALSE;
1482 if ((dir = opendir(level_directory)) == NULL)
1484 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1488 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1490 struct stat file_status;
1491 char *directory_name = dir_entry->d_name;
1492 char *directory_path = getPath2(level_directory, directory_name);
1494 /* skip entries for current and parent directory */
1495 if (strcmp(directory_name, ".") == 0 ||
1496 strcmp(directory_name, "..") == 0)
1498 free(directory_path);
1502 /* find out if directory entry is itself a directory */
1503 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1504 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1506 free(directory_path);
1510 free(directory_path);
1512 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1513 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1514 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1517 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1524 if (!valid_entry_found)
1526 /* check if this directory directly contains a file "levelinfo.conf" */
1527 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1528 level_directory, ".");
1531 if (!valid_entry_found)
1532 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1536 void LoadLevelInfo()
1538 InitUserLevelDirectory(getLoginName());
1540 DrawInitText("Loading level series:", 120, FC_GREEN);
1542 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1543 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1545 /* before sorting, the first entries will be from the user directory */
1546 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1548 if (leveldir_first == NULL)
1549 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1551 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1554 dumpTreeInfo(leveldir_first, 0);
1558 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1559 TreeInfo *node_parent,
1560 char *base_directory,
1561 char *directory_name, int type)
1563 char *directory_path = getPath2(base_directory, directory_name);
1564 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1565 struct SetupFileList *setup_file_list = NULL;
1566 TreeInfo *artwork_new = NULL;
1569 if (access(filename, F_OK) == 0) /* file exists */
1570 setup_file_list = loadSetupFileList(filename);
1572 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1575 struct dirent *dir_entry;
1576 boolean valid_file_found = FALSE;
1578 if ((dir = opendir(directory_path)) != NULL)
1580 while ((dir_entry = readdir(dir)) != NULL)
1582 char *entry_name = dir_entry->d_name;
1584 if (FileIsArtworkType(entry_name, type))
1586 valid_file_found = TRUE;
1594 if (!valid_file_found)
1596 if (strcmp(directory_name, ".") != 0)
1597 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1599 free(directory_path);
1606 artwork_new = newTreeInfo();
1609 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1611 setTreeInfoToDefaults(artwork_new, type);
1613 artwork_new->filename = getStringCopy(directory_name);
1615 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1618 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1621 /* set all structure fields according to the token/value pairs */
1623 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1624 setSetupInfo(levelinfo_tokens, i,
1625 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1628 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1630 free(artwork_new->name);
1631 artwork_new->name = getStringCopy(artwork_new->filename);
1634 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1636 if (artwork_new->name_short == NULL)
1637 artwork_new->name_short = getStringCopy(artwork_new->name);
1639 if (artwork_new->name_sorting == NULL)
1640 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1643 if (node_parent == NULL) /* top level group */
1645 artwork_new->basepath = getStringCopy(base_directory);
1646 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1648 else /* sub level group */
1650 artwork_new->basepath = getStringCopy(node_parent->basepath);
1651 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1654 artwork_new->user_defined =
1655 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1657 /* (may use ".sort_priority" from "setup_file_list" above) */
1658 artwork_new->color = LEVELCOLOR(artwork_new);
1659 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1661 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1663 if (artwork_new->name != NULL)
1664 free(artwork_new->name);
1666 if (strcmp(artwork_new->filename, ".") == 0)
1668 if (artwork_new->user_defined)
1670 artwork_new->name = getStringCopy("private");
1671 artwork_new->sort_priority = LEVELCLASS_USER;
1675 artwork_new->name = getStringCopy("classic");
1676 artwork_new->sort_priority = LEVELCLASS_CLASSICS;
1679 artwork_new->color = LEVELCOLOR(artwork_new);
1680 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1683 artwork_new->name = getStringCopy(artwork_new->filename);
1685 artwork_new->name_short = getStringCopy(artwork_new->name);
1686 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1689 pushTreeInfo(node_first, artwork_new);
1691 freeSetupFileList(setup_file_list);
1693 free(directory_path);
1699 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1700 TreeInfo *node_parent,
1701 char *base_directory, int type)
1704 struct dirent *dir_entry;
1705 boolean valid_entry_found = FALSE;
1707 if ((dir = opendir(base_directory)) == NULL)
1709 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1710 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1714 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1716 struct stat file_status;
1717 char *directory_name = dir_entry->d_name;
1718 char *directory_path = getPath2(base_directory, directory_name);
1720 /* skip entries for current and parent directory */
1721 if (strcmp(directory_name, ".") == 0 ||
1722 strcmp(directory_name, "..") == 0)
1724 free(directory_path);
1728 /* find out if directory entry is itself a directory */
1729 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1730 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1732 free(directory_path);
1736 free(directory_path);
1738 /* check if this directory contains artwork with or without config file */
1739 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1741 directory_name, type);
1746 /* check if this directory directly contains artwork itself */
1747 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1748 base_directory, ".",
1750 if (!valid_entry_found)
1751 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1755 void LoadArtworkInfo()
1757 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1759 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1760 options.graphics_directory,
1761 TREE_TYPE_GRAPHICS_DIR);
1762 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1763 getUserGraphicsDir(),
1764 TREE_TYPE_GRAPHICS_DIR);
1766 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1767 options.sounds_directory,
1768 TREE_TYPE_SOUNDS_DIR);
1769 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1771 TREE_TYPE_SOUNDS_DIR);
1773 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1774 options.music_directory,
1775 TREE_TYPE_MUSIC_DIR);
1776 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1778 TREE_TYPE_MUSIC_DIR);
1780 /* before sorting, the first entries will be from the user directory */
1781 artwork.gfx_current =
1782 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1783 if (artwork.gfx_current == NULL)
1784 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1786 artwork.snd_current =
1787 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1788 if (artwork.snd_current == NULL)
1789 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1791 artwork.mus_current =
1792 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1793 if (artwork.mus_current == NULL)
1794 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1796 artwork.graphics_set_current_name = artwork.gfx_current->name;
1797 artwork.sounds_set_current_name = artwork.snd_current->name;
1798 artwork.music_set_current_name = artwork.mus_current->name;
1801 printf("graphics set == %s\n\n", artwork.graphics_set_current_name);
1802 printf("sounds set == %s\n\n", artwork.sounds_set_current_name);
1803 printf("music set == %s\n\n", artwork.music_set_current_name);
1806 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1807 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1808 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1811 dumpTreeInfo(artwork.gfx_first, 0);
1812 dumpTreeInfo(artwork.snd_first, 0);
1813 dumpTreeInfo(artwork.mus_first, 0);
1817 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1818 LevelDirTree *level_node)
1820 /* recursively check all level directories for artwork sub-directories */
1824 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1825 ARTWORK_DIRECTORY((*artwork_node)->type));
1828 if (!level_node->parent_link)
1829 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1830 level_node->filename, level_node->name);
1833 if (!level_node->parent_link)
1835 TreeInfo *topnode_last = *artwork_node;
1837 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1838 (*artwork_node)->type);
1840 if (topnode_last != *artwork_node)
1842 free((*artwork_node)->name);
1843 free((*artwork_node)->name_sorting);
1844 free((*artwork_node)->name_short);
1846 (*artwork_node)->name = getStringCopy(level_node->name);
1847 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1848 (*artwork_node)->name_short = getStringCopy(level_node->filename);
1850 (*artwork_node)->sort_priority = level_node->sort_priority;
1851 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1857 if (level_node->node_group != NULL)
1858 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1860 level_node = level_node->next;
1864 void LoadLevelArtworkInfo()
1866 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1868 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1869 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1870 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1872 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1873 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1874 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1877 dumpTreeInfo(artwork.gfx_first, 0);
1878 dumpTreeInfo(artwork.snd_first, 0);
1879 dumpTreeInfo(artwork.mus_first, 0);
1883 static void SaveUserLevelInfo()
1889 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1891 if (!(file = fopen(filename, MODE_WRITE)))
1893 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1898 /* always start with reliable default values */
1899 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1901 ldi.name = getLoginName();
1902 ldi.author = getRealName();
1904 ldi.first_level = 1;
1905 ldi.sort_priority = LEVELCLASS_USER_START;
1906 ldi.readonly = FALSE;
1908 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1909 getCookie("LEVELINFO")));
1911 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1912 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1913 i != LEVELINFO_TOKEN_NAME_SORTING &&
1914 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1915 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1920 SetFilePermissions(filename, PERMS_PRIVATE);
1923 char *getSetupValue(int type, void *value)
1925 static char value_string[MAX_LINE_LEN];
1933 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1937 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1941 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1945 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1949 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1953 sprintf(value_string, "%d", *(int *)value);
1957 strcpy(value_string, *(char **)value);
1961 value_string[0] = '\0';
1965 return value_string;
1968 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1972 static char token_string[MAX_LINE_LEN];
1973 int token_type = token_info[token_nr].type;
1974 void *setup_value = token_info[token_nr].value;
1975 char *token_text = token_info[token_nr].text;
1976 char *value_string = getSetupValue(token_type, setup_value);
1978 /* build complete token string */
1979 sprintf(token_string, "%s%s", prefix, token_text);
1981 /* build setup entry line */
1982 line = getFormattedSetupEntry(token_string, value_string);
1984 if (token_type == TYPE_KEY_X11)
1986 Key key = *(Key *)setup_value;
1987 char *keyname = getKeyNameFromKey(key);
1989 /* add comment, if useful */
1990 if (strcmp(keyname, "(undefined)") != 0 &&
1991 strcmp(keyname, "(unknown)") != 0)
1993 /* add at least one whitespace */
1995 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
1999 strcat(line, keyname);
2006 void LoadLevelSetup_LastSeries()
2009 struct SetupFileList *level_setup_list = NULL;
2011 /* always start with reliable default values */
2012 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2014 /* ----------------------------------------------------------------------- */
2015 /* ~/.<program>/levelsetup.conf */
2016 /* ----------------------------------------------------------------------- */
2018 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2020 if ((level_setup_list = loadSetupFileList(filename)))
2022 char *last_level_series =
2023 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2025 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2027 if (leveldir_current == NULL)
2028 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2030 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2032 freeSetupFileList(level_setup_list);
2035 Error(ERR_WARN, "using default setup values");
2040 void SaveLevelSetup_LastSeries()
2043 char *level_subdir = leveldir_current->filename;
2046 /* ----------------------------------------------------------------------- */
2047 /* ~/.<program>/levelsetup.conf */
2048 /* ----------------------------------------------------------------------- */
2050 InitUserDataDirectory();
2052 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2054 if (!(file = fopen(filename, MODE_WRITE)))
2056 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2061 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2062 getCookie("LEVELSETUP")));
2063 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2069 SetFilePermissions(filename, PERMS_PRIVATE);
2072 static void checkSeriesInfo()
2074 static char *level_directory = NULL;
2076 struct dirent *dir_entry;
2078 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2080 level_directory = getPath2((leveldir_current->user_defined ?
2081 getUserLevelDir(NULL) :
2082 options.level_directory),
2083 leveldir_current->fullpath);
2085 if ((dir = opendir(level_directory)) == NULL)
2087 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2091 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2093 if (strlen(dir_entry->d_name) > 4 &&
2094 dir_entry->d_name[3] == '.' &&
2095 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2097 char levelnum_str[4];
2100 strncpy(levelnum_str, dir_entry->d_name, 3);
2101 levelnum_str[3] = '\0';
2103 levelnum_value = atoi(levelnum_str);
2105 if (levelnum_value < leveldir_current->first_level)
2107 Error(ERR_WARN, "additional level %d found", levelnum_value);
2108 leveldir_current->first_level = levelnum_value;
2110 else if (levelnum_value > leveldir_current->last_level)
2112 Error(ERR_WARN, "additional level %d found", levelnum_value);
2113 leveldir_current->last_level = levelnum_value;
2121 void LoadLevelSetup_SeriesInfo()
2124 struct SetupFileList *level_setup_list = NULL;
2125 char *level_subdir = leveldir_current->filename;
2127 /* always start with reliable default values */
2128 level_nr = leveldir_current->first_level;
2130 checkSeriesInfo(leveldir_current);
2132 /* ----------------------------------------------------------------------- */
2133 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2134 /* ----------------------------------------------------------------------- */
2136 level_subdir = leveldir_current->filename;
2138 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2140 if ((level_setup_list = loadSetupFileList(filename)))
2144 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2148 level_nr = atoi(token_value);
2150 if (level_nr < leveldir_current->first_level)
2151 level_nr = leveldir_current->first_level;
2152 if (level_nr > leveldir_current->last_level)
2153 level_nr = leveldir_current->last_level;
2156 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2160 int level_nr = atoi(token_value);
2162 if (level_nr < leveldir_current->first_level)
2163 level_nr = leveldir_current->first_level;
2164 if (level_nr > leveldir_current->last_level + 1)
2165 level_nr = leveldir_current->last_level;
2167 if (leveldir_current->user_defined)
2168 level_nr = leveldir_current->last_level;
2170 leveldir_current->handicap_level = level_nr;
2173 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2175 freeSetupFileList(level_setup_list);
2178 Error(ERR_WARN, "using default setup values");
2183 void SaveLevelSetup_SeriesInfo()
2186 char *level_subdir = leveldir_current->filename;
2187 char *level_nr_str = int2str(level_nr, 0);
2188 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2191 /* ----------------------------------------------------------------------- */
2192 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2193 /* ----------------------------------------------------------------------- */
2195 InitLevelSetupDirectory(level_subdir);
2197 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2199 if (!(file = fopen(filename, MODE_WRITE)))
2201 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2206 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2207 getCookie("LEVELSETUP")));
2208 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2210 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2211 handicap_level_str));
2216 SetFilePermissions(filename, PERMS_PRIVATE);