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 static char *getSetupArtworkDir(TreeInfo *ti)
302 static char *artwork_dir = NULL;
304 if (artwork_dir != NULL)
307 artwork_dir = getPath2(ti->basepath, ti->fullpath);
312 void setLevelArtworkDir(TreeInfo *ti)
314 char **artwork_path_ptr, *artwork_set;
315 TreeInfo *level_artwork;
317 if (ti == NULL || leveldir_current == NULL)
321 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
322 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
323 &leveldir_current->music_path);
326 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
327 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
328 leveldir_current->music_set);
330 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)) == NULL)
333 if (*artwork_path_ptr != NULL)
334 free(*artwork_path_ptr);
336 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
339 static char *getLevelArtworkDir(int type)
343 if (leveldir_current == NULL)
344 return UNDEFINED_FILENAME;
347 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
348 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
349 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
355 char *getLevelFilename(int nr)
357 static char *filename = NULL;
358 char basename[MAX_FILENAME_LEN];
360 if (filename != NULL)
363 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
364 filename = getPath2(getCurrentLevelDir(), basename);
369 char *getTapeFilename(int nr)
371 static char *filename = NULL;
372 char basename[MAX_FILENAME_LEN];
374 if (filename != NULL)
377 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
378 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
383 char *getScoreFilename(int nr)
385 static char *filename = NULL;
386 char basename[MAX_FILENAME_LEN];
388 if (filename != NULL)
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 if (filename != NULL)
404 filename = getPath2(getSetupDir(), SETUP_FILENAME);
409 static char *getCorrectedImageBasename(char *basename)
411 char *basename_corrected = basename;
413 #if defined(PLATFORM_MSDOS)
414 if (program.filename_prefix != NULL)
416 int prefix_len = strlen(program.filename_prefix);
418 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
419 basename_corrected = &basename[prefix_len];
421 /* if corrected filename is still longer than standard MS-DOS filename
422 size (8 characters + 1 dot + 3 characters file extension), shorten
423 filename by writing file extension after 8th basename character */
424 if (strlen(basename_corrected) > 8+1+3)
426 static char *msdos_filename = NULL;
428 if (filename != NULL)
431 filename = getStringCopy(basename_corrected);
432 strncpy(&filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
437 return basename_corrected;
440 static boolean fileExists(char *filename)
443 printf("checking file '%s'\n", filename);
446 return (access(filename, F_OK) == 0);
449 char *getCustomImageFilename(char *basename)
451 static char *filename = NULL;
453 if (filename != NULL)
456 basename = getCorrectedImageBasename(basename);
458 if (!setup.override_level_graphics)
460 /* 1st try: look for special artwork configured in level series config */
461 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
462 if (fileExists(filename))
467 /* 2nd try: look for special artwork in current level series directory */
468 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
469 if (fileExists(filename))
475 /* 3rd try: look for special artwork in configured artwork directory */
476 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
477 if (fileExists(filename))
482 /* 4th try: look for default artwork in new default artwork directory */
483 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
484 if (fileExists(filename))
489 /* 5th try: look for default artwork in old default artwork directory */
490 filename = getPath2(options.graphics_directory, basename);
491 if (fileExists(filename))
494 return NULL; /* cannot find specified artwork file anywhere */
497 char *getCustomSoundFilename(char *basename)
499 static char *filename = NULL;
501 if (filename != NULL)
504 if (!setup.override_level_sounds)
506 /* 1st try: look for special artwork configured in level series config */
507 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
508 if (fileExists(filename))
513 /* 2nd try: look for special artwork in current level series directory */
514 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
515 if (fileExists(filename))
521 /* 3rd try: look for special artwork in configured artwork directory */
522 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
523 if (fileExists(filename))
528 /* 4th try: look for default artwork in new default artwork directory */
529 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
530 if (fileExists(filename))
535 /* 5th try: look for default artwork in old default artwork directory */
536 filename = getPath2(options.sounds_directory, basename);
537 if (fileExists(filename))
540 return NULL; /* cannot find specified artwork file anywhere */
543 char *getCustomArtworkFilename(char *basename, int type)
545 if (type == ARTWORK_TYPE_GRAPHICS)
546 return getCustomImageFilename(basename);
547 else if (type == ARTWORK_TYPE_SOUNDS)
548 return getCustomSoundFilename(basename);
550 return UNDEFINED_FILENAME;
553 char *getCustomArtworkConfigFilename(int type)
555 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
558 char *getCustomMusicDirectory(void)
560 static char *directory = NULL;
562 if (directory != NULL)
565 if (!setup.override_level_music)
567 /* 1st try: look for special artwork configured in level series config */
568 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
569 if (fileExists(directory))
574 /* 2nd try: look for special artwork in current level series directory */
575 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
576 if (fileExists(directory))
582 /* 3rd try: look for special artwork in configured artwork directory */
583 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
584 if (fileExists(directory))
589 /* 4th try: look for default artwork in new default artwork directory */
590 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
591 if (fileExists(directory))
596 /* 5th try: look for default artwork in old default artwork directory */
597 directory = getStringCopy(options.music_directory);
598 if (fileExists(directory))
601 return NULL; /* cannot find specified artwork file anywhere */
604 void InitTapeDirectory(char *level_subdir)
606 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
607 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
608 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
611 void InitScoreDirectory(char *level_subdir)
613 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
614 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
617 static void SaveUserLevelInfo();
619 void InitUserLevelDirectory(char *level_subdir)
621 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
623 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
624 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
625 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
631 void InitLevelSetupDirectory(char *level_subdir)
633 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
634 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
635 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
639 /* ------------------------------------------------------------------------- */
640 /* some functions to handle lists of level directories */
641 /* ------------------------------------------------------------------------- */
643 TreeInfo *newTreeInfo()
645 return checked_calloc(sizeof(TreeInfo));
648 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
650 node_new->next = *node_first;
651 *node_first = node_new;
654 int numTreeInfo(TreeInfo *node)
667 boolean validLevelSeries(TreeInfo *node)
669 return (node != NULL && !node->node_group && !node->parent_link);
672 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
677 if (node->node_group) /* enter level group (step down into tree) */
678 return getFirstValidTreeInfoEntry(node->node_group);
679 else if (node->parent_link) /* skip start entry of level group */
681 if (node->next) /* get first real level series entry */
682 return getFirstValidTreeInfoEntry(node->next);
683 else /* leave empty level group and go on */
684 return getFirstValidTreeInfoEntry(node->node_parent->next);
686 else /* this seems to be a regular level series */
690 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
695 if (node->node_parent == NULL) /* top level group */
696 return *node->node_top;
697 else /* sub level group */
698 return node->node_parent->node_group;
701 int numTreeInfoInGroup(TreeInfo *node)
703 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
706 int posTreeInfo(TreeInfo *node)
708 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
713 if (node_cmp == node)
717 node_cmp = node_cmp->next;
723 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
725 TreeInfo *node_default = node;
740 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
742 if (identifier == NULL)
747 if (node->node_group)
749 TreeInfo *node_group;
751 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
756 else if (!node->parent_link)
758 if (strcmp(identifier, node->identifier) == 0)
768 void dumpTreeInfo(TreeInfo *node, int depth)
772 printf("Dumping TreeInfo:\n");
776 for (i=0; i<(depth + 1) * 3; i++)
779 printf("filename == '%s' (%s) [%s] (%d)\n",
780 node->filename, node->name, node->identifier, node->sort_priority);
782 if (node->node_group != NULL)
783 dumpTreeInfo(node->node_group, depth + 1);
789 void sortTreeInfo(TreeInfo **node_first,
790 int (*compare_function)(const void *, const void *))
792 int num_nodes = numTreeInfo(*node_first);
793 TreeInfo **sort_array;
794 TreeInfo *node = *node_first;
800 /* allocate array for sorting structure pointers */
801 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
803 /* writing structure pointers to sorting array */
804 while (i < num_nodes && node) /* double boundary check... */
806 sort_array[i] = node;
812 /* sorting the structure pointers in the sorting array */
813 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
816 /* update the linkage of list elements with the sorted node array */
817 for (i=0; i<num_nodes - 1; i++)
818 sort_array[i]->next = sort_array[i + 1];
819 sort_array[num_nodes - 1]->next = NULL;
821 /* update the linkage of the main list anchor pointer */
822 *node_first = sort_array[0];
826 /* now recursively sort the level group structures */
830 if (node->node_group != NULL)
831 sortTreeInfo(&node->node_group, compare_function);
838 /* ========================================================================= */
839 /* some stuff from "files.c" */
840 /* ========================================================================= */
842 #if defined(PLATFORM_WIN32)
844 #define S_IRGRP S_IRUSR
847 #define S_IROTH S_IRUSR
850 #define S_IWGRP S_IWUSR
853 #define S_IWOTH S_IWUSR
856 #define S_IXGRP S_IXUSR
859 #define S_IXOTH S_IXUSR
862 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
867 #endif /* PLATFORM_WIN32 */
869 /* file permissions for newly written files */
870 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
871 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
872 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
874 #define MODE_W_PRIVATE (S_IWUSR)
875 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
876 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
878 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
879 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
881 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
882 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
884 char *getUserDataDir(void)
886 static char *userdata_dir = NULL;
890 char *home_dir = getHomeDir();
891 char *data_dir = program.userdata_directory;
893 userdata_dir = getPath2(home_dir, data_dir);
901 return getUserDataDir();
904 static mode_t posix_umask(mode_t mask)
906 #if defined(PLATFORM_UNIX)
913 static int posix_mkdir(const char *pathname, mode_t mode)
915 #if defined(PLATFORM_WIN32)
916 return mkdir(pathname);
918 return mkdir(pathname, mode);
922 void createDirectory(char *dir, char *text, int permission_class)
924 /* leave "other" permissions in umask untouched, but ensure group parts
925 of USERDATA_DIR_MODE are not masked */
926 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
927 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
928 mode_t normal_umask = posix_umask(0);
929 mode_t group_umask = ~(dir_mode & S_IRWXG);
930 posix_umask(normal_umask & group_umask);
932 if (access(dir, F_OK) != 0)
933 if (posix_mkdir(dir, dir_mode) != 0)
934 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
936 posix_umask(normal_umask); /* reset normal umask */
939 void InitUserDataDirectory()
941 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
944 void SetFilePermissions(char *filename, int permission_class)
946 chmod(filename, (permission_class == PERMS_PRIVATE ?
947 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
950 char *getCookie(char *file_type)
952 static char cookie[MAX_COOKIE_LEN + 1];
954 if (strlen(program.cookie_prefix) + 1 +
955 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
956 return "[COOKIE ERROR]"; /* should never happen */
958 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
959 program.cookie_prefix, file_type,
960 program.version_major, program.version_minor);
965 int getFileVersionFromCookieString(const char *cookie)
967 const char *ptr_cookie1, *ptr_cookie2;
968 const char *pattern1 = "_FILE_VERSION_";
969 const char *pattern2 = "?.?";
970 const int len_cookie = strlen(cookie);
971 const int len_pattern1 = strlen(pattern1);
972 const int len_pattern2 = strlen(pattern2);
973 const int len_pattern = len_pattern1 + len_pattern2;
974 int version_major, version_minor;
976 if (len_cookie <= len_pattern)
979 ptr_cookie1 = &cookie[len_cookie - len_pattern];
980 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
982 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
985 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
986 ptr_cookie2[1] != '.' ||
987 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
990 version_major = ptr_cookie2[0] - '0';
991 version_minor = ptr_cookie2[2] - '0';
993 return VERSION_IDENT(version_major, version_minor, 0);
996 boolean checkCookieString(const char *cookie, const char *template)
998 const char *pattern = "_FILE_VERSION_?.?";
999 const int len_cookie = strlen(cookie);
1000 const int len_template = strlen(template);
1001 const int len_pattern = strlen(pattern);
1003 if (len_cookie != len_template)
1006 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1012 /* ------------------------------------------------------------------------- */
1013 /* setup file list handling functions */
1014 /* ------------------------------------------------------------------------- */
1016 char *getFormattedSetupEntry(char *token, char *value)
1019 static char entry[MAX_LINE_LEN];
1021 /* start with the token and some spaces to format output line */
1022 sprintf(entry, "%s:", token);
1023 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1026 /* continue with the token's value */
1027 strcat(entry, value);
1032 void freeSetupFileList(struct SetupFileList *setup_file_list)
1034 if (!setup_file_list)
1037 if (setup_file_list->token)
1038 free(setup_file_list->token);
1039 if (setup_file_list->value)
1040 free(setup_file_list->value);
1041 if (setup_file_list->next)
1042 freeSetupFileList(setup_file_list->next);
1043 free(setup_file_list);
1046 static struct SetupFileList *newSetupFileList(char *token, char *value)
1048 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1050 new->token = checked_malloc(strlen(token) + 1);
1051 strcpy(new->token, token);
1053 new->value = checked_malloc(strlen(value) + 1);
1054 strcpy(new->value, value);
1061 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1063 if (!setup_file_list)
1066 if (strcmp(setup_file_list->token, token) == 0)
1067 return setup_file_list->value;
1069 return getTokenValue(setup_file_list->next, token);
1072 static void setTokenValue(struct SetupFileList *setup_file_list,
1073 char *token, char *value)
1075 if (!setup_file_list)
1078 if (strcmp(setup_file_list->token, token) == 0)
1080 free(setup_file_list->value);
1081 setup_file_list->value = checked_malloc(strlen(value) + 1);
1082 strcpy(setup_file_list->value, value);
1084 else if (setup_file_list->next == NULL)
1085 setup_file_list->next = newSetupFileList(token, value);
1087 setTokenValue(setup_file_list->next, token, value);
1091 static void printSetupFileList(struct SetupFileList *setup_file_list)
1093 if (!setup_file_list)
1096 printf("token: '%s'\n", setup_file_list->token);
1097 printf("value: '%s'\n", setup_file_list->value);
1099 printSetupFileList(setup_file_list->next);
1103 struct SetupFileList *loadSetupFileList(char *filename)
1106 char line[MAX_LINE_LEN];
1107 char *token, *value, *line_ptr;
1108 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1109 struct SetupFileList *first_valid_list_entry;
1113 if (!(file = fopen(filename, MODE_READ)))
1115 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1121 /* read next line of input file */
1122 if (!fgets(line, MAX_LINE_LEN, file))
1125 /* cut trailing comment or whitespace from input line */
1126 for (line_ptr = line; *line_ptr; line_ptr++)
1128 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1135 /* cut trailing whitespaces from input line */
1136 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1137 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1140 /* ignore empty lines */
1144 line_len = strlen(line);
1146 /* cut leading whitespaces from token */
1147 for (token = line; *token; token++)
1148 if (*token != ' ' && *token != '\t')
1151 /* find end of token */
1152 for (line_ptr = token; *line_ptr; line_ptr++)
1154 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1161 if (line_ptr < line + line_len)
1162 value = line_ptr + 1;
1166 /* cut leading whitespaces from value */
1167 for (; *value; value++)
1168 if (*value != ' ' && *value != '\t')
1171 if (*token && *value)
1172 setTokenValue(setup_file_list, token, value);
1177 first_valid_list_entry = setup_file_list->next;
1179 /* free empty list header */
1180 setup_file_list->next = NULL;
1181 freeSetupFileList(setup_file_list);
1183 if (first_valid_list_entry == NULL)
1184 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1186 return first_valid_list_entry;
1189 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1192 if (!setup_file_list)
1195 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1197 if (!checkCookieString(setup_file_list->value, identifier))
1199 Error(ERR_WARN, "configuration file has wrong file identifier");
1206 if (setup_file_list->next)
1207 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1210 Error(ERR_WARN, "configuration file has no file identifier");
1216 /* ========================================================================= */
1217 /* setup file stuff */
1218 /* ========================================================================= */
1220 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1221 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1222 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1224 /* level directory info */
1225 #define LEVELINFO_TOKEN_IDENTIFIER 0
1226 #define LEVELINFO_TOKEN_NAME 1
1227 #define LEVELINFO_TOKEN_NAME_SORTING 2
1228 #define LEVELINFO_TOKEN_AUTHOR 3
1229 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1230 #define LEVELINFO_TOKEN_LEVELS 5
1231 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1232 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1233 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1234 #define LEVELINFO_TOKEN_READONLY 9
1235 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1236 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1237 #define LEVELINFO_TOKEN_MUSIC_SET 12
1239 #define NUM_LEVELINFO_TOKENS 13
1241 static LevelDirTree ldi;
1243 static struct TokenInfo levelinfo_tokens[] =
1245 /* level directory info */
1246 { TYPE_STRING, &ldi.identifier, "identifier" },
1247 { TYPE_STRING, &ldi.name, "name" },
1248 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1249 { TYPE_STRING, &ldi.author, "author" },
1250 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1251 { TYPE_INTEGER, &ldi.levels, "levels" },
1252 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1253 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1254 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1255 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1256 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1257 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1258 { TYPE_STRING, &ldi.music_set, "music_set" }
1261 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1265 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1266 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1267 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1268 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1271 ldi->node_parent = NULL;
1272 ldi->node_group = NULL;
1276 ldi->cl_cursor = -1;
1278 ldi->filename = NULL;
1279 ldi->fullpath = NULL;
1280 ldi->basepath = NULL;
1281 ldi->identifier = NULL;
1282 ldi->name = getStringCopy(ANONYMOUS_NAME);
1283 ldi->name_sorting = NULL;
1284 ldi->author = getStringCopy(ANONYMOUS_NAME);
1286 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1287 ldi->parent_link = FALSE;
1288 ldi->user_defined = FALSE;
1290 ldi->class_desc = NULL;
1292 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1294 ldi->imported_from = NULL;
1295 ldi->graphics_set = NULL;
1296 ldi->sounds_set = NULL;
1297 ldi->music_set = NULL;
1298 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1299 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1300 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1302 ldi->first_level = 0;
1303 ldi->last_level = 0;
1304 ldi->level_group = FALSE;
1305 ldi->handicap_level = 0;
1306 ldi->readonly = TRUE;
1310 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1314 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1316 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1320 /* first copy all values from the parent structure ... */
1323 /* ... then set all fields to default that cannot be inherited from parent.
1324 This is especially important for all those fields that can be set from
1325 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1326 calls 'free()' for all already set token values which requires that no
1327 other structure's pointer may point to them!
1330 ldi->filename = NULL;
1331 ldi->fullpath = NULL;
1332 ldi->basepath = NULL;
1333 ldi->identifier = NULL;
1334 ldi->name = getStringCopy(ANONYMOUS_NAME);
1335 ldi->name_sorting = NULL;
1336 ldi->author = getStringCopy(parent->author);
1337 ldi->imported_from = getStringCopy(parent->imported_from);
1339 ldi->level_group = FALSE;
1340 ldi->parent_link = FALSE;
1342 ldi->node_top = parent->node_top;
1343 ldi->node_parent = parent;
1344 ldi->node_group = NULL;
1348 void setSetupInfo(struct TokenInfo *token_info,
1349 int token_nr, char *token_value)
1351 int token_type = token_info[token_nr].type;
1352 void *setup_value = token_info[token_nr].value;
1354 if (token_value == NULL)
1357 /* set setup field to corresponding token value */
1362 *(boolean *)setup_value = get_boolean_from_string(token_value);
1366 *(Key *)setup_value = getKeyFromKeyName(token_value);
1370 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1374 *(int *)setup_value = get_integer_from_string(token_value);
1378 if (*(char **)setup_value != NULL)
1379 free(*(char **)setup_value);
1380 *(char **)setup_value = getStringCopy(token_value);
1388 static int compareTreeInfoEntries(const void *object1, const void *object2)
1390 const TreeInfo *entry1 = *((TreeInfo **)object1);
1391 const TreeInfo *entry2 = *((TreeInfo **)object2);
1392 int class_sorting1, class_sorting2;
1395 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1397 class_sorting1 = LEVELSORTING(entry1);
1398 class_sorting2 = LEVELSORTING(entry2);
1402 class_sorting1 = ARTWORKSORTING(entry1);
1403 class_sorting2 = ARTWORKSORTING(entry2);
1406 if (entry1->parent_link || entry2->parent_link)
1407 compare_result = (entry1->parent_link ? -1 : +1);
1408 else if (entry1->sort_priority == entry2->sort_priority)
1410 char *name1 = getStringToLower(entry1->name_sorting);
1411 char *name2 = getStringToLower(entry2->name_sorting);
1413 compare_result = strcmp(name1, name2);
1418 else if (class_sorting1 == class_sorting2)
1419 compare_result = entry1->sort_priority - entry2->sort_priority;
1421 compare_result = class_sorting1 - class_sorting2;
1423 return compare_result;
1426 static void createParentTreeInfoNode(TreeInfo *node_parent)
1430 if (node_parent == NULL)
1433 ti_new = newTreeInfo();
1434 setTreeInfoToDefaults(ti_new, node_parent->type);
1436 ti_new->node_parent = node_parent;
1437 ti_new->parent_link = TRUE;
1439 ti_new->identifier = getStringCopy(node_parent->identifier);
1440 ti_new->name = ".. (parent directory)";
1441 ti_new->name_sorting = getStringCopy(ti_new->name);
1443 ti_new->filename = "..";
1444 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1446 ti_new->sort_priority = node_parent->sort_priority;
1447 ti_new->class_desc = getLevelClassDescription(ti_new);
1449 pushTreeInfo(&node_parent->node_group, ti_new);
1452 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1453 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1455 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1456 TreeInfo *node_parent,
1457 char *level_directory,
1458 char *directory_name)
1460 char *directory_path = getPath2(level_directory, directory_name);
1461 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1462 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1463 LevelDirTree *leveldir_new = NULL;
1466 if (setup_file_list == NULL)
1468 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1470 free(directory_path);
1476 leveldir_new = newTreeInfo();
1479 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1481 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1483 leveldir_new->filename = getStringCopy(directory_name);
1485 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1487 /* set all structure fields according to the token/value pairs */
1488 ldi = *leveldir_new;
1489 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1490 setSetupInfo(levelinfo_tokens, i,
1491 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1492 *leveldir_new = ldi;
1494 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1496 free(leveldir_new->name);
1497 leveldir_new->name = getStringCopy(leveldir_new->filename);
1500 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1502 if (leveldir_new->identifier == NULL)
1503 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1505 if (leveldir_new->name_sorting == NULL)
1506 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1508 if (node_parent == NULL) /* top level group */
1510 leveldir_new->basepath = level_directory;
1511 leveldir_new->fullpath = leveldir_new->filename;
1513 else /* sub level group */
1515 leveldir_new->basepath = node_parent->basepath;
1516 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1519 if (leveldir_new->levels < 1)
1520 leveldir_new->levels = 1;
1522 leveldir_new->last_level =
1523 leveldir_new->first_level + leveldir_new->levels - 1;
1525 leveldir_new->user_defined =
1526 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1528 leveldir_new->color = LEVELCOLOR(leveldir_new);
1529 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1531 leveldir_new->handicap_level = /* set handicap to default value */
1532 (leveldir_new->user_defined ?
1533 leveldir_new->last_level :
1534 leveldir_new->first_level);
1536 pushTreeInfo(node_first, leveldir_new);
1538 freeSetupFileList(setup_file_list);
1540 if (leveldir_new->level_group)
1542 /* create node to link back to current level directory */
1543 createParentTreeInfoNode(leveldir_new);
1545 /* step into sub-directory and look for more level series */
1546 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1547 leveldir_new, directory_path);
1550 free(directory_path);
1556 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1557 TreeInfo *node_parent,
1558 char *level_directory)
1561 struct dirent *dir_entry;
1562 boolean valid_entry_found = FALSE;
1564 if ((dir = opendir(level_directory)) == NULL)
1566 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1570 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1572 struct stat file_status;
1573 char *directory_name = dir_entry->d_name;
1574 char *directory_path = getPath2(level_directory, directory_name);
1576 /* skip entries for current and parent directory */
1577 if (strcmp(directory_name, ".") == 0 ||
1578 strcmp(directory_name, "..") == 0)
1580 free(directory_path);
1584 /* find out if directory entry is itself a directory */
1585 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1586 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1588 free(directory_path);
1592 free(directory_path);
1594 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1595 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1596 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1599 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1606 if (!valid_entry_found)
1608 /* check if this directory directly contains a file "levelinfo.conf" */
1609 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1610 level_directory, ".");
1613 if (!valid_entry_found)
1614 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1618 void LoadLevelInfo()
1620 InitUserLevelDirectory(getLoginName());
1622 DrawInitText("Loading level series:", 120, FC_GREEN);
1624 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1625 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1627 /* before sorting, the first entries will be from the user directory */
1628 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1630 if (leveldir_first == NULL)
1631 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1633 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1636 dumpTreeInfo(leveldir_first, 0);
1640 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1641 TreeInfo *node_parent,
1642 char *base_directory,
1643 char *directory_name, int type)
1645 char *directory_path = getPath2(base_directory, directory_name);
1646 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1647 struct SetupFileList *setup_file_list = NULL;
1648 TreeInfo *artwork_new = NULL;
1651 if (access(filename, F_OK) == 0) /* file exists */
1652 setup_file_list = loadSetupFileList(filename);
1654 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1657 struct dirent *dir_entry;
1658 boolean valid_file_found = FALSE;
1660 if ((dir = opendir(directory_path)) != NULL)
1662 while ((dir_entry = readdir(dir)) != NULL)
1664 char *entry_name = dir_entry->d_name;
1666 if (FileIsArtworkType(entry_name, type))
1668 valid_file_found = TRUE;
1676 if (!valid_file_found)
1678 if (strcmp(directory_name, ".") != 0)
1679 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1681 free(directory_path);
1688 artwork_new = newTreeInfo();
1691 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1693 setTreeInfoToDefaults(artwork_new, type);
1695 artwork_new->filename = getStringCopy(directory_name);
1697 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1700 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1703 /* set all structure fields according to the token/value pairs */
1705 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1706 setSetupInfo(levelinfo_tokens, i,
1707 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1710 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1712 free(artwork_new->name);
1713 artwork_new->name = getStringCopy(artwork_new->filename);
1717 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1720 if (artwork_new->identifier == NULL)
1721 artwork_new->identifier = getStringCopy(artwork_new->filename);
1723 if (artwork_new->name_sorting == NULL)
1724 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1727 if (node_parent == NULL) /* top level group */
1729 artwork_new->basepath = getStringCopy(base_directory);
1730 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1732 else /* sub level group */
1734 artwork_new->basepath = getStringCopy(node_parent->basepath);
1735 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1738 artwork_new->user_defined =
1739 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1741 /* (may use ".sort_priority" from "setup_file_list" above) */
1742 artwork_new->color = ARTWORKCOLOR(artwork_new);
1743 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1745 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1747 if (artwork_new->name != NULL)
1748 free(artwork_new->name);
1750 if (strcmp(artwork_new->filename, ".") == 0)
1752 if (artwork_new->user_defined)
1754 artwork_new->identifier = getStringCopy("private");
1755 artwork_new->sort_priority = ARTWORKCLASS_USER;
1759 artwork_new->identifier = getStringCopy("classic");
1760 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1763 /* set to new values after changing ".sort_priority" */
1764 artwork_new->color = ARTWORKCOLOR(artwork_new);
1765 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1769 artwork_new->identifier = getStringCopy(artwork_new->filename);
1772 artwork_new->name = getStringCopy(artwork_new->identifier);
1773 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1776 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1778 pushTreeInfo(node_first, artwork_new);
1780 freeSetupFileList(setup_file_list);
1782 free(directory_path);
1788 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1789 TreeInfo *node_parent,
1790 char *base_directory, int type)
1793 struct dirent *dir_entry;
1794 boolean valid_entry_found = FALSE;
1796 if ((dir = opendir(base_directory)) == NULL)
1798 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1799 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1803 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1805 struct stat file_status;
1806 char *directory_name = dir_entry->d_name;
1807 char *directory_path = getPath2(base_directory, directory_name);
1809 /* skip entries for current and parent directory */
1810 if (strcmp(directory_name, ".") == 0 ||
1811 strcmp(directory_name, "..") == 0)
1813 free(directory_path);
1817 /* find out if directory entry is itself a directory */
1818 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1819 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1821 free(directory_path);
1825 free(directory_path);
1827 /* check if this directory contains artwork with or without config file */
1828 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1830 directory_name, type);
1835 /* check if this directory directly contains artwork itself */
1836 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1837 base_directory, ".",
1839 if (!valid_entry_found)
1840 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1844 static TreeInfo *getDummyArtworkInfo(int type)
1846 /* this is only needed when there is completely no artwork available */
1847 TreeInfo *artwork_new = newTreeInfo();
1849 setTreeInfoToDefaults(artwork_new, type);
1851 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1852 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1853 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1855 if (artwork_new->name != NULL)
1856 free(artwork_new->name);
1858 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1859 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1860 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1865 void LoadArtworkInfo()
1867 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1869 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1870 options.graphics_directory,
1871 TREE_TYPE_GRAPHICS_DIR);
1872 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1873 getUserGraphicsDir(),
1874 TREE_TYPE_GRAPHICS_DIR);
1876 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1877 options.sounds_directory,
1878 TREE_TYPE_SOUNDS_DIR);
1879 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1881 TREE_TYPE_SOUNDS_DIR);
1883 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1884 options.music_directory,
1885 TREE_TYPE_MUSIC_DIR);
1886 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1888 TREE_TYPE_MUSIC_DIR);
1890 if (artwork.gfx_first == NULL)
1891 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1892 if (artwork.snd_first == NULL)
1893 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1894 if (artwork.mus_first == NULL)
1895 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1897 /* before sorting, the first entries will be from the user directory */
1898 artwork.gfx_current =
1899 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1900 if (artwork.gfx_current == NULL)
1901 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1903 artwork.snd_current =
1904 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1905 if (artwork.snd_current == NULL)
1906 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1908 artwork.mus_current =
1909 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1910 if (artwork.mus_current == NULL)
1911 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1913 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1914 artwork.snd_current_identifier = artwork.snd_current->identifier;
1915 artwork.mus_current_identifier = artwork.mus_current->identifier;
1918 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1919 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1920 printf("music set == %s\n\n", artwork.mus_current_identifier);
1923 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1924 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1925 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1928 dumpTreeInfo(artwork.gfx_first, 0);
1929 dumpTreeInfo(artwork.snd_first, 0);
1930 dumpTreeInfo(artwork.mus_first, 0);
1934 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1935 LevelDirTree *level_node)
1937 /* recursively check all level directories for artwork sub-directories */
1941 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1942 ARTWORK_DIRECTORY((*artwork_node)->type));
1945 if (!level_node->parent_link)
1946 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1947 level_node->filename, level_node->name);
1950 if (!level_node->parent_link)
1952 TreeInfo *topnode_last = *artwork_node;
1954 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1955 (*artwork_node)->type);
1957 if (topnode_last != *artwork_node)
1959 free((*artwork_node)->identifier);
1960 free((*artwork_node)->name);
1961 free((*artwork_node)->name_sorting);
1963 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1964 (*artwork_node)->name = getStringCopy(level_node->name);
1965 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1967 (*artwork_node)->sort_priority = level_node->sort_priority;
1968 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1974 if (level_node->node_group != NULL)
1975 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1977 level_node = level_node->next;
1981 void LoadLevelArtworkInfo()
1983 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1985 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1986 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1987 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1989 /* needed for reloading level artwork not known at ealier stage */
1990 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
1992 artwork.gfx_current =
1993 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1994 if (artwork.gfx_current == NULL)
1995 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1998 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2000 artwork.snd_current =
2001 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2002 if (artwork.snd_current == NULL)
2003 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2006 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2008 artwork.mus_current =
2009 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2010 if (artwork.mus_current == NULL)
2011 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2014 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2015 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2016 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2019 dumpTreeInfo(artwork.gfx_first, 0);
2020 dumpTreeInfo(artwork.snd_first, 0);
2021 dumpTreeInfo(artwork.mus_first, 0);
2025 static void SaveUserLevelInfo()
2031 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2033 if (!(file = fopen(filename, MODE_WRITE)))
2035 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2040 /* always start with reliable default values */
2041 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2043 ldi.name = getStringCopy(getLoginName());
2044 ldi.author = getStringCopy(getRealName());
2046 ldi.first_level = 1;
2047 ldi.sort_priority = LEVELCLASS_USER_START;
2048 ldi.readonly = FALSE;
2049 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2050 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2051 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2053 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2054 getCookie("LEVELINFO")));
2056 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2057 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2058 i != LEVELINFO_TOKEN_NAME_SORTING &&
2059 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2060 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2065 SetFilePermissions(filename, PERMS_PRIVATE);
2068 char *getSetupValue(int type, void *value)
2070 static char value_string[MAX_LINE_LEN];
2078 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2082 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2086 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2090 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2094 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2098 sprintf(value_string, "%d", *(int *)value);
2102 strcpy(value_string, *(char **)value);
2106 value_string[0] = '\0';
2110 return value_string;
2113 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2117 static char token_string[MAX_LINE_LEN];
2118 int token_type = token_info[token_nr].type;
2119 void *setup_value = token_info[token_nr].value;
2120 char *token_text = token_info[token_nr].text;
2121 char *value_string = getSetupValue(token_type, setup_value);
2123 /* build complete token string */
2124 sprintf(token_string, "%s%s", prefix, token_text);
2126 /* build setup entry line */
2127 line = getFormattedSetupEntry(token_string, value_string);
2129 if (token_type == TYPE_KEY_X11)
2131 Key key = *(Key *)setup_value;
2132 char *keyname = getKeyNameFromKey(key);
2134 /* add comment, if useful */
2135 if (strcmp(keyname, "(undefined)") != 0 &&
2136 strcmp(keyname, "(unknown)") != 0)
2138 /* add at least one whitespace */
2140 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2144 strcat(line, keyname);
2151 void LoadLevelSetup_LastSeries()
2154 struct SetupFileList *level_setup_list = NULL;
2156 /* always start with reliable default values */
2157 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2159 /* ----------------------------------------------------------------------- */
2160 /* ~/.<program>/levelsetup.conf */
2161 /* ----------------------------------------------------------------------- */
2163 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2165 if ((level_setup_list = loadSetupFileList(filename)))
2167 char *last_level_series =
2168 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2170 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2172 if (leveldir_current == NULL)
2173 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2175 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2177 freeSetupFileList(level_setup_list);
2180 Error(ERR_WARN, "using default setup values");
2185 void SaveLevelSetup_LastSeries()
2188 char *level_subdir = leveldir_current->filename;
2191 /* ----------------------------------------------------------------------- */
2192 /* ~/.<program>/levelsetup.conf */
2193 /* ----------------------------------------------------------------------- */
2195 InitUserDataDirectory();
2197 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2199 if (!(file = fopen(filename, MODE_WRITE)))
2201 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2206 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2207 getCookie("LEVELSETUP")));
2208 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2214 SetFilePermissions(filename, PERMS_PRIVATE);
2217 static void checkSeriesInfo()
2219 static char *level_directory = NULL;
2221 struct dirent *dir_entry;
2223 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2225 level_directory = getPath2((leveldir_current->user_defined ?
2226 getUserLevelDir(NULL) :
2227 options.level_directory),
2228 leveldir_current->fullpath);
2230 if ((dir = opendir(level_directory)) == NULL)
2232 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2236 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2238 if (strlen(dir_entry->d_name) > 4 &&
2239 dir_entry->d_name[3] == '.' &&
2240 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2242 char levelnum_str[4];
2245 strncpy(levelnum_str, dir_entry->d_name, 3);
2246 levelnum_str[3] = '\0';
2248 levelnum_value = atoi(levelnum_str);
2250 if (levelnum_value < leveldir_current->first_level)
2252 Error(ERR_WARN, "additional level %d found", levelnum_value);
2253 leveldir_current->first_level = levelnum_value;
2255 else if (levelnum_value > leveldir_current->last_level)
2257 Error(ERR_WARN, "additional level %d found", levelnum_value);
2258 leveldir_current->last_level = levelnum_value;
2266 void LoadLevelSetup_SeriesInfo()
2269 struct SetupFileList *level_setup_list = NULL;
2270 char *level_subdir = leveldir_current->filename;
2272 /* always start with reliable default values */
2273 level_nr = leveldir_current->first_level;
2275 checkSeriesInfo(leveldir_current);
2277 /* ----------------------------------------------------------------------- */
2278 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2279 /* ----------------------------------------------------------------------- */
2281 level_subdir = leveldir_current->filename;
2283 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2285 if ((level_setup_list = loadSetupFileList(filename)))
2289 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2293 level_nr = atoi(token_value);
2295 if (level_nr < leveldir_current->first_level)
2296 level_nr = leveldir_current->first_level;
2297 if (level_nr > leveldir_current->last_level)
2298 level_nr = leveldir_current->last_level;
2301 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2305 int level_nr = atoi(token_value);
2307 if (level_nr < leveldir_current->first_level)
2308 level_nr = leveldir_current->first_level;
2309 if (level_nr > leveldir_current->last_level + 1)
2310 level_nr = leveldir_current->last_level;
2312 if (leveldir_current->user_defined)
2313 level_nr = leveldir_current->last_level;
2315 leveldir_current->handicap_level = level_nr;
2318 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2320 freeSetupFileList(level_setup_list);
2323 Error(ERR_WARN, "using default setup values");
2328 void SaveLevelSetup_SeriesInfo()
2331 char *level_subdir = leveldir_current->filename;
2332 char *level_nr_str = int2str(level_nr, 0);
2333 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2336 /* ----------------------------------------------------------------------- */
2337 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2338 /* ----------------------------------------------------------------------- */
2340 InitLevelSetupDirectory(level_subdir);
2342 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2344 if (!(file = fopen(filename, MODE_WRITE)))
2346 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2351 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2352 getCookie("LEVELSETUP")));
2353 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2355 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2356 handicap_level_str));
2361 SetFilePermissions(filename, PERMS_PRIVATE);