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 ***********************************************************/
14 #include <sys/types.h>
25 /* file names and filename extensions */
26 #if !defined(PLATFORM_MSDOS)
27 #define LEVELSETUP_DIRECTORY "levelsetup"
28 #define SETUP_FILENAME "setup.conf"
29 #define LEVELSETUP_FILENAME "levelsetup.conf"
30 #define LEVELINFO_FILENAME "levelinfo.conf"
31 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
32 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
33 #define MUSICINFO_FILENAME "musicinfo.conf"
34 #define LEVELFILE_EXTENSION "level"
35 #define TAPEFILE_EXTENSION "tape"
36 #define SCOREFILE_EXTENSION "score"
38 #define LEVELSETUP_DIRECTORY "lvlsetup"
39 #define SETUP_FILENAME "setup.cnf"
40 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
41 #define LEVELINFO_FILENAME "lvlinfo.cnf"
42 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
43 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
44 #define MUSICINFO_FILENAME "musinfo.cnf"
45 #define LEVELFILE_EXTENSION "lvl"
46 #define TAPEFILE_EXTENSION "tap"
47 #define SCOREFILE_EXTENSION "sco"
50 #define NUM_LEVELCLASS_DESC 8
51 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
63 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
64 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
65 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
66 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
67 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
68 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
69 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
70 IS_LEVELCLASS_USER(n) ? FC_RED : \
73 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
74 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
75 IS_LEVELCLASS_BD(n) ? 2 : \
76 IS_LEVELCLASS_EM(n) ? 3 : \
77 IS_LEVELCLASS_SP(n) ? 4 : \
78 IS_LEVELCLASS_DX(n) ? 5 : \
79 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
80 IS_LEVELCLASS_USER(n) ? 7 : \
83 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
84 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
85 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
86 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
89 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
90 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
91 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
92 IS_ARTWORKCLASS_USER(n) ? 3 : \
95 #define TOKEN_VALUE_POSITION 40
96 #define TOKEN_COMMENT_POSITION 60
98 #define MAX_COOKIE_LEN 256
100 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
101 GRAPHICSINFO_FILENAME : \
102 (type) == TREE_TYPE_SOUNDS_DIR ? \
103 SOUNDSINFO_FILENAME : \
104 (type) == TREE_TYPE_MUSIC_DIR ? \
105 MUSICINFO_FILENAME : "")
107 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
108 GRAPHICS_DIRECTORY : \
109 (type) == TREE_TYPE_SOUNDS_DIR ? \
111 (type) == TREE_TYPE_MUSIC_DIR ? \
112 MUSIC_DIRECTORY : "")
114 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
115 options.graphics_directory : \
116 (type) == TREE_TYPE_SOUNDS_DIR ? \
117 options.sounds_directory : \
118 (type) == TREE_TYPE_MUSIC_DIR ? \
119 options.music_directory : "")
122 /* ------------------------------------------------------------------------- */
124 /* ------------------------------------------------------------------------- */
126 static char *getLevelClassDescription(TreeInfo *ldi)
128 int position = ldi->sort_priority / 100;
130 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
131 return levelclass_desc[position];
133 return "Unknown Level Class";
136 static char *getUserLevelDir(char *level_subdir)
138 static char *userlevel_dir = NULL;
139 char *data_dir = getUserDataDir();
140 char *userlevel_subdir = LEVELS_DIRECTORY;
145 if (level_subdir != NULL)
146 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
148 userlevel_dir = getPath2(data_dir, userlevel_subdir);
150 return userlevel_dir;
153 static char *getTapeDir(char *level_subdir)
155 static char *tape_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *tape_subdir = TAPES_DIRECTORY;
162 if (level_subdir != NULL)
163 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
165 tape_dir = getPath2(data_dir, tape_subdir);
170 static char *getScoreDir(char *level_subdir)
172 static char *score_dir = NULL;
173 char *data_dir = options.rw_base_directory;
174 char *score_subdir = SCORES_DIRECTORY;
179 if (level_subdir != NULL)
180 score_dir = getPath3(data_dir, score_subdir, level_subdir);
182 score_dir = getPath2(data_dir, score_subdir);
187 static char *getLevelSetupDir(char *level_subdir)
189 static char *levelsetup_dir = NULL;
190 char *data_dir = getUserDataDir();
191 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
194 free(levelsetup_dir);
196 if (level_subdir != NULL)
197 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
199 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
201 return levelsetup_dir;
204 static char *getLevelDirFromTreeInfo(TreeInfo *node)
206 static char *level_dir = NULL;
209 return options.level_directory;
214 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
215 options.level_directory), node->fullpath);
220 static char *getCurrentLevelDir()
222 return getLevelDirFromTreeInfo(leveldir_current);
225 static char *getDefaultGraphicsDir(char *graphics_subdir)
227 static char *graphics_dir = NULL;
229 if (graphics_subdir == NULL)
230 return options.graphics_directory;
235 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
240 static char *getDefaultSoundsDir(char *sounds_subdir)
242 static char *sounds_dir = NULL;
244 if (sounds_subdir == NULL)
245 return options.sounds_directory;
250 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
255 static char *getDefaultMusicDir(char *music_subdir)
257 static char *music_dir = NULL;
259 if (music_subdir == NULL)
260 return options.music_directory;
265 music_dir = getPath2(options.music_directory, music_subdir);
270 static char *getUserGraphicsDir()
272 static char *usergraphics_dir = NULL;
274 if (usergraphics_dir == NULL)
275 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
277 return usergraphics_dir;
280 static char *getUserSoundsDir()
282 static char *usersounds_dir = NULL;
284 if (usersounds_dir == NULL)
285 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
287 return usersounds_dir;
290 static char *getUserMusicDir()
292 static char *usermusic_dir = NULL;
294 if (usermusic_dir == NULL)
295 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
297 return usermusic_dir;
300 char *getLevelFilename(int nr)
302 static char *filename = NULL;
303 char basename[MAX_FILENAME_LEN];
305 if (filename != NULL)
308 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
309 filename = getPath2(getCurrentLevelDir(), basename);
314 char *getTapeFilename(int nr)
316 static char *filename = NULL;
317 char basename[MAX_FILENAME_LEN];
319 if (filename != NULL)
322 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
323 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
328 char *getScoreFilename(int nr)
330 static char *filename = NULL;
331 char basename[MAX_FILENAME_LEN];
333 if (filename != NULL)
336 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
337 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
342 char *getSetupFilename()
344 static char *filename = NULL;
346 if (filename != NULL)
349 filename = getPath2(getSetupDir(), SETUP_FILENAME);
354 static char *getSetupArtworkDir(TreeInfo *ti)
356 static char *artwork_dir = NULL;
358 if (artwork_dir != NULL)
361 artwork_dir = getPath2(ti->basepath, ti->fullpath);
366 static char *getCorrectedImageBasename(char *basename)
368 char *result = basename;
370 #if defined(PLATFORM_MSDOS)
371 if (program.filename_prefix != NULL)
373 int prefix_len = strlen(program.filename_prefix);
375 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
376 result = &basename[prefix_len];
383 static boolean fileExists(char *filename)
386 printf("checking file '%s'\n", filename);
389 return (access(filename, F_OK) == 0);
392 char *getCustomImageFilename(char *basename)
394 static char *filename = NULL;
396 if (filename != NULL)
399 basename = getCorrectedImageBasename(basename);
401 if (!setup.override_level_graphics)
403 /* 1st try: look for special artwork in current level series directory */
404 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
405 if (fileExists(filename))
409 /* 2nd try: look for special artwork in configured artwork directory */
410 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
411 if (fileExists(filename))
414 /* 3rd try: look for default artwork in new default artwork directory */
415 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
416 if (fileExists(filename))
419 /* 4th try: look for default artwork in old default artwork directory */
420 filename = getPath2(options.graphics_directory, basename);
421 if (fileExists(filename))
424 return NULL; /* cannot find specified artwork file anywhere */
427 char *getCustomSoundFilename(char *basename)
429 static char *filename = NULL;
431 if (filename != NULL)
434 if (!setup.override_level_sounds)
436 /* 1st try: look for special artwork in current level series directory */
437 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
438 if (fileExists(filename))
442 /* 2nd try: look for special artwork in configured artwork directory */
443 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
444 if (fileExists(filename))
447 /* 3rd try: look for default artwork in new default artwork directory */
448 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
449 if (fileExists(filename))
452 /* 4th try: look for default artwork in old default artwork directory */
453 filename = getPath2(options.sounds_directory, basename);
454 if (fileExists(filename))
457 return NULL; /* cannot find specified artwork file anywhere */
460 char *getCustomSoundConfigFilename()
462 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
465 char *getCustomMusicDirectory(void)
467 static char *directory = NULL;
469 if (directory != NULL)
472 if (!setup.override_level_music)
474 /* 1st try: look for special artwork in current level series directory */
475 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
476 if (fileExists(directory))
480 /* 2nd try: look for special artwork in configured artwork directory */
481 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
482 if (fileExists(directory))
485 /* 3rd try: look for default artwork in new default artwork directory */
486 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
487 if (fileExists(directory))
490 /* 4th try: look for default artwork in old default artwork directory */
491 directory = getStringCopy(options.music_directory);
492 if (fileExists(directory))
495 return NULL; /* cannot find specified artwork file anywhere */
498 void InitTapeDirectory(char *level_subdir)
500 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
501 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
502 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
505 void InitScoreDirectory(char *level_subdir)
507 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
508 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
511 static void SaveUserLevelInfo();
513 void InitUserLevelDirectory(char *level_subdir)
515 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
517 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
518 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
519 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
525 void InitLevelSetupDirectory(char *level_subdir)
527 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
528 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
529 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
533 /* ------------------------------------------------------------------------- */
534 /* some functions to handle lists of level directories */
535 /* ------------------------------------------------------------------------- */
537 TreeInfo *newTreeInfo()
539 return checked_calloc(sizeof(TreeInfo));
542 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
544 node_new->next = *node_first;
545 *node_first = node_new;
548 int numTreeInfo(TreeInfo *node)
561 boolean validLevelSeries(TreeInfo *node)
563 return (node != NULL && !node->node_group && !node->parent_link);
566 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
571 if (node->node_group) /* enter level group (step down into tree) */
572 return getFirstValidTreeInfoEntry(node->node_group);
573 else if (node->parent_link) /* skip start entry of level group */
575 if (node->next) /* get first real level series entry */
576 return getFirstValidTreeInfoEntry(node->next);
577 else /* leave empty level group and go on */
578 return getFirstValidTreeInfoEntry(node->node_parent->next);
580 else /* this seems to be a regular level series */
584 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
589 if (node->node_parent == NULL) /* top level group */
590 return *node->node_top;
591 else /* sub level group */
592 return node->node_parent->node_group;
595 int numTreeInfoInGroup(TreeInfo *node)
597 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
600 int posTreeInfo(TreeInfo *node)
602 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
607 if (node_cmp == node)
611 node_cmp = node_cmp->next;
617 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
619 TreeInfo *node_default = node;
634 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
636 if (filename == NULL)
641 if (node->node_group)
643 TreeInfo *node_group;
645 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
650 else if (!node->parent_link)
652 if (strcmp(filename, node->filename) == 0)
655 /* special case when looking for level series artwork:
656 node->name_short contains level series filename */
657 if (strcmp(node->filename, ".") == 0 &&
658 strcmp(filename, node->name_short) == 0)
668 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
670 return getTreeInfoFromFilenameExt(ti, filename);
673 void dumpTreeInfo(TreeInfo *node, int depth)
677 printf("Dumping TreeInfo:\n");
681 for (i=0; i<(depth + 1) * 3; i++)
684 printf("filename == '%s' (%s) [%s] (%d)\n",
685 node->filename, node->name, node->name_short, node->sort_priority);
687 if (node->node_group != NULL)
688 dumpTreeInfo(node->node_group, depth + 1);
694 void sortTreeInfo(TreeInfo **node_first,
695 int (*compare_function)(const void *, const void *))
697 int num_nodes = numTreeInfo(*node_first);
698 TreeInfo **sort_array;
699 TreeInfo *node = *node_first;
705 /* allocate array for sorting structure pointers */
706 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
708 /* writing structure pointers to sorting array */
709 while (i < num_nodes && node) /* double boundary check... */
711 sort_array[i] = node;
717 /* sorting the structure pointers in the sorting array */
718 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
721 /* update the linkage of list elements with the sorted node array */
722 for (i=0; i<num_nodes - 1; i++)
723 sort_array[i]->next = sort_array[i + 1];
724 sort_array[num_nodes - 1]->next = NULL;
726 /* update the linkage of the main list anchor pointer */
727 *node_first = sort_array[0];
731 /* now recursively sort the level group structures */
735 if (node->node_group != NULL)
736 sortTreeInfo(&node->node_group, compare_function);
743 /* ========================================================================= */
744 /* some stuff from "files.c" */
745 /* ========================================================================= */
747 #if defined(PLATFORM_WIN32)
749 #define S_IRGRP S_IRUSR
752 #define S_IROTH S_IRUSR
755 #define S_IWGRP S_IWUSR
758 #define S_IWOTH S_IWUSR
761 #define S_IXGRP S_IXUSR
764 #define S_IXOTH S_IXUSR
767 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
772 #endif /* PLATFORM_WIN32 */
774 /* file permissions for newly written files */
775 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
776 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
777 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
779 #define MODE_W_PRIVATE (S_IWUSR)
780 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
781 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
783 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
784 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
786 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
787 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
789 char *getUserDataDir(void)
791 static char *userdata_dir = NULL;
795 char *home_dir = getHomeDir();
796 char *data_dir = program.userdata_directory;
798 userdata_dir = getPath2(home_dir, data_dir);
806 return getUserDataDir();
809 static mode_t posix_umask(mode_t mask)
811 #if defined(PLATFORM_UNIX)
818 static int posix_mkdir(const char *pathname, mode_t mode)
820 #if defined(PLATFORM_WIN32)
821 return mkdir(pathname);
823 return mkdir(pathname, mode);
827 void createDirectory(char *dir, char *text, int permission_class)
829 /* leave "other" permissions in umask untouched, but ensure group parts
830 of USERDATA_DIR_MODE are not masked */
831 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
832 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
833 mode_t normal_umask = posix_umask(0);
834 mode_t group_umask = ~(dir_mode & S_IRWXG);
835 posix_umask(normal_umask & group_umask);
837 if (access(dir, F_OK) != 0)
838 if (posix_mkdir(dir, dir_mode) != 0)
839 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
841 posix_umask(normal_umask); /* reset normal umask */
844 void InitUserDataDirectory()
846 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
849 void SetFilePermissions(char *filename, int permission_class)
851 chmod(filename, (permission_class == PERMS_PRIVATE ?
852 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
855 char *getCookie(char *file_type)
857 static char cookie[MAX_COOKIE_LEN + 1];
859 if (strlen(program.cookie_prefix) + 1 +
860 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
861 return "[COOKIE ERROR]"; /* should never happen */
863 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
864 program.cookie_prefix, file_type,
865 program.version_major, program.version_minor);
870 int getFileVersionFromCookieString(const char *cookie)
872 const char *ptr_cookie1, *ptr_cookie2;
873 const char *pattern1 = "_FILE_VERSION_";
874 const char *pattern2 = "?.?";
875 const int len_cookie = strlen(cookie);
876 const int len_pattern1 = strlen(pattern1);
877 const int len_pattern2 = strlen(pattern2);
878 const int len_pattern = len_pattern1 + len_pattern2;
879 int version_major, version_minor;
881 if (len_cookie <= len_pattern)
884 ptr_cookie1 = &cookie[len_cookie - len_pattern];
885 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
887 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
890 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
891 ptr_cookie2[1] != '.' ||
892 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
895 version_major = ptr_cookie2[0] - '0';
896 version_minor = ptr_cookie2[2] - '0';
898 return VERSION_IDENT(version_major, version_minor, 0);
901 boolean checkCookieString(const char *cookie, const char *template)
903 const char *pattern = "_FILE_VERSION_?.?";
904 const int len_cookie = strlen(cookie);
905 const int len_template = strlen(template);
906 const int len_pattern = strlen(pattern);
908 if (len_cookie != len_template)
911 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
917 /* ------------------------------------------------------------------------- */
918 /* setup file list handling functions */
919 /* ------------------------------------------------------------------------- */
921 int get_string_integer_value(char *s)
923 static char *number_text[][3] =
925 { "0", "zero", "null", },
926 { "1", "one", "first" },
927 { "2", "two", "second" },
928 { "3", "three", "third" },
929 { "4", "four", "fourth" },
930 { "5", "five", "fifth" },
931 { "6", "six", "sixth" },
932 { "7", "seven", "seventh" },
933 { "8", "eight", "eighth" },
934 { "9", "nine", "ninth" },
935 { "10", "ten", "tenth" },
936 { "11", "eleven", "eleventh" },
937 { "12", "twelve", "twelfth" },
941 char *s_lower = getStringToLower(s);
946 if (strcmp(s_lower, number_text[i][j]) == 0)
957 boolean get_string_boolean_value(char *s)
959 char *s_lower = getStringToLower(s);
960 boolean result = FALSE;
962 if (strcmp(s_lower, "true") == 0 ||
963 strcmp(s_lower, "yes") == 0 ||
964 strcmp(s_lower, "on") == 0 ||
965 get_string_integer_value(s) == 1)
973 char *getFormattedSetupEntry(char *token, char *value)
976 static char entry[MAX_LINE_LEN];
978 /* start with the token and some spaces to format output line */
979 sprintf(entry, "%s:", token);
980 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
983 /* continue with the token's value */
984 strcat(entry, value);
989 void freeSetupFileList(struct SetupFileList *setup_file_list)
991 if (!setup_file_list)
994 if (setup_file_list->token)
995 free(setup_file_list->token);
996 if (setup_file_list->value)
997 free(setup_file_list->value);
998 if (setup_file_list->next)
999 freeSetupFileList(setup_file_list->next);
1000 free(setup_file_list);
1003 static struct SetupFileList *newSetupFileList(char *token, char *value)
1005 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1007 new->token = checked_malloc(strlen(token) + 1);
1008 strcpy(new->token, token);
1010 new->value = checked_malloc(strlen(value) + 1);
1011 strcpy(new->value, value);
1018 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1020 if (!setup_file_list)
1023 if (strcmp(setup_file_list->token, token) == 0)
1024 return setup_file_list->value;
1026 return getTokenValue(setup_file_list->next, token);
1029 static void setTokenValue(struct SetupFileList *setup_file_list,
1030 char *token, char *value)
1032 if (!setup_file_list)
1035 if (strcmp(setup_file_list->token, token) == 0)
1037 free(setup_file_list->value);
1038 setup_file_list->value = checked_malloc(strlen(value) + 1);
1039 strcpy(setup_file_list->value, value);
1041 else if (setup_file_list->next == NULL)
1042 setup_file_list->next = newSetupFileList(token, value);
1044 setTokenValue(setup_file_list->next, token, value);
1048 static void printSetupFileList(struct SetupFileList *setup_file_list)
1050 if (!setup_file_list)
1053 printf("token: '%s'\n", setup_file_list->token);
1054 printf("value: '%s'\n", setup_file_list->value);
1056 printSetupFileList(setup_file_list->next);
1060 struct SetupFileList *loadSetupFileList(char *filename)
1063 char line[MAX_LINE_LEN];
1064 char *token, *value, *line_ptr;
1065 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1066 struct SetupFileList *first_valid_list_entry;
1070 if (!(file = fopen(filename, MODE_READ)))
1072 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1078 /* read next line of input file */
1079 if (!fgets(line, MAX_LINE_LEN, file))
1082 /* cut trailing comment or whitespace from input line */
1083 for (line_ptr = line; *line_ptr; line_ptr++)
1085 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1092 /* cut trailing whitespaces from input line */
1093 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1094 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1097 /* ignore empty lines */
1101 line_len = strlen(line);
1103 /* cut leading whitespaces from token */
1104 for (token = line; *token; token++)
1105 if (*token != ' ' && *token != '\t')
1108 /* find end of token */
1109 for (line_ptr = token; *line_ptr; line_ptr++)
1111 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1118 if (line_ptr < line + line_len)
1119 value = line_ptr + 1;
1123 /* cut leading whitespaces from value */
1124 for (; *value; value++)
1125 if (*value != ' ' && *value != '\t')
1128 if (*token && *value)
1129 setTokenValue(setup_file_list, token, value);
1134 first_valid_list_entry = setup_file_list->next;
1136 /* free empty list header */
1137 setup_file_list->next = NULL;
1138 freeSetupFileList(setup_file_list);
1140 if (first_valid_list_entry == NULL)
1141 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1143 return first_valid_list_entry;
1146 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1149 if (!setup_file_list)
1152 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1154 if (!checkCookieString(setup_file_list->value, identifier))
1156 Error(ERR_WARN, "configuration file has wrong file identifier");
1163 if (setup_file_list->next)
1164 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1167 Error(ERR_WARN, "configuration file has no file identifier");
1173 /* ========================================================================= */
1174 /* setup file stuff */
1175 /* ========================================================================= */
1177 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1178 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1179 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1181 /* level directory info */
1182 #define LEVELINFO_TOKEN_NAME 0
1183 #define LEVELINFO_TOKEN_NAME_SHORT 1
1184 #define LEVELINFO_TOKEN_NAME_SORTING 2
1185 #define LEVELINFO_TOKEN_AUTHOR 3
1186 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1187 #define LEVELINFO_TOKEN_LEVELS 5
1188 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1189 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1190 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1191 #define LEVELINFO_TOKEN_READONLY 9
1193 #define NUM_LEVELINFO_TOKENS 10
1195 static LevelDirTree ldi;
1197 static struct TokenInfo levelinfo_tokens[] =
1199 /* level directory info */
1200 { TYPE_STRING, &ldi.name, "name" },
1201 { TYPE_STRING, &ldi.name_short, "name_short" },
1202 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1203 { TYPE_STRING, &ldi.author, "author" },
1204 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1205 { TYPE_INTEGER, &ldi.levels, "levels" },
1206 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1207 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1208 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1209 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1212 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1216 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1217 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1218 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1219 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1222 ldi->node_parent = NULL;
1223 ldi->node_group = NULL;
1227 ldi->cl_cursor = -1;
1229 ldi->filename = NULL;
1230 ldi->fullpath = NULL;
1231 ldi->basepath = NULL;
1232 ldi->name = getStringCopy(ANONYMOUS_NAME);
1233 ldi->name_short = NULL;
1234 ldi->name_sorting = NULL;
1235 ldi->author = getStringCopy(ANONYMOUS_NAME);
1237 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1238 ldi->parent_link = FALSE;
1239 ldi->user_defined = FALSE;
1241 ldi->class_desc = NULL;
1243 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1245 ldi->imported_from = NULL;
1247 ldi->first_level = 0;
1248 ldi->last_level = 0;
1249 ldi->level_group = FALSE;
1250 ldi->handicap_level = 0;
1251 ldi->readonly = TRUE;
1255 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1259 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1261 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1265 /* first copy all values from the parent structure ... */
1268 /* ... then set all fields to default that cannot be inherited from parent.
1269 This is especially important for all those fields that can be set from
1270 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1271 calls 'free()' for all already set token values which requires that no
1272 other structure's pointer may point to them!
1275 ldi->filename = NULL;
1276 ldi->fullpath = NULL;
1277 ldi->basepath = NULL;
1278 ldi->name = getStringCopy(ANONYMOUS_NAME);
1279 ldi->name_short = NULL;
1280 ldi->name_sorting = NULL;
1281 ldi->author = getStringCopy(parent->author);
1282 ldi->imported_from = getStringCopy(parent->imported_from);
1284 ldi->level_group = FALSE;
1285 ldi->parent_link = FALSE;
1287 ldi->node_top = parent->node_top;
1288 ldi->node_parent = parent;
1289 ldi->node_group = NULL;
1293 void setSetupInfo(struct TokenInfo *token_info,
1294 int token_nr, char *token_value)
1296 int token_type = token_info[token_nr].type;
1297 void *setup_value = token_info[token_nr].value;
1299 if (token_value == NULL)
1302 /* set setup field to corresponding token value */
1307 *(boolean *)setup_value = get_string_boolean_value(token_value);
1311 *(Key *)setup_value = getKeyFromKeyName(token_value);
1315 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1319 *(int *)setup_value = get_string_integer_value(token_value);
1323 if (*(char **)setup_value != NULL)
1324 free(*(char **)setup_value);
1325 *(char **)setup_value = getStringCopy(token_value);
1333 static int compareTreeInfoEntries(const void *object1, const void *object2)
1335 const TreeInfo *entry1 = *((TreeInfo **)object1);
1336 const TreeInfo *entry2 = *((TreeInfo **)object2);
1337 int class_sorting1, class_sorting2;
1340 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1342 class_sorting1 = LEVELSORTING(entry1);
1343 class_sorting2 = LEVELSORTING(entry2);
1347 class_sorting1 = ARTWORKSORTING(entry1);
1348 class_sorting2 = ARTWORKSORTING(entry2);
1351 if (entry1->parent_link || entry2->parent_link)
1352 compare_result = (entry1->parent_link ? -1 : +1);
1353 else if (entry1->sort_priority == entry2->sort_priority)
1355 char *name1 = getStringToLower(entry1->name_sorting);
1356 char *name2 = getStringToLower(entry2->name_sorting);
1358 compare_result = strcmp(name1, name2);
1363 else if (class_sorting1 == class_sorting2)
1364 compare_result = entry1->sort_priority - entry2->sort_priority;
1366 compare_result = class_sorting1 - class_sorting2;
1368 return compare_result;
1371 static void createParentTreeInfoNode(TreeInfo *node_parent)
1375 if (node_parent == NULL)
1378 ti_new = newTreeInfo();
1379 setTreeInfoToDefaults(ti_new, node_parent->type);
1381 ti_new->node_parent = node_parent;
1382 ti_new->parent_link = TRUE;
1384 ti_new->name = ".. (parent directory)";
1385 ti_new->name_short = getStringCopy(ti_new->name);
1386 ti_new->name_sorting = getStringCopy(ti_new->name);
1388 ti_new->filename = "..";
1389 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1391 ti_new->sort_priority = node_parent->sort_priority;
1392 ti_new->class_desc = getLevelClassDescription(ti_new);
1394 pushTreeInfo(&node_parent->node_group, ti_new);
1397 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1398 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1400 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1401 TreeInfo *node_parent,
1402 char *level_directory,
1403 char *directory_name)
1405 char *directory_path = getPath2(level_directory, directory_name);
1406 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1407 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1408 LevelDirTree *leveldir_new = NULL;
1411 if (setup_file_list == NULL)
1413 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1415 free(directory_path);
1421 leveldir_new = newTreeInfo();
1424 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1426 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1428 leveldir_new->filename = getStringCopy(directory_name);
1430 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1432 /* set all structure fields according to the token/value pairs */
1433 ldi = *leveldir_new;
1434 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1435 setSetupInfo(levelinfo_tokens, i,
1436 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1437 *leveldir_new = ldi;
1439 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1441 free(leveldir_new->name);
1442 leveldir_new->name = getStringCopy(leveldir_new->filename);
1445 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1447 if (leveldir_new->name_short == NULL)
1448 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1450 if (leveldir_new->name_sorting == NULL)
1451 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1453 if (node_parent == NULL) /* top level group */
1455 leveldir_new->basepath = level_directory;
1456 leveldir_new->fullpath = leveldir_new->filename;
1458 else /* sub level group */
1460 leveldir_new->basepath = node_parent->basepath;
1461 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1464 if (leveldir_new->levels < 1)
1465 leveldir_new->levels = 1;
1467 leveldir_new->last_level =
1468 leveldir_new->first_level + leveldir_new->levels - 1;
1470 leveldir_new->user_defined =
1471 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1473 leveldir_new->color = LEVELCOLOR(leveldir_new);
1474 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1476 leveldir_new->handicap_level = /* set handicap to default value */
1477 (leveldir_new->user_defined ?
1478 leveldir_new->last_level :
1479 leveldir_new->first_level);
1481 pushTreeInfo(node_first, leveldir_new);
1483 freeSetupFileList(setup_file_list);
1485 if (leveldir_new->level_group)
1487 /* create node to link back to current level directory */
1488 createParentTreeInfoNode(leveldir_new);
1490 /* step into sub-directory and look for more level series */
1491 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1492 leveldir_new, directory_path);
1495 free(directory_path);
1501 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1502 TreeInfo *node_parent,
1503 char *level_directory)
1506 struct dirent *dir_entry;
1507 boolean valid_entry_found = FALSE;
1509 if ((dir = opendir(level_directory)) == NULL)
1511 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1515 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1517 struct stat file_status;
1518 char *directory_name = dir_entry->d_name;
1519 char *directory_path = getPath2(level_directory, directory_name);
1521 /* skip entries for current and parent directory */
1522 if (strcmp(directory_name, ".") == 0 ||
1523 strcmp(directory_name, "..") == 0)
1525 free(directory_path);
1529 /* find out if directory entry is itself a directory */
1530 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1531 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1533 free(directory_path);
1537 free(directory_path);
1539 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1540 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1541 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1544 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1551 if (!valid_entry_found)
1553 /* check if this directory directly contains a file "levelinfo.conf" */
1554 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1555 level_directory, ".");
1558 if (!valid_entry_found)
1559 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1563 void LoadLevelInfo()
1565 InitUserLevelDirectory(getLoginName());
1567 DrawInitText("Loading level series:", 120, FC_GREEN);
1569 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1570 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1572 /* before sorting, the first entries will be from the user directory */
1573 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1575 if (leveldir_first == NULL)
1576 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1578 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1581 dumpTreeInfo(leveldir_first, 0);
1585 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1586 TreeInfo *node_parent,
1587 char *base_directory,
1588 char *directory_name, int type)
1590 char *directory_path = getPath2(base_directory, directory_name);
1591 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1592 struct SetupFileList *setup_file_list = NULL;
1593 TreeInfo *artwork_new = NULL;
1596 if (access(filename, F_OK) == 0) /* file exists */
1597 setup_file_list = loadSetupFileList(filename);
1599 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1602 struct dirent *dir_entry;
1603 boolean valid_file_found = FALSE;
1605 if ((dir = opendir(directory_path)) != NULL)
1607 while ((dir_entry = readdir(dir)) != NULL)
1609 char *entry_name = dir_entry->d_name;
1611 if (FileIsArtworkType(entry_name, type))
1613 valid_file_found = TRUE;
1621 if (!valid_file_found)
1623 if (strcmp(directory_name, ".") != 0)
1624 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1626 free(directory_path);
1633 artwork_new = newTreeInfo();
1636 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1638 setTreeInfoToDefaults(artwork_new, type);
1640 artwork_new->filename = getStringCopy(directory_name);
1642 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1645 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1648 /* set all structure fields according to the token/value pairs */
1650 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1651 setSetupInfo(levelinfo_tokens, i,
1652 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1655 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1657 free(artwork_new->name);
1658 artwork_new->name = getStringCopy(artwork_new->filename);
1662 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1665 if (artwork_new->name_short == NULL)
1666 artwork_new->name_short = getStringCopy(artwork_new->name);
1668 if (artwork_new->name_sorting == NULL)
1669 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1672 if (node_parent == NULL) /* top level group */
1674 artwork_new->basepath = getStringCopy(base_directory);
1675 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1677 else /* sub level group */
1679 artwork_new->basepath = getStringCopy(node_parent->basepath);
1680 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1683 artwork_new->user_defined =
1684 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1686 /* (may use ".sort_priority" from "setup_file_list" above) */
1687 artwork_new->color = ARTWORKCOLOR(artwork_new);
1688 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1690 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1692 if (artwork_new->name != NULL)
1693 free(artwork_new->name);
1695 if (strcmp(artwork_new->filename, ".") == 0)
1697 if (artwork_new->user_defined)
1699 artwork_new->name = getStringCopy("private");
1700 artwork_new->sort_priority = ARTWORKCLASS_USER;
1704 artwork_new->name = getStringCopy("classic");
1705 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1708 artwork_new->color = ARTWORKCOLOR(artwork_new);
1709 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1712 artwork_new->name = getStringCopy(artwork_new->filename);
1714 artwork_new->name_short = getStringCopy(artwork_new->name);
1715 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1718 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1720 pushTreeInfo(node_first, artwork_new);
1722 freeSetupFileList(setup_file_list);
1724 free(directory_path);
1730 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1731 TreeInfo *node_parent,
1732 char *base_directory, int type)
1735 struct dirent *dir_entry;
1736 boolean valid_entry_found = FALSE;
1738 if ((dir = opendir(base_directory)) == NULL)
1740 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1741 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1745 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1747 struct stat file_status;
1748 char *directory_name = dir_entry->d_name;
1749 char *directory_path = getPath2(base_directory, directory_name);
1751 /* skip entries for current and parent directory */
1752 if (strcmp(directory_name, ".") == 0 ||
1753 strcmp(directory_name, "..") == 0)
1755 free(directory_path);
1759 /* find out if directory entry is itself a directory */
1760 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1761 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1763 free(directory_path);
1767 free(directory_path);
1769 /* check if this directory contains artwork with or without config file */
1770 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1772 directory_name, type);
1777 /* check if this directory directly contains artwork itself */
1778 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1779 base_directory, ".",
1781 if (!valid_entry_found)
1782 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1786 static TreeInfo *getDummyArtworkInfo(int type)
1788 /* this is only needed when there is completely no artwork available */
1789 TreeInfo *artwork_new = newTreeInfo();
1791 setTreeInfoToDefaults(artwork_new, type);
1793 artwork_new->filename = getStringCopy(NOT_AVAILABLE);
1794 artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
1795 artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
1797 if (artwork_new->name != NULL)
1798 free(artwork_new->name);
1800 artwork_new->name = getStringCopy(NOT_AVAILABLE);
1801 artwork_new->name_short = getStringCopy(NOT_AVAILABLE);
1802 artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
1807 void LoadArtworkInfo()
1809 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1811 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1812 options.graphics_directory,
1813 TREE_TYPE_GRAPHICS_DIR);
1814 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1815 getUserGraphicsDir(),
1816 TREE_TYPE_GRAPHICS_DIR);
1818 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1819 options.sounds_directory,
1820 TREE_TYPE_SOUNDS_DIR);
1821 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1823 TREE_TYPE_SOUNDS_DIR);
1825 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1826 options.music_directory,
1827 TREE_TYPE_MUSIC_DIR);
1828 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1830 TREE_TYPE_MUSIC_DIR);
1832 if (artwork.gfx_first == NULL)
1833 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1834 if (artwork.snd_first == NULL)
1835 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1836 if (artwork.mus_first == NULL)
1837 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1839 /* before sorting, the first entries will be from the user directory */
1840 artwork.gfx_current =
1841 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1842 if (artwork.gfx_current == NULL)
1843 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1845 artwork.snd_current =
1846 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1847 if (artwork.snd_current == NULL)
1848 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1850 artwork.mus_current =
1851 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1852 if (artwork.mus_current == NULL)
1853 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1855 artwork.graphics_set_current_name = artwork.gfx_current->name;
1856 artwork.sounds_set_current_name = artwork.snd_current->name;
1857 artwork.music_set_current_name = artwork.mus_current->name;
1860 printf("graphics set == %s\n\n", artwork.graphics_set_current_name);
1861 printf("sounds set == %s\n\n", artwork.sounds_set_current_name);
1862 printf("music set == %s\n\n", artwork.music_set_current_name);
1865 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1866 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1867 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1870 dumpTreeInfo(artwork.gfx_first, 0);
1871 dumpTreeInfo(artwork.snd_first, 0);
1872 dumpTreeInfo(artwork.mus_first, 0);
1876 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1877 LevelDirTree *level_node)
1879 /* recursively check all level directories for artwork sub-directories */
1883 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1884 ARTWORK_DIRECTORY((*artwork_node)->type));
1887 if (!level_node->parent_link)
1888 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1889 level_node->filename, level_node->name);
1892 if (!level_node->parent_link)
1894 TreeInfo *topnode_last = *artwork_node;
1896 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1897 (*artwork_node)->type);
1899 if (topnode_last != *artwork_node)
1901 free((*artwork_node)->name);
1902 free((*artwork_node)->name_sorting);
1903 free((*artwork_node)->name_short);
1905 (*artwork_node)->name = getStringCopy(level_node->name);
1906 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1907 (*artwork_node)->name_short = getStringCopy(level_node->filename);
1909 (*artwork_node)->sort_priority = level_node->sort_priority;
1910 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1916 if (level_node->node_group != NULL)
1917 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1919 level_node = level_node->next;
1923 void LoadLevelArtworkInfo()
1925 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1927 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1928 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1929 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1931 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1932 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1933 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1936 dumpTreeInfo(artwork.gfx_first, 0);
1937 dumpTreeInfo(artwork.snd_first, 0);
1938 dumpTreeInfo(artwork.mus_first, 0);
1942 static void SaveUserLevelInfo()
1948 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1950 if (!(file = fopen(filename, MODE_WRITE)))
1952 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1957 /* always start with reliable default values */
1958 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1960 ldi.name = getStringCopy(getLoginName());
1961 ldi.author = getStringCopy(getRealName());
1963 ldi.first_level = 1;
1964 ldi.sort_priority = LEVELCLASS_USER_START;
1965 ldi.readonly = FALSE;
1967 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1968 getCookie("LEVELINFO")));
1970 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1971 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1972 i != LEVELINFO_TOKEN_NAME_SORTING &&
1973 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1974 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1979 SetFilePermissions(filename, PERMS_PRIVATE);
1982 char *getSetupValue(int type, void *value)
1984 static char value_string[MAX_LINE_LEN];
1992 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1996 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2000 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2004 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2008 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2012 sprintf(value_string, "%d", *(int *)value);
2016 strcpy(value_string, *(char **)value);
2020 value_string[0] = '\0';
2024 return value_string;
2027 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2031 static char token_string[MAX_LINE_LEN];
2032 int token_type = token_info[token_nr].type;
2033 void *setup_value = token_info[token_nr].value;
2034 char *token_text = token_info[token_nr].text;
2035 char *value_string = getSetupValue(token_type, setup_value);
2037 /* build complete token string */
2038 sprintf(token_string, "%s%s", prefix, token_text);
2040 /* build setup entry line */
2041 line = getFormattedSetupEntry(token_string, value_string);
2043 if (token_type == TYPE_KEY_X11)
2045 Key key = *(Key *)setup_value;
2046 char *keyname = getKeyNameFromKey(key);
2048 /* add comment, if useful */
2049 if (strcmp(keyname, "(undefined)") != 0 &&
2050 strcmp(keyname, "(unknown)") != 0)
2052 /* add at least one whitespace */
2054 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2058 strcat(line, keyname);
2065 void LoadLevelSetup_LastSeries()
2068 struct SetupFileList *level_setup_list = NULL;
2070 /* always start with reliable default values */
2071 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2073 /* ----------------------------------------------------------------------- */
2074 /* ~/.<program>/levelsetup.conf */
2075 /* ----------------------------------------------------------------------- */
2077 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2079 if ((level_setup_list = loadSetupFileList(filename)))
2081 char *last_level_series =
2082 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2084 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2086 if (leveldir_current == NULL)
2087 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2089 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2091 freeSetupFileList(level_setup_list);
2094 Error(ERR_WARN, "using default setup values");
2099 void SaveLevelSetup_LastSeries()
2102 char *level_subdir = leveldir_current->filename;
2105 /* ----------------------------------------------------------------------- */
2106 /* ~/.<program>/levelsetup.conf */
2107 /* ----------------------------------------------------------------------- */
2109 InitUserDataDirectory();
2111 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2113 if (!(file = fopen(filename, MODE_WRITE)))
2115 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2120 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2121 getCookie("LEVELSETUP")));
2122 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2128 SetFilePermissions(filename, PERMS_PRIVATE);
2131 static void checkSeriesInfo()
2133 static char *level_directory = NULL;
2135 struct dirent *dir_entry;
2137 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2139 level_directory = getPath2((leveldir_current->user_defined ?
2140 getUserLevelDir(NULL) :
2141 options.level_directory),
2142 leveldir_current->fullpath);
2144 if ((dir = opendir(level_directory)) == NULL)
2146 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2150 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2152 if (strlen(dir_entry->d_name) > 4 &&
2153 dir_entry->d_name[3] == '.' &&
2154 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2156 char levelnum_str[4];
2159 strncpy(levelnum_str, dir_entry->d_name, 3);
2160 levelnum_str[3] = '\0';
2162 levelnum_value = atoi(levelnum_str);
2164 if (levelnum_value < leveldir_current->first_level)
2166 Error(ERR_WARN, "additional level %d found", levelnum_value);
2167 leveldir_current->first_level = levelnum_value;
2169 else if (levelnum_value > leveldir_current->last_level)
2171 Error(ERR_WARN, "additional level %d found", levelnum_value);
2172 leveldir_current->last_level = levelnum_value;
2180 void LoadLevelSetup_SeriesInfo()
2183 struct SetupFileList *level_setup_list = NULL;
2184 char *level_subdir = leveldir_current->filename;
2186 /* always start with reliable default values */
2187 level_nr = leveldir_current->first_level;
2189 checkSeriesInfo(leveldir_current);
2191 /* ----------------------------------------------------------------------- */
2192 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2193 /* ----------------------------------------------------------------------- */
2195 level_subdir = leveldir_current->filename;
2197 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2199 if ((level_setup_list = loadSetupFileList(filename)))
2203 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2207 level_nr = atoi(token_value);
2209 if (level_nr < leveldir_current->first_level)
2210 level_nr = leveldir_current->first_level;
2211 if (level_nr > leveldir_current->last_level)
2212 level_nr = leveldir_current->last_level;
2215 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2219 int level_nr = atoi(token_value);
2221 if (level_nr < leveldir_current->first_level)
2222 level_nr = leveldir_current->first_level;
2223 if (level_nr > leveldir_current->last_level + 1)
2224 level_nr = leveldir_current->last_level;
2226 if (leveldir_current->user_defined)
2227 level_nr = leveldir_current->last_level;
2229 leveldir_current->handicap_level = level_nr;
2232 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2234 freeSetupFileList(level_setup_list);
2237 Error(ERR_WARN, "using default setup values");
2242 void SaveLevelSetup_SeriesInfo()
2245 char *level_subdir = leveldir_current->filename;
2246 char *level_nr_str = int2str(level_nr, 0);
2247 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2250 /* ----------------------------------------------------------------------- */
2251 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2252 /* ----------------------------------------------------------------------- */
2254 InitLevelSetupDirectory(level_subdir);
2256 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2258 if (!(file = fopen(filename, MODE_WRITE)))
2260 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2265 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2266 getCookie("LEVELSETUP")));
2267 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2269 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2270 handicap_level_str));
2275 SetFilePermissions(filename, PERMS_PRIVATE);