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)
558 if (node->node_group) /* enter level group (step down into tree) */
559 return getFirstValidTreeInfoEntry(node->node_group);
560 else if (node->parent_link) /* skip start entry of level group */
562 if (node->next) /* get first real level series entry */
563 return getFirstValidTreeInfoEntry(node->next);
564 else /* leave empty level group and go on */
565 return getFirstValidTreeInfoEntry(node->node_parent->next);
567 else /* this seems to be a regular level series */
571 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
576 if (node->node_parent == NULL) /* top level group */
577 return *node->node_top;
578 else /* sub level group */
579 return node->node_parent->node_group;
582 int numTreeInfoInGroup(TreeInfo *node)
584 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
587 int posTreeInfo(TreeInfo *node)
589 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
594 if (node_cmp == node)
598 node_cmp = node_cmp->next;
604 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
606 TreeInfo *node_default = node;
621 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
623 if (filename == NULL)
628 if (node->node_group)
630 TreeInfo *node_group;
632 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
637 else if (!node->parent_link)
639 if (strcmp(filename, node->filename) == 0)
642 /* special case when looking for level series artwork:
643 node->name_short contains level series filename */
644 if (strcmp(node->filename, ".") == 0 &&
645 strcmp(filename, node->name_short) == 0)
655 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
657 return getTreeInfoFromFilenameExt(ti, filename);
660 void dumpTreeInfo(TreeInfo *node, int depth)
664 printf("Dumping TreeInfo:\n");
668 for (i=0; i<(depth + 1) * 3; i++)
671 printf("filename == '%s' (%s) [%s]\n",
672 node->filename, node->name, node->name_short);
674 if (node->node_group != NULL)
675 dumpTreeInfo(node->node_group, depth + 1);
681 void sortTreeInfo(TreeInfo **node_first,
682 int (*compare_function)(const void *, const void *))
684 int num_nodes = numTreeInfo(*node_first);
685 TreeInfo **sort_array;
686 TreeInfo *node = *node_first;
692 /* allocate array for sorting structure pointers */
693 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
695 /* writing structure pointers to sorting array */
696 while (i < num_nodes && node) /* double boundary check... */
698 sort_array[i] = node;
704 /* sorting the structure pointers in the sorting array */
705 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
708 /* update the linkage of list elements with the sorted node array */
709 for (i=0; i<num_nodes - 1; i++)
710 sort_array[i]->next = sort_array[i + 1];
711 sort_array[num_nodes - 1]->next = NULL;
713 /* update the linkage of the main list anchor pointer */
714 *node_first = sort_array[0];
718 /* now recursively sort the level group structures */
722 if (node->node_group != NULL)
723 sortTreeInfo(&node->node_group, compare_function);
730 /* ========================================================================= */
731 /* some stuff from "files.c" */
732 /* ========================================================================= */
734 #if defined(PLATFORM_WIN32)
736 #define S_IRGRP S_IRUSR
739 #define S_IROTH S_IRUSR
742 #define S_IWGRP S_IWUSR
745 #define S_IWOTH S_IWUSR
748 #define S_IXGRP S_IXUSR
751 #define S_IXOTH S_IXUSR
754 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
759 #endif /* PLATFORM_WIN32 */
761 /* file permissions for newly written files */
762 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
763 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
764 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
766 #define MODE_W_PRIVATE (S_IWUSR)
767 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
768 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
770 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
771 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
773 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
774 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
776 char *getUserDataDir(void)
778 static char *userdata_dir = NULL;
782 char *home_dir = getHomeDir();
783 char *data_dir = program.userdata_directory;
785 userdata_dir = getPath2(home_dir, data_dir);
793 return getUserDataDir();
796 static mode_t posix_umask(mode_t mask)
798 #if defined(PLATFORM_UNIX)
805 static int posix_mkdir(const char *pathname, mode_t mode)
807 #if defined(PLATFORM_WIN32)
808 return mkdir(pathname);
810 return mkdir(pathname, mode);
814 void createDirectory(char *dir, char *text, int permission_class)
816 /* leave "other" permissions in umask untouched, but ensure group parts
817 of USERDATA_DIR_MODE are not masked */
818 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
819 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
820 mode_t normal_umask = posix_umask(0);
821 mode_t group_umask = ~(dir_mode & S_IRWXG);
822 posix_umask(normal_umask & group_umask);
824 if (access(dir, F_OK) != 0)
825 if (posix_mkdir(dir, dir_mode) != 0)
826 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
828 posix_umask(normal_umask); /* reset normal umask */
831 void InitUserDataDirectory()
833 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
836 void SetFilePermissions(char *filename, int permission_class)
838 chmod(filename, (permission_class == PERMS_PRIVATE ?
839 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
842 char *getCookie(char *file_type)
844 static char cookie[MAX_COOKIE_LEN + 1];
846 if (strlen(program.cookie_prefix) + 1 +
847 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
848 return "[COOKIE ERROR]"; /* should never happen */
850 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
851 program.cookie_prefix, file_type,
852 program.version_major, program.version_minor);
857 int getFileVersionFromCookieString(const char *cookie)
859 const char *ptr_cookie1, *ptr_cookie2;
860 const char *pattern1 = "_FILE_VERSION_";
861 const char *pattern2 = "?.?";
862 const int len_cookie = strlen(cookie);
863 const int len_pattern1 = strlen(pattern1);
864 const int len_pattern2 = strlen(pattern2);
865 const int len_pattern = len_pattern1 + len_pattern2;
866 int version_major, version_minor;
868 if (len_cookie <= len_pattern)
871 ptr_cookie1 = &cookie[len_cookie - len_pattern];
872 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
874 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
877 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
878 ptr_cookie2[1] != '.' ||
879 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
882 version_major = ptr_cookie2[0] - '0';
883 version_minor = ptr_cookie2[2] - '0';
885 return VERSION_IDENT(version_major, version_minor, 0);
888 boolean checkCookieString(const char *cookie, const char *template)
890 const char *pattern = "_FILE_VERSION_?.?";
891 const int len_cookie = strlen(cookie);
892 const int len_template = strlen(template);
893 const int len_pattern = strlen(pattern);
895 if (len_cookie != len_template)
898 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
904 /* ------------------------------------------------------------------------- */
905 /* setup file list handling functions */
906 /* ------------------------------------------------------------------------- */
908 int get_string_integer_value(char *s)
910 static char *number_text[][3] =
912 { "0", "zero", "null", },
913 { "1", "one", "first" },
914 { "2", "two", "second" },
915 { "3", "three", "third" },
916 { "4", "four", "fourth" },
917 { "5", "five", "fifth" },
918 { "6", "six", "sixth" },
919 { "7", "seven", "seventh" },
920 { "8", "eight", "eighth" },
921 { "9", "nine", "ninth" },
922 { "10", "ten", "tenth" },
923 { "11", "eleven", "eleventh" },
924 { "12", "twelve", "twelfth" },
928 char *s_lower = getStringToLower(s);
933 if (strcmp(s_lower, number_text[i][j]) == 0)
944 boolean get_string_boolean_value(char *s)
946 char *s_lower = getStringToLower(s);
947 boolean result = FALSE;
949 if (strcmp(s_lower, "true") == 0 ||
950 strcmp(s_lower, "yes") == 0 ||
951 strcmp(s_lower, "on") == 0 ||
952 get_string_integer_value(s) == 1)
960 char *getFormattedSetupEntry(char *token, char *value)
963 static char entry[MAX_LINE_LEN];
965 /* start with the token and some spaces to format output line */
966 sprintf(entry, "%s:", token);
967 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
970 /* continue with the token's value */
971 strcat(entry, value);
976 void freeSetupFileList(struct SetupFileList *setup_file_list)
978 if (!setup_file_list)
981 if (setup_file_list->token)
982 free(setup_file_list->token);
983 if (setup_file_list->value)
984 free(setup_file_list->value);
985 if (setup_file_list->next)
986 freeSetupFileList(setup_file_list->next);
987 free(setup_file_list);
990 static struct SetupFileList *newSetupFileList(char *token, char *value)
992 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
994 new->token = checked_malloc(strlen(token) + 1);
995 strcpy(new->token, token);
997 new->value = checked_malloc(strlen(value) + 1);
998 strcpy(new->value, value);
1005 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1007 if (!setup_file_list)
1010 if (strcmp(setup_file_list->token, token) == 0)
1011 return setup_file_list->value;
1013 return getTokenValue(setup_file_list->next, token);
1016 static void setTokenValue(struct SetupFileList *setup_file_list,
1017 char *token, char *value)
1019 if (!setup_file_list)
1022 if (strcmp(setup_file_list->token, token) == 0)
1024 free(setup_file_list->value);
1025 setup_file_list->value = checked_malloc(strlen(value) + 1);
1026 strcpy(setup_file_list->value, value);
1028 else if (setup_file_list->next == NULL)
1029 setup_file_list->next = newSetupFileList(token, value);
1031 setTokenValue(setup_file_list->next, token, value);
1035 static void printSetupFileList(struct SetupFileList *setup_file_list)
1037 if (!setup_file_list)
1040 printf("token: '%s'\n", setup_file_list->token);
1041 printf("value: '%s'\n", setup_file_list->value);
1043 printSetupFileList(setup_file_list->next);
1047 struct SetupFileList *loadSetupFileList(char *filename)
1050 char line[MAX_LINE_LEN];
1051 char *token, *value, *line_ptr;
1052 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1053 struct SetupFileList *first_valid_list_entry;
1057 if (!(file = fopen(filename, MODE_READ)))
1059 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1065 /* read next line of input file */
1066 if (!fgets(line, MAX_LINE_LEN, file))
1069 /* cut trailing comment or whitespace from input line */
1070 for (line_ptr = line; *line_ptr; line_ptr++)
1072 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1079 /* cut trailing whitespaces from input line */
1080 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1081 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1084 /* ignore empty lines */
1088 line_len = strlen(line);
1090 /* cut leading whitespaces from token */
1091 for (token = line; *token; token++)
1092 if (*token != ' ' && *token != '\t')
1095 /* find end of token */
1096 for (line_ptr = token; *line_ptr; line_ptr++)
1098 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1105 if (line_ptr < line + line_len)
1106 value = line_ptr + 1;
1110 /* cut leading whitespaces from value */
1111 for (; *value; value++)
1112 if (*value != ' ' && *value != '\t')
1115 if (*token && *value)
1116 setTokenValue(setup_file_list, token, value);
1121 first_valid_list_entry = setup_file_list->next;
1123 /* free empty list header */
1124 setup_file_list->next = NULL;
1125 freeSetupFileList(setup_file_list);
1127 if (first_valid_list_entry == NULL)
1128 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1130 return first_valid_list_entry;
1133 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1136 if (!setup_file_list)
1139 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1141 if (!checkCookieString(setup_file_list->value, identifier))
1143 Error(ERR_WARN, "configuration file has wrong file identifier");
1150 if (setup_file_list->next)
1151 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1154 Error(ERR_WARN, "configuration file has no file identifier");
1160 /* ========================================================================= */
1161 /* setup file stuff */
1162 /* ========================================================================= */
1164 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1165 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1166 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1168 /* level directory info */
1169 #define LEVELINFO_TOKEN_NAME 0
1170 #define LEVELINFO_TOKEN_NAME_SHORT 1
1171 #define LEVELINFO_TOKEN_NAME_SORTING 2
1172 #define LEVELINFO_TOKEN_AUTHOR 3
1173 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1174 #define LEVELINFO_TOKEN_LEVELS 5
1175 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1176 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1177 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1178 #define LEVELINFO_TOKEN_READONLY 9
1180 #define NUM_LEVELINFO_TOKENS 10
1182 static LevelDirTree ldi;
1184 static struct TokenInfo levelinfo_tokens[] =
1186 /* level directory info */
1187 { TYPE_STRING, &ldi.name, "name" },
1188 { TYPE_STRING, &ldi.name_short, "name_short" },
1189 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1190 { TYPE_STRING, &ldi.author, "author" },
1191 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1192 { TYPE_INTEGER, &ldi.levels, "levels" },
1193 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1194 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1195 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1196 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1199 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1203 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1204 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1205 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1206 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1209 ldi->node_parent = NULL;
1210 ldi->node_group = NULL;
1214 ldi->cl_cursor = -1;
1216 ldi->filename = NULL;
1217 ldi->fullpath = NULL;
1218 ldi->basepath = NULL;
1219 ldi->name = getStringCopy(ANONYMOUS_NAME);
1220 ldi->name_short = NULL;
1221 ldi->name_sorting = NULL;
1222 ldi->author = getStringCopy(ANONYMOUS_NAME);
1224 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1225 ldi->parent_link = FALSE;
1226 ldi->user_defined = FALSE;
1228 ldi->class_desc = NULL;
1230 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1232 ldi->imported_from = NULL;
1234 ldi->first_level = 0;
1235 ldi->last_level = 0;
1236 ldi->level_group = FALSE;
1237 ldi->handicap_level = 0;
1238 ldi->readonly = TRUE;
1242 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1246 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1248 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1252 /* first copy all values from the parent structure ... */
1255 /* ... then set all fields to default that cannot be inherited from parent.
1256 This is especially important for all those fields that can be set from
1257 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1258 calls 'free()' for all already set token values which requires that no
1259 other structure's pointer may point to them!
1262 ldi->filename = NULL;
1263 ldi->fullpath = NULL;
1264 ldi->basepath = NULL;
1265 ldi->name = getStringCopy(ANONYMOUS_NAME);
1266 ldi->name_short = NULL;
1267 ldi->name_sorting = NULL;
1268 ldi->author = getStringCopy(parent->author);
1269 ldi->imported_from = getStringCopy(parent->imported_from);
1271 ldi->level_group = FALSE;
1272 ldi->parent_link = FALSE;
1274 ldi->node_top = parent->node_top;
1275 ldi->node_parent = parent;
1276 ldi->node_group = NULL;
1280 void setSetupInfo(struct TokenInfo *token_info,
1281 int token_nr, char *token_value)
1283 int token_type = token_info[token_nr].type;
1284 void *setup_value = token_info[token_nr].value;
1286 if (token_value == NULL)
1289 /* set setup field to corresponding token value */
1294 *(boolean *)setup_value = get_string_boolean_value(token_value);
1298 *(Key *)setup_value = getKeyFromKeyName(token_value);
1302 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1306 *(int *)setup_value = get_string_integer_value(token_value);
1310 if (*(char **)setup_value != NULL)
1311 free(*(char **)setup_value);
1312 *(char **)setup_value = getStringCopy(token_value);
1320 static int compareTreeInfoEntries(const void *object1, const void *object2)
1322 const TreeInfo *entry1 = *((TreeInfo **)object1);
1323 const TreeInfo *entry2 = *((TreeInfo **)object2);
1326 if (entry1->parent_link || entry2->parent_link)
1327 compare_result = (entry1->parent_link ? -1 : +1);
1328 else if (entry1->sort_priority == entry2->sort_priority)
1330 char *name1 = getStringToLower(entry1->name_sorting);
1331 char *name2 = getStringToLower(entry2->name_sorting);
1333 compare_result = strcmp(name1, name2);
1338 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1339 compare_result = entry1->sort_priority - entry2->sort_priority;
1341 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1343 return compare_result;
1346 static void createParentTreeInfoNode(TreeInfo *node_parent)
1350 if (node_parent == NULL)
1353 ti_new = newTreeInfo();
1354 setTreeInfoToDefaults(ti_new, node_parent->type);
1356 ti_new->node_parent = node_parent;
1357 ti_new->parent_link = TRUE;
1359 ti_new->name = ".. (parent directory)";
1360 ti_new->name_short = getStringCopy(ti_new->name);
1361 ti_new->name_sorting = getStringCopy(ti_new->name);
1363 ti_new->filename = "..";
1364 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1366 ti_new->sort_priority = node_parent->sort_priority;
1367 ti_new->class_desc = getLevelClassDescription(ti_new);
1369 pushTreeInfo(&node_parent->node_group, ti_new);
1372 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1373 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1375 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1376 TreeInfo *node_parent,
1377 char *level_directory,
1378 char *directory_name)
1380 char *directory_path = getPath2(level_directory, directory_name);
1381 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1382 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1383 LevelDirTree *leveldir_new = NULL;
1386 if (setup_file_list == NULL)
1388 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1390 free(directory_path);
1396 leveldir_new = newTreeInfo();
1399 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1401 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1403 leveldir_new->filename = getStringCopy(directory_name);
1405 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1407 /* set all structure fields according to the token/value pairs */
1408 ldi = *leveldir_new;
1409 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1410 setSetupInfo(levelinfo_tokens, i,
1411 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1412 *leveldir_new = ldi;
1414 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1416 free(leveldir_new->name);
1417 leveldir_new->name = getStringCopy(leveldir_new->filename);
1420 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1422 if (leveldir_new->name_short == NULL)
1423 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1425 if (leveldir_new->name_sorting == NULL)
1426 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1428 if (node_parent == NULL) /* top level group */
1430 leveldir_new->basepath = level_directory;
1431 leveldir_new->fullpath = leveldir_new->filename;
1433 else /* sub level group */
1435 leveldir_new->basepath = node_parent->basepath;
1436 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1439 if (leveldir_new->levels < 1)
1440 leveldir_new->levels = 1;
1442 leveldir_new->last_level =
1443 leveldir_new->first_level + leveldir_new->levels - 1;
1445 leveldir_new->user_defined =
1446 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1448 leveldir_new->color = LEVELCOLOR(leveldir_new);
1449 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1451 leveldir_new->handicap_level = /* set handicap to default value */
1452 (leveldir_new->user_defined ?
1453 leveldir_new->last_level :
1454 leveldir_new->first_level);
1456 pushTreeInfo(node_first, leveldir_new);
1458 freeSetupFileList(setup_file_list);
1460 if (leveldir_new->level_group)
1462 /* create node to link back to current level directory */
1463 createParentTreeInfoNode(leveldir_new);
1465 /* step into sub-directory and look for more level series */
1466 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1467 leveldir_new, directory_path);
1470 free(directory_path);
1476 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1477 TreeInfo *node_parent,
1478 char *level_directory)
1481 struct dirent *dir_entry;
1482 boolean valid_entry_found = FALSE;
1484 if ((dir = opendir(level_directory)) == NULL)
1486 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1490 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1492 struct stat file_status;
1493 char *directory_name = dir_entry->d_name;
1494 char *directory_path = getPath2(level_directory, directory_name);
1496 /* skip entries for current and parent directory */
1497 if (strcmp(directory_name, ".") == 0 ||
1498 strcmp(directory_name, "..") == 0)
1500 free(directory_path);
1504 /* find out if directory entry is itself a directory */
1505 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1506 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1508 free(directory_path);
1512 free(directory_path);
1514 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1515 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1516 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1519 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1526 if (!valid_entry_found)
1528 /* check if this directory directly contains a file "levelinfo.conf" */
1529 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1530 level_directory, ".");
1533 if (!valid_entry_found)
1534 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1538 void LoadLevelInfo()
1540 InitUserLevelDirectory(getLoginName());
1542 DrawInitText("Loading level series:", 120, FC_GREEN);
1544 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1545 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1547 /* before sorting, the first entries will be from the user directory */
1548 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1550 if (leveldir_first == NULL)
1551 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1553 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1556 dumpTreeInfo(leveldir_first, 0);
1560 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1561 TreeInfo *node_parent,
1562 char *base_directory,
1563 char *directory_name, int type)
1565 char *directory_path = getPath2(base_directory, directory_name);
1566 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1567 struct SetupFileList *setup_file_list = NULL;
1568 TreeInfo *artwork_new = NULL;
1571 if (access(filename, F_OK) == 0) /* file exists */
1572 setup_file_list = loadSetupFileList(filename);
1574 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1577 struct dirent *dir_entry;
1578 boolean valid_file_found = FALSE;
1580 if ((dir = opendir(directory_path)) != NULL)
1582 while ((dir_entry = readdir(dir)) != NULL)
1584 char *entry_name = dir_entry->d_name;
1586 if (FileIsArtworkType(entry_name, type))
1588 valid_file_found = TRUE;
1596 if (!valid_file_found)
1598 if (strcmp(directory_name, ".") != 0)
1599 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1601 free(directory_path);
1608 artwork_new = newTreeInfo();
1611 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1613 setTreeInfoToDefaults(artwork_new, type);
1615 artwork_new->filename = getStringCopy(directory_name);
1617 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1620 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1623 /* set all structure fields according to the token/value pairs */
1625 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1626 setSetupInfo(levelinfo_tokens, i,
1627 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1630 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1632 free(artwork_new->name);
1633 artwork_new->name = getStringCopy(artwork_new->filename);
1637 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1640 if (artwork_new->name_short == NULL)
1641 artwork_new->name_short = getStringCopy(artwork_new->name);
1643 if (artwork_new->name_sorting == NULL)
1644 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1647 if (node_parent == NULL) /* top level group */
1649 artwork_new->basepath = getStringCopy(base_directory);
1650 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1652 else /* sub level group */
1654 artwork_new->basepath = getStringCopy(node_parent->basepath);
1655 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1658 artwork_new->user_defined =
1659 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1661 /* (may use ".sort_priority" from "setup_file_list" above) */
1662 artwork_new->color = LEVELCOLOR(artwork_new);
1663 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1665 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1667 if (artwork_new->name != NULL)
1668 free(artwork_new->name);
1670 if (strcmp(artwork_new->filename, ".") == 0)
1672 if (artwork_new->user_defined)
1674 artwork_new->name = getStringCopy("private");
1675 artwork_new->sort_priority = LEVELCLASS_USER;
1679 artwork_new->name = getStringCopy("classic");
1680 artwork_new->sort_priority = LEVELCLASS_CLASSICS;
1683 artwork_new->color = LEVELCOLOR(artwork_new);
1684 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1687 artwork_new->name = getStringCopy(artwork_new->filename);
1689 artwork_new->name_short = getStringCopy(artwork_new->name);
1690 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1693 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1695 pushTreeInfo(node_first, artwork_new);
1697 freeSetupFileList(setup_file_list);
1699 free(directory_path);
1705 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1706 TreeInfo *node_parent,
1707 char *base_directory, int type)
1710 struct dirent *dir_entry;
1711 boolean valid_entry_found = FALSE;
1713 if ((dir = opendir(base_directory)) == NULL)
1715 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1716 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1720 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1722 struct stat file_status;
1723 char *directory_name = dir_entry->d_name;
1724 char *directory_path = getPath2(base_directory, directory_name);
1726 /* skip entries for current and parent directory */
1727 if (strcmp(directory_name, ".") == 0 ||
1728 strcmp(directory_name, "..") == 0)
1730 free(directory_path);
1734 /* find out if directory entry is itself a directory */
1735 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1736 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1738 free(directory_path);
1742 free(directory_path);
1744 /* check if this directory contains artwork with or without config file */
1745 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1747 directory_name, type);
1752 /* check if this directory directly contains artwork itself */
1753 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1754 base_directory, ".",
1756 if (!valid_entry_found)
1757 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1761 static TreeInfo *getDummyArtworkInfo(int type)
1763 /* this is only needed when there is completely no artwork available */
1764 TreeInfo *artwork_new = newTreeInfo();
1766 setTreeInfoToDefaults(artwork_new, type);
1768 artwork_new->filename = getStringCopy(NOT_AVAILABLE);
1769 artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
1770 artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
1772 if (artwork_new->name != NULL)
1773 free(artwork_new->name);
1775 artwork_new->name = getStringCopy(NOT_AVAILABLE);
1776 artwork_new->name_short = getStringCopy(NOT_AVAILABLE);
1777 artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
1782 void LoadArtworkInfo()
1784 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1786 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1787 options.graphics_directory,
1788 TREE_TYPE_GRAPHICS_DIR);
1789 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1790 getUserGraphicsDir(),
1791 TREE_TYPE_GRAPHICS_DIR);
1793 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1794 options.sounds_directory,
1795 TREE_TYPE_SOUNDS_DIR);
1796 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1798 TREE_TYPE_SOUNDS_DIR);
1800 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1801 options.music_directory,
1802 TREE_TYPE_MUSIC_DIR);
1803 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1805 TREE_TYPE_MUSIC_DIR);
1807 if (artwork.gfx_first == NULL)
1808 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1809 if (artwork.snd_first == NULL)
1810 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1811 if (artwork.mus_first == NULL)
1812 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1814 /* before sorting, the first entries will be from the user directory */
1815 artwork.gfx_current =
1816 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1817 if (artwork.gfx_current == NULL)
1818 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1820 artwork.snd_current =
1821 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1822 if (artwork.snd_current == NULL)
1823 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1825 artwork.mus_current =
1826 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1827 if (artwork.mus_current == NULL)
1828 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1830 artwork.graphics_set_current_name = artwork.gfx_current->name;
1831 artwork.sounds_set_current_name = artwork.snd_current->name;
1832 artwork.music_set_current_name = artwork.mus_current->name;
1835 printf("graphics set == %s\n\n", artwork.graphics_set_current_name);
1836 printf("sounds set == %s\n\n", artwork.sounds_set_current_name);
1837 printf("music set == %s\n\n", artwork.music_set_current_name);
1840 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1841 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1842 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1845 dumpTreeInfo(artwork.gfx_first, 0);
1846 dumpTreeInfo(artwork.snd_first, 0);
1847 dumpTreeInfo(artwork.mus_first, 0);
1851 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1852 LevelDirTree *level_node)
1854 /* recursively check all level directories for artwork sub-directories */
1858 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1859 ARTWORK_DIRECTORY((*artwork_node)->type));
1862 if (!level_node->parent_link)
1863 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1864 level_node->filename, level_node->name);
1867 if (!level_node->parent_link)
1869 TreeInfo *topnode_last = *artwork_node;
1871 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1872 (*artwork_node)->type);
1874 if (topnode_last != *artwork_node)
1876 free((*artwork_node)->name);
1877 free((*artwork_node)->name_sorting);
1878 free((*artwork_node)->name_short);
1880 (*artwork_node)->name = getStringCopy(level_node->name);
1881 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1882 (*artwork_node)->name_short = getStringCopy(level_node->filename);
1884 (*artwork_node)->sort_priority = level_node->sort_priority;
1885 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1891 if (level_node->node_group != NULL)
1892 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1894 level_node = level_node->next;
1898 void LoadLevelArtworkInfo()
1900 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1902 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1903 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1904 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1906 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1907 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1908 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1911 dumpTreeInfo(artwork.gfx_first, 0);
1912 dumpTreeInfo(artwork.snd_first, 0);
1913 dumpTreeInfo(artwork.mus_first, 0);
1917 static void SaveUserLevelInfo()
1923 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1925 if (!(file = fopen(filename, MODE_WRITE)))
1927 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1932 /* always start with reliable default values */
1933 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1935 ldi.name = getLoginName();
1936 ldi.author = getRealName();
1938 ldi.first_level = 1;
1939 ldi.sort_priority = LEVELCLASS_USER_START;
1940 ldi.readonly = FALSE;
1942 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1943 getCookie("LEVELINFO")));
1945 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1946 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1947 i != LEVELINFO_TOKEN_NAME_SORTING &&
1948 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1949 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1954 SetFilePermissions(filename, PERMS_PRIVATE);
1957 char *getSetupValue(int type, void *value)
1959 static char value_string[MAX_LINE_LEN];
1967 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1971 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1975 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1979 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1983 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1987 sprintf(value_string, "%d", *(int *)value);
1991 strcpy(value_string, *(char **)value);
1995 value_string[0] = '\0';
1999 return value_string;
2002 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2006 static char token_string[MAX_LINE_LEN];
2007 int token_type = token_info[token_nr].type;
2008 void *setup_value = token_info[token_nr].value;
2009 char *token_text = token_info[token_nr].text;
2010 char *value_string = getSetupValue(token_type, setup_value);
2012 /* build complete token string */
2013 sprintf(token_string, "%s%s", prefix, token_text);
2015 /* build setup entry line */
2016 line = getFormattedSetupEntry(token_string, value_string);
2018 if (token_type == TYPE_KEY_X11)
2020 Key key = *(Key *)setup_value;
2021 char *keyname = getKeyNameFromKey(key);
2023 /* add comment, if useful */
2024 if (strcmp(keyname, "(undefined)") != 0 &&
2025 strcmp(keyname, "(unknown)") != 0)
2027 /* add at least one whitespace */
2029 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2033 strcat(line, keyname);
2040 void LoadLevelSetup_LastSeries()
2043 struct SetupFileList *level_setup_list = NULL;
2045 /* always start with reliable default values */
2046 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2048 /* ----------------------------------------------------------------------- */
2049 /* ~/.<program>/levelsetup.conf */
2050 /* ----------------------------------------------------------------------- */
2052 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2054 if ((level_setup_list = loadSetupFileList(filename)))
2056 char *last_level_series =
2057 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2059 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2061 if (leveldir_current == NULL)
2062 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2064 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2066 freeSetupFileList(level_setup_list);
2069 Error(ERR_WARN, "using default setup values");
2074 void SaveLevelSetup_LastSeries()
2077 char *level_subdir = leveldir_current->filename;
2080 /* ----------------------------------------------------------------------- */
2081 /* ~/.<program>/levelsetup.conf */
2082 /* ----------------------------------------------------------------------- */
2084 InitUserDataDirectory();
2086 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2088 if (!(file = fopen(filename, MODE_WRITE)))
2090 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2095 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2096 getCookie("LEVELSETUP")));
2097 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2103 SetFilePermissions(filename, PERMS_PRIVATE);
2106 static void checkSeriesInfo()
2108 static char *level_directory = NULL;
2110 struct dirent *dir_entry;
2112 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2114 level_directory = getPath2((leveldir_current->user_defined ?
2115 getUserLevelDir(NULL) :
2116 options.level_directory),
2117 leveldir_current->fullpath);
2119 if ((dir = opendir(level_directory)) == NULL)
2121 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2125 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2127 if (strlen(dir_entry->d_name) > 4 &&
2128 dir_entry->d_name[3] == '.' &&
2129 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2131 char levelnum_str[4];
2134 strncpy(levelnum_str, dir_entry->d_name, 3);
2135 levelnum_str[3] = '\0';
2137 levelnum_value = atoi(levelnum_str);
2139 if (levelnum_value < leveldir_current->first_level)
2141 Error(ERR_WARN, "additional level %d found", levelnum_value);
2142 leveldir_current->first_level = levelnum_value;
2144 else if (levelnum_value > leveldir_current->last_level)
2146 Error(ERR_WARN, "additional level %d found", levelnum_value);
2147 leveldir_current->last_level = levelnum_value;
2155 void LoadLevelSetup_SeriesInfo()
2158 struct SetupFileList *level_setup_list = NULL;
2159 char *level_subdir = leveldir_current->filename;
2161 /* always start with reliable default values */
2162 level_nr = leveldir_current->first_level;
2164 checkSeriesInfo(leveldir_current);
2166 /* ----------------------------------------------------------------------- */
2167 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2168 /* ----------------------------------------------------------------------- */
2170 level_subdir = leveldir_current->filename;
2172 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2174 if ((level_setup_list = loadSetupFileList(filename)))
2178 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2182 level_nr = atoi(token_value);
2184 if (level_nr < leveldir_current->first_level)
2185 level_nr = leveldir_current->first_level;
2186 if (level_nr > leveldir_current->last_level)
2187 level_nr = leveldir_current->last_level;
2190 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2194 int level_nr = atoi(token_value);
2196 if (level_nr < leveldir_current->first_level)
2197 level_nr = leveldir_current->first_level;
2198 if (level_nr > leveldir_current->last_level + 1)
2199 level_nr = leveldir_current->last_level;
2201 if (leveldir_current->user_defined)
2202 level_nr = leveldir_current->last_level;
2204 leveldir_current->handicap_level = level_nr;
2207 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2209 freeSetupFileList(level_setup_list);
2212 Error(ERR_WARN, "using default setup values");
2217 void SaveLevelSetup_SeriesInfo()
2220 char *level_subdir = leveldir_current->filename;
2221 char *level_nr_str = int2str(level_nr, 0);
2222 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2225 /* ----------------------------------------------------------------------- */
2226 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2227 /* ----------------------------------------------------------------------- */
2229 InitLevelSetupDirectory(level_subdir);
2231 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2233 if (!(file = fopen(filename, MODE_WRITE)))
2235 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2240 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2241 getCookie("LEVELSETUP")));
2242 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2244 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2245 handicap_level_str));
2250 SetFilePermissions(filename, PERMS_PRIVATE);