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>
26 /* file names and filename extensions */
27 #if !defined(PLATFORM_MSDOS)
28 #define LEVELSETUP_DIRECTORY "levelsetup"
29 #define SETUP_FILENAME "setup.conf"
30 #define LEVELSETUP_FILENAME "levelsetup.conf"
31 #define LEVELINFO_FILENAME "levelinfo.conf"
32 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
33 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
34 #define MUSICINFO_FILENAME "musicinfo.conf"
35 #define LEVELFILE_EXTENSION "level"
36 #define TAPEFILE_EXTENSION "tape"
37 #define SCOREFILE_EXTENSION "score"
39 #define LEVELSETUP_DIRECTORY "lvlsetup"
40 #define SETUP_FILENAME "setup.cnf"
41 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
42 #define LEVELINFO_FILENAME "lvlinfo.cnf"
43 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
44 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
45 #define MUSICINFO_FILENAME "musinfo.cnf"
46 #define LEVELFILE_EXTENSION "lvl"
47 #define TAPEFILE_EXTENSION "tap"
48 #define SCOREFILE_EXTENSION "sco"
51 #define NUM_LEVELCLASS_DESC 8
52 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
64 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
65 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
66 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
67 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
68 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
69 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
70 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
71 IS_LEVELCLASS_USER(n) ? FC_RED : \
74 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
75 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
76 IS_LEVELCLASS_BD(n) ? 2 : \
77 IS_LEVELCLASS_EM(n) ? 3 : \
78 IS_LEVELCLASS_SP(n) ? 4 : \
79 IS_LEVELCLASS_DX(n) ? 5 : \
80 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
81 IS_LEVELCLASS_USER(n) ? 7 : \
84 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
85 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
86 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
87 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
90 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
91 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
92 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
93 IS_ARTWORKCLASS_USER(n) ? 3 : \
96 #define TOKEN_VALUE_POSITION 40
97 #define TOKEN_COMMENT_POSITION 60
99 #define MAX_COOKIE_LEN 256
101 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
102 GRAPHICSINFO_FILENAME : \
103 (type) == TREE_TYPE_SOUNDS_DIR ? \
104 SOUNDSINFO_FILENAME : \
105 (type) == TREE_TYPE_MUSIC_DIR ? \
106 MUSICINFO_FILENAME : "")
108 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
109 GRAPHICS_DIRECTORY : \
110 (type) == TREE_TYPE_SOUNDS_DIR ? \
112 (type) == TREE_TYPE_MUSIC_DIR ? \
113 MUSIC_DIRECTORY : "")
115 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
116 options.graphics_directory : \
117 (type) == TREE_TYPE_SOUNDS_DIR ? \
118 options.sounds_directory : \
119 (type) == TREE_TYPE_MUSIC_DIR ? \
120 options.music_directory : "")
123 /* ------------------------------------------------------------------------- */
125 /* ------------------------------------------------------------------------- */
127 static char *getLevelClassDescription(TreeInfo *ldi)
129 int position = ldi->sort_priority / 100;
131 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
132 return levelclass_desc[position];
134 return "Unknown Level Class";
137 static char *getUserLevelDir(char *level_subdir)
139 static char *userlevel_dir = NULL;
140 char *data_dir = getUserDataDir();
141 char *userlevel_subdir = LEVELS_DIRECTORY;
146 if (level_subdir != NULL)
147 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
149 userlevel_dir = getPath2(data_dir, userlevel_subdir);
151 return userlevel_dir;
154 static char *getTapeDir(char *level_subdir)
156 static char *tape_dir = NULL;
157 char *data_dir = getUserDataDir();
158 char *tape_subdir = TAPES_DIRECTORY;
163 if (level_subdir != NULL)
164 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
166 tape_dir = getPath2(data_dir, tape_subdir);
171 static char *getScoreDir(char *level_subdir)
173 static char *score_dir = NULL;
174 char *data_dir = getCommonDataDir();
175 char *score_subdir = SCORES_DIRECTORY;
180 if (level_subdir != NULL)
181 score_dir = getPath3(data_dir, score_subdir, level_subdir);
183 score_dir = getPath2(data_dir, score_subdir);
188 static char *getLevelSetupDir(char *level_subdir)
190 static char *levelsetup_dir = NULL;
191 char *data_dir = getUserDataDir();
192 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
195 free(levelsetup_dir);
197 if (level_subdir != NULL)
198 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
200 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
202 return levelsetup_dir;
205 static char *getLevelDirFromTreeInfo(TreeInfo *node)
207 static char *level_dir = NULL;
210 return options.level_directory;
215 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
216 options.level_directory), node->fullpath);
221 static char *getCurrentLevelDir()
223 return getLevelDirFromTreeInfo(leveldir_current);
226 static char *getDefaultGraphicsDir(char *graphics_subdir)
228 static char *graphics_dir = NULL;
230 if (graphics_subdir == NULL)
231 return options.graphics_directory;
236 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
241 static char *getDefaultSoundsDir(char *sounds_subdir)
243 static char *sounds_dir = NULL;
245 if (sounds_subdir == NULL)
246 return options.sounds_directory;
251 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
256 static char *getDefaultMusicDir(char *music_subdir)
258 static char *music_dir = NULL;
260 if (music_subdir == NULL)
261 return options.music_directory;
266 music_dir = getPath2(options.music_directory, music_subdir);
271 static char *getUserGraphicsDir()
273 static char *usergraphics_dir = NULL;
275 if (usergraphics_dir == NULL)
276 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
278 return usergraphics_dir;
281 static char *getUserSoundsDir()
283 static char *usersounds_dir = NULL;
285 if (usersounds_dir == NULL)
286 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
288 return usersounds_dir;
291 static char *getUserMusicDir()
293 static char *usermusic_dir = NULL;
295 if (usermusic_dir == NULL)
296 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
298 return usermusic_dir;
301 static char *getSetupArtworkDir(TreeInfo *ti)
303 static char *artwork_dir = NULL;
305 if (artwork_dir != NULL)
308 artwork_dir = getPath2(ti->basepath, ti->fullpath);
313 void setLevelArtworkDir(TreeInfo *ti)
315 char **artwork_path_ptr, *artwork_set;
316 TreeInfo *level_artwork;
318 if (ti == NULL || leveldir_current == NULL)
322 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
323 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
324 &leveldir_current->music_path);
327 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
328 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
329 leveldir_current->music_set);
331 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)) == NULL)
334 if (*artwork_path_ptr != NULL)
335 free(*artwork_path_ptr);
337 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
340 static char *getLevelArtworkDir(int type)
344 if (leveldir_current == NULL)
345 return UNDEFINED_FILENAME;
348 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
349 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
350 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
356 char *getLevelFilename(int nr)
358 static char *filename = NULL;
359 char basename[MAX_FILENAME_LEN];
361 if (filename != NULL)
364 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
365 filename = getPath2(getCurrentLevelDir(), basename);
370 char *getTapeFilename(int nr)
372 static char *filename = NULL;
373 char basename[MAX_FILENAME_LEN];
375 if (filename != NULL)
378 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
379 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
384 char *getScoreFilename(int nr)
386 static char *filename = NULL;
387 char basename[MAX_FILENAME_LEN];
389 if (filename != NULL)
392 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
393 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
398 char *getSetupFilename()
400 static char *filename = NULL;
402 if (filename != NULL)
405 filename = getPath2(getSetupDir(), SETUP_FILENAME);
410 static char *getCorrectedImageBasename(char *basename)
412 char *basename_corrected = basename;
414 #if defined(PLATFORM_MSDOS)
415 if (program.filename_prefix != NULL)
417 int prefix_len = strlen(program.filename_prefix);
419 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
420 basename_corrected = &basename[prefix_len];
422 /* if corrected filename is still longer than standard MS-DOS filename
423 size (8 characters + 1 dot + 3 characters file extension), shorten
424 filename by writing file extension after 8th basename character */
425 if (strlen(basename_corrected) > 8+1+3)
427 static char *msdos_filename = NULL;
429 if (msdos_filename != NULL)
430 free(msdos_filename);
432 msdos_filename = getStringCopy(basename_corrected);
433 strncpy(&msdos_filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
438 return basename_corrected;
441 char *getCustomImageFilename(char *basename)
443 static char *filename = NULL;
445 if (filename != NULL)
448 basename = getCorrectedImageBasename(basename);
450 if (!setup.override_level_graphics)
452 /* 1st try: look for special artwork configured in level series config */
453 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
454 if (fileExists(filename))
459 /* 2nd try: look for special artwork in current level series directory */
460 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
461 if (fileExists(filename))
467 /* 3rd try: look for special artwork in configured artwork directory */
468 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
469 if (fileExists(filename))
474 /* 4th try: look for default artwork in new default artwork directory */
475 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
476 if (fileExists(filename))
481 /* 5th try: look for default artwork in old default artwork directory */
482 filename = getPath2(options.graphics_directory, basename);
483 if (fileExists(filename))
486 return NULL; /* cannot find specified artwork file anywhere */
489 char *getCustomSoundFilename(char *basename)
491 static char *filename = NULL;
493 if (filename != NULL)
496 if (!setup.override_level_sounds)
498 /* 1st try: look for special artwork configured in level series config */
499 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
500 if (fileExists(filename))
505 /* 2nd try: look for special artwork in current level series directory */
506 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
507 if (fileExists(filename))
513 /* 3rd try: look for special artwork in configured artwork directory */
514 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
515 if (fileExists(filename))
520 /* 4th try: look for default artwork in new default artwork directory */
521 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
522 if (fileExists(filename))
527 /* 5th try: look for default artwork in old default artwork directory */
528 filename = getPath2(options.sounds_directory, basename);
529 if (fileExists(filename))
532 return NULL; /* cannot find specified artwork file anywhere */
535 char *getCustomArtworkFilename(char *basename, int type)
537 if (type == ARTWORK_TYPE_GRAPHICS)
538 return getCustomImageFilename(basename);
539 else if (type == ARTWORK_TYPE_SOUNDS)
540 return getCustomSoundFilename(basename);
542 return UNDEFINED_FILENAME;
545 char *getCustomArtworkConfigFilename(int type)
547 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
550 char *getCustomMusicDirectory(void)
552 static char *directory = NULL;
554 if (directory != NULL)
557 if (!setup.override_level_music)
559 /* 1st try: look for special artwork configured in level series config */
560 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
561 if (fileExists(directory))
566 /* 2nd try: look for special artwork in current level series directory */
567 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
568 if (fileExists(directory))
574 /* 3rd try: look for special artwork in configured artwork directory */
575 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
576 if (fileExists(directory))
581 /* 4th try: look for default artwork in new default artwork directory */
582 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
583 if (fileExists(directory))
588 /* 5th try: look for default artwork in old default artwork directory */
589 directory = getStringCopy(options.music_directory);
590 if (fileExists(directory))
593 return NULL; /* cannot find specified artwork file anywhere */
596 void InitTapeDirectory(char *level_subdir)
598 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
599 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
600 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
603 void InitScoreDirectory(char *level_subdir)
605 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
606 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
607 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
610 static void SaveUserLevelInfo();
612 void InitUserLevelDirectory(char *level_subdir)
614 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
616 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
617 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
618 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
624 void InitLevelSetupDirectory(char *level_subdir)
626 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
627 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
628 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
632 /* ------------------------------------------------------------------------- */
633 /* some functions to handle lists of level directories */
634 /* ------------------------------------------------------------------------- */
636 TreeInfo *newTreeInfo()
638 return checked_calloc(sizeof(TreeInfo));
641 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
643 node_new->next = *node_first;
644 *node_first = node_new;
647 int numTreeInfo(TreeInfo *node)
660 boolean validLevelSeries(TreeInfo *node)
662 return (node != NULL && !node->node_group && !node->parent_link);
665 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
670 if (node->node_group) /* enter level group (step down into tree) */
671 return getFirstValidTreeInfoEntry(node->node_group);
672 else if (node->parent_link) /* skip start entry of level group */
674 if (node->next) /* get first real level series entry */
675 return getFirstValidTreeInfoEntry(node->next);
676 else /* leave empty level group and go on */
677 return getFirstValidTreeInfoEntry(node->node_parent->next);
679 else /* this seems to be a regular level series */
683 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
688 if (node->node_parent == NULL) /* top level group */
689 return *node->node_top;
690 else /* sub level group */
691 return node->node_parent->node_group;
694 int numTreeInfoInGroup(TreeInfo *node)
696 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
699 int posTreeInfo(TreeInfo *node)
701 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
706 if (node_cmp == node)
710 node_cmp = node_cmp->next;
716 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
718 TreeInfo *node_default = node;
733 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
735 if (identifier == NULL)
740 if (node->node_group)
742 TreeInfo *node_group;
744 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
749 else if (!node->parent_link)
751 if (strcmp(identifier, node->identifier) == 0)
761 void dumpTreeInfo(TreeInfo *node, int depth)
765 printf("Dumping TreeInfo:\n");
769 for (i=0; i<(depth + 1) * 3; i++)
772 printf("filename == '%s' (%s) [%s] (%d)\n",
773 node->filename, node->name, node->identifier, node->sort_priority);
775 if (node->node_group != NULL)
776 dumpTreeInfo(node->node_group, depth + 1);
782 void sortTreeInfo(TreeInfo **node_first,
783 int (*compare_function)(const void *, const void *))
785 int num_nodes = numTreeInfo(*node_first);
786 TreeInfo **sort_array;
787 TreeInfo *node = *node_first;
793 /* allocate array for sorting structure pointers */
794 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
796 /* writing structure pointers to sorting array */
797 while (i < num_nodes && node) /* double boundary check... */
799 sort_array[i] = node;
805 /* sorting the structure pointers in the sorting array */
806 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
809 /* update the linkage of list elements with the sorted node array */
810 for (i=0; i<num_nodes - 1; i++)
811 sort_array[i]->next = sort_array[i + 1];
812 sort_array[num_nodes - 1]->next = NULL;
814 /* update the linkage of the main list anchor pointer */
815 *node_first = sort_array[0];
819 /* now recursively sort the level group structures */
823 if (node->node_group != NULL)
824 sortTreeInfo(&node->node_group, compare_function);
831 /* ========================================================================= */
832 /* some stuff from "files.c" */
833 /* ========================================================================= */
835 #if defined(PLATFORM_WIN32)
837 #define S_IRGRP S_IRUSR
840 #define S_IROTH S_IRUSR
843 #define S_IWGRP S_IWUSR
846 #define S_IWOTH S_IWUSR
849 #define S_IXGRP S_IXUSR
852 #define S_IXOTH S_IXUSR
855 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
860 #endif /* PLATFORM_WIN32 */
862 /* file permissions for newly written files */
863 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
864 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
865 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
867 #define MODE_W_PRIVATE (S_IWUSR)
868 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
869 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
871 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
872 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
874 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
875 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
877 char *getUserDataDir(void)
879 static char *userdata_dir = NULL;
881 if (userdata_dir == NULL)
882 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
887 char *getCommonDataDir(void)
889 static char *common_data_dir = NULL;
891 #if defined(PLATFORM_WIN32)
892 if (common_data_dir == NULL)
894 char *dir = checked_malloc(MAX_PATH + 1);
896 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
897 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
898 common_data_dir = getPath2(dir, program.userdata_directory);
900 common_data_dir = options.rw_base_directory;
903 if (common_data_dir == NULL)
904 common_data_dir = options.rw_base_directory;
907 return common_data_dir;
912 return getUserDataDir();
915 static mode_t posix_umask(mode_t mask)
917 #if defined(PLATFORM_UNIX)
924 static int posix_mkdir(const char *pathname, mode_t mode)
926 #if defined(PLATFORM_WIN32)
927 return mkdir(pathname);
929 return mkdir(pathname, mode);
933 void createDirectory(char *dir, char *text, int permission_class)
935 /* leave "other" permissions in umask untouched, but ensure group parts
936 of USERDATA_DIR_MODE are not masked */
937 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
938 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
939 mode_t normal_umask = posix_umask(0);
940 mode_t group_umask = ~(dir_mode & S_IRWXG);
941 posix_umask(normal_umask & group_umask);
943 if (access(dir, F_OK) != 0)
944 if (posix_mkdir(dir, dir_mode) != 0)
945 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
947 posix_umask(normal_umask); /* reset normal umask */
950 void InitUserDataDirectory()
952 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
955 void SetFilePermissions(char *filename, int permission_class)
957 chmod(filename, (permission_class == PERMS_PRIVATE ?
958 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
961 char *getCookie(char *file_type)
963 static char cookie[MAX_COOKIE_LEN + 1];
965 if (strlen(program.cookie_prefix) + 1 +
966 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
967 return "[COOKIE ERROR]"; /* should never happen */
969 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
970 program.cookie_prefix, file_type,
971 program.version_major, program.version_minor);
976 int getFileVersionFromCookieString(const char *cookie)
978 const char *ptr_cookie1, *ptr_cookie2;
979 const char *pattern1 = "_FILE_VERSION_";
980 const char *pattern2 = "?.?";
981 const int len_cookie = strlen(cookie);
982 const int len_pattern1 = strlen(pattern1);
983 const int len_pattern2 = strlen(pattern2);
984 const int len_pattern = len_pattern1 + len_pattern2;
985 int version_major, version_minor;
987 if (len_cookie <= len_pattern)
990 ptr_cookie1 = &cookie[len_cookie - len_pattern];
991 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
993 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
996 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
997 ptr_cookie2[1] != '.' ||
998 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1001 version_major = ptr_cookie2[0] - '0';
1002 version_minor = ptr_cookie2[2] - '0';
1004 return VERSION_IDENT(version_major, version_minor, 0);
1007 boolean checkCookieString(const char *cookie, const char *template)
1009 const char *pattern = "_FILE_VERSION_?.?";
1010 const int len_cookie = strlen(cookie);
1011 const int len_template = strlen(template);
1012 const int len_pattern = strlen(pattern);
1014 if (len_cookie != len_template)
1017 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1023 /* ------------------------------------------------------------------------- */
1024 /* setup file list and hash handling functions */
1025 /* ------------------------------------------------------------------------- */
1027 char *getFormattedSetupEntry(char *token, char *value)
1030 static char entry[MAX_LINE_LEN];
1032 /* start with the token and some spaces to format output line */
1033 sprintf(entry, "%s:", token);
1034 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1037 /* continue with the token's value */
1038 strcat(entry, value);
1043 void freeSetupFileList(SetupFileList *list)
1053 freeSetupFileList(list->next);
1057 SetupFileList *newSetupFileList(char *token, char *value)
1059 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1061 new->token = getStringCopy(token);
1062 new->value = getStringCopy(value);
1069 char *getTokenValue(SetupFileList *list, char *token)
1074 if (strcmp(list->token, token) == 0)
1077 return getTokenValue(list->next, token);
1080 void setTokenValue(SetupFileList *list, char *token, char *value)
1085 if (strcmp(list->token, token) == 0)
1090 list->value = getStringCopy(value);
1092 else if (list->next == NULL)
1093 list->next = newSetupFileList(token, value);
1095 setTokenValue(list->next, token, value);
1099 static void printSetupFileList(SetupFileList *list)
1104 printf("token: '%s'\n", list->token);
1105 printf("value: '%s'\n", list->value);
1107 printSetupFileList(list->next);
1112 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1113 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1114 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1115 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1117 #define insert_hash_entry hashtable_insert
1118 #define search_hash_entry hashtable_search
1119 #define change_hash_entry hashtable_change
1120 #define remove_hash_entry hashtable_remove
1123 static unsigned int get_hash_from_key(void *key)
1128 this algorithm (k=33) was first reported by dan bernstein many years ago in
1129 comp.lang.c. another version of this algorithm (now favored by bernstein)
1130 uses xor: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1131 it works better than many other constants, prime or not) has never been
1132 adequately explained.
1135 char *str = (char *)key;
1136 unsigned int hash = 5381;
1139 while ((c = *str++))
1140 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1145 static int keys_are_equal(void *key1, void *key2)
1147 return (strcmp((char *)key1, (char *)key2) == 0);
1150 void freeSetupFileHash(SetupFileHash *hash)
1155 hashtable_destroy(hash, 1); /* 1 == also free values */
1159 SetupFileHash *newSetupFileHash()
1161 SetupFileHash *new_hash =
1162 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1167 char *getHashEntry(SetupFileHash *hash, char *token)
1172 return search_hash_entry(hash, token);
1175 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1182 value_copy = getStringCopy(value);
1184 /* change value; if it does not exist, insert it as new */
1185 if (!change_hash_entry(hash, token, value_copy))
1186 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1187 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1192 static void printSetupFileHash(SetupFileHash *hash)
1198 /* iterator constructor only returns valid iterator for non-empty hash */
1199 if (hash != NULL && hashtable_count(hash) > 0)
1201 struct hashtable_itr *itr = hashtable_iterator(hash);
1205 printf("token: '%s'\n", (char *)hashtable_iterator_key(itr));
1206 printf("value: '%s'\n", (char *)hashtable_iterator_value(itr));
1208 while (hashtable_iterator_advance(itr));
1214 BEGIN_HASH_ITERATION(hash, itr)
1216 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1217 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1219 END_HASH_ITERATION(hash, itr)
1224 SetupFileHash *loadSetupFileHash(char *filename)
1227 char line[MAX_LINE_LEN];
1228 char *token, *value, *line_ptr;
1229 SetupFileHash *setup_file_hash = newSetupFileHash();
1232 if (!(file = fopen(filename, MODE_READ)))
1234 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1240 /* read next line of input file */
1241 if (!fgets(line, MAX_LINE_LEN, file))
1244 /* cut trailing comment or whitespace from input line */
1245 for (line_ptr = line; *line_ptr; line_ptr++)
1247 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1254 /* cut trailing whitespaces from input line */
1255 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1256 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1259 /* ignore empty lines */
1263 line_len = strlen(line);
1265 /* cut leading whitespaces from token */
1266 for (token = line; *token; token++)
1267 if (*token != ' ' && *token != '\t')
1270 /* find end of token */
1271 for (line_ptr = token; *line_ptr; line_ptr++)
1273 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1280 if (line_ptr < line + line_len)
1281 value = line_ptr + 1;
1285 /* cut leading whitespaces from value */
1286 for (; *value; value++)
1287 if (*value != ' ' && *value != '\t')
1290 if (*token && *value)
1291 setHashEntry(setup_file_hash, token, value);
1296 if (hashtable_count(setup_file_hash) == 0)
1297 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1299 return setup_file_hash;
1302 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1305 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1308 Error(ERR_WARN, "configuration file has no file identifier");
1309 else if (!checkCookieString(value, identifier))
1310 Error(ERR_WARN, "configuration file has wrong file identifier");
1314 /* ========================================================================= */
1315 /* setup file stuff */
1316 /* ========================================================================= */
1318 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1319 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1320 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1322 /* level directory info */
1323 #define LEVELINFO_TOKEN_IDENTIFIER 0
1324 #define LEVELINFO_TOKEN_NAME 1
1325 #define LEVELINFO_TOKEN_NAME_SORTING 2
1326 #define LEVELINFO_TOKEN_AUTHOR 3
1327 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1328 #define LEVELINFO_TOKEN_LEVELS 5
1329 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1330 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1331 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1332 #define LEVELINFO_TOKEN_READONLY 9
1333 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1334 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1335 #define LEVELINFO_TOKEN_MUSIC_SET 12
1337 #define NUM_LEVELINFO_TOKENS 13
1339 static LevelDirTree ldi;
1341 static struct TokenInfo levelinfo_tokens[] =
1343 /* level directory info */
1344 { TYPE_STRING, &ldi.identifier, "identifier" },
1345 { TYPE_STRING, &ldi.name, "name" },
1346 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1347 { TYPE_STRING, &ldi.author, "author" },
1348 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1349 { TYPE_INTEGER, &ldi.levels, "levels" },
1350 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1351 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1352 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1353 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1354 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1355 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1356 { TYPE_STRING, &ldi.music_set, "music_set" }
1359 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1363 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1364 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1365 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1366 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1369 ldi->node_parent = NULL;
1370 ldi->node_group = NULL;
1374 ldi->cl_cursor = -1;
1376 ldi->filename = NULL;
1377 ldi->fullpath = NULL;
1378 ldi->basepath = NULL;
1379 ldi->identifier = NULL;
1380 ldi->name = getStringCopy(ANONYMOUS_NAME);
1381 ldi->name_sorting = NULL;
1382 ldi->author = getStringCopy(ANONYMOUS_NAME);
1384 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1385 ldi->parent_link = FALSE;
1386 ldi->user_defined = FALSE;
1388 ldi->class_desc = NULL;
1390 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1392 ldi->imported_from = NULL;
1393 ldi->graphics_set = NULL;
1394 ldi->sounds_set = NULL;
1395 ldi->music_set = NULL;
1396 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1397 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1398 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1400 ldi->first_level = 0;
1401 ldi->last_level = 0;
1402 ldi->level_group = FALSE;
1403 ldi->handicap_level = 0;
1404 ldi->readonly = TRUE;
1408 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1412 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1414 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1418 /* first copy all values from the parent structure ... */
1421 /* ... then set all fields to default that cannot be inherited from parent.
1422 This is especially important for all those fields that can be set from
1423 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1424 calls 'free()' for all already set token values which requires that no
1425 other structure's pointer may point to them!
1428 ldi->filename = NULL;
1429 ldi->fullpath = NULL;
1430 ldi->basepath = NULL;
1431 ldi->identifier = NULL;
1432 ldi->name = getStringCopy(ANONYMOUS_NAME);
1433 ldi->name_sorting = NULL;
1434 ldi->author = getStringCopy(parent->author);
1435 ldi->imported_from = getStringCopy(parent->imported_from);
1437 ldi->level_group = FALSE;
1438 ldi->parent_link = FALSE;
1440 ldi->node_top = parent->node_top;
1441 ldi->node_parent = parent;
1442 ldi->node_group = NULL;
1446 void setSetupInfo(struct TokenInfo *token_info,
1447 int token_nr, char *token_value)
1449 int token_type = token_info[token_nr].type;
1450 void *setup_value = token_info[token_nr].value;
1452 if (token_value == NULL)
1455 /* set setup field to corresponding token value */
1460 *(boolean *)setup_value = get_boolean_from_string(token_value);
1464 *(Key *)setup_value = getKeyFromKeyName(token_value);
1468 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1472 *(int *)setup_value = get_integer_from_string(token_value);
1476 if (*(char **)setup_value != NULL)
1477 free(*(char **)setup_value);
1478 *(char **)setup_value = getStringCopy(token_value);
1486 static int compareTreeInfoEntries(const void *object1, const void *object2)
1488 const TreeInfo *entry1 = *((TreeInfo **)object1);
1489 const TreeInfo *entry2 = *((TreeInfo **)object2);
1490 int class_sorting1, class_sorting2;
1493 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1495 class_sorting1 = LEVELSORTING(entry1);
1496 class_sorting2 = LEVELSORTING(entry2);
1500 class_sorting1 = ARTWORKSORTING(entry1);
1501 class_sorting2 = ARTWORKSORTING(entry2);
1504 if (entry1->parent_link || entry2->parent_link)
1505 compare_result = (entry1->parent_link ? -1 : +1);
1506 else if (entry1->sort_priority == entry2->sort_priority)
1508 char *name1 = getStringToLower(entry1->name_sorting);
1509 char *name2 = getStringToLower(entry2->name_sorting);
1511 compare_result = strcmp(name1, name2);
1516 else if (class_sorting1 == class_sorting2)
1517 compare_result = entry1->sort_priority - entry2->sort_priority;
1519 compare_result = class_sorting1 - class_sorting2;
1521 return compare_result;
1524 static void createParentTreeInfoNode(TreeInfo *node_parent)
1528 if (node_parent == NULL)
1531 ti_new = newTreeInfo();
1532 setTreeInfoToDefaults(ti_new, node_parent->type);
1534 ti_new->node_parent = node_parent;
1535 ti_new->parent_link = TRUE;
1537 ti_new->identifier = getStringCopy(node_parent->identifier);
1538 ti_new->name = ".. (parent directory)";
1539 ti_new->name_sorting = getStringCopy(ti_new->name);
1541 ti_new->filename = "..";
1542 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1544 ti_new->sort_priority = node_parent->sort_priority;
1545 ti_new->class_desc = getLevelClassDescription(ti_new);
1547 pushTreeInfo(&node_parent->node_group, ti_new);
1550 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1551 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1553 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1554 TreeInfo *node_parent,
1555 char *level_directory,
1556 char *directory_name)
1558 char *directory_path = getPath2(level_directory, directory_name);
1559 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1560 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1561 LevelDirTree *leveldir_new = NULL;
1564 if (setup_file_hash == NULL)
1566 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1568 free(directory_path);
1574 leveldir_new = newTreeInfo();
1577 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1579 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1581 leveldir_new->filename = getStringCopy(directory_name);
1583 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1585 /* set all structure fields according to the token/value pairs */
1586 ldi = *leveldir_new;
1587 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1588 setSetupInfo(levelinfo_tokens, i,
1589 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1590 *leveldir_new = ldi;
1592 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1594 free(leveldir_new->name);
1595 leveldir_new->name = getStringCopy(leveldir_new->filename);
1598 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1600 if (leveldir_new->identifier == NULL)
1601 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1603 if (leveldir_new->name_sorting == NULL)
1604 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1606 if (node_parent == NULL) /* top level group */
1608 leveldir_new->basepath = level_directory;
1609 leveldir_new->fullpath = leveldir_new->filename;
1611 else /* sub level group */
1613 leveldir_new->basepath = node_parent->basepath;
1614 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1617 if (leveldir_new->levels < 1)
1618 leveldir_new->levels = 1;
1620 leveldir_new->last_level =
1621 leveldir_new->first_level + leveldir_new->levels - 1;
1623 leveldir_new->user_defined =
1624 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1626 leveldir_new->color = LEVELCOLOR(leveldir_new);
1627 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1629 leveldir_new->handicap_level = /* set handicap to default value */
1630 (leveldir_new->user_defined ?
1631 leveldir_new->last_level :
1632 leveldir_new->first_level);
1634 pushTreeInfo(node_first, leveldir_new);
1636 freeSetupFileHash(setup_file_hash);
1638 if (leveldir_new->level_group)
1640 /* create node to link back to current level directory */
1641 createParentTreeInfoNode(leveldir_new);
1643 /* step into sub-directory and look for more level series */
1644 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1645 leveldir_new, directory_path);
1648 free(directory_path);
1654 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1655 TreeInfo *node_parent,
1656 char *level_directory)
1659 struct dirent *dir_entry;
1660 boolean valid_entry_found = FALSE;
1662 if ((dir = opendir(level_directory)) == NULL)
1664 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1668 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1670 struct stat file_status;
1671 char *directory_name = dir_entry->d_name;
1672 char *directory_path = getPath2(level_directory, directory_name);
1674 /* skip entries for current and parent directory */
1675 if (strcmp(directory_name, ".") == 0 ||
1676 strcmp(directory_name, "..") == 0)
1678 free(directory_path);
1682 /* find out if directory entry is itself a directory */
1683 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1684 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1686 free(directory_path);
1690 free(directory_path);
1692 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1693 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1694 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1697 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1704 if (!valid_entry_found)
1706 /* check if this directory directly contains a file "levelinfo.conf" */
1707 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1708 level_directory, ".");
1711 if (!valid_entry_found)
1712 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1716 void LoadLevelInfo()
1718 InitUserLevelDirectory(getLoginName());
1720 DrawInitText("Loading level series:", 120, FC_GREEN);
1722 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1723 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1725 /* before sorting, the first entries will be from the user directory */
1726 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1728 if (leveldir_first == NULL)
1729 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1731 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1734 dumpTreeInfo(leveldir_first, 0);
1738 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1739 TreeInfo *node_parent,
1740 char *base_directory,
1741 char *directory_name, int type)
1743 char *directory_path = getPath2(base_directory, directory_name);
1744 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1745 SetupFileHash *setup_file_hash = NULL;
1746 TreeInfo *artwork_new = NULL;
1749 if (access(filename, F_OK) == 0) /* file exists */
1750 setup_file_hash = loadSetupFileHash(filename);
1752 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1755 struct dirent *dir_entry;
1756 boolean valid_file_found = FALSE;
1758 if ((dir = opendir(directory_path)) != NULL)
1760 while ((dir_entry = readdir(dir)) != NULL)
1762 char *entry_name = dir_entry->d_name;
1764 if (FileIsArtworkType(entry_name, type))
1766 valid_file_found = TRUE;
1774 if (!valid_file_found)
1776 if (strcmp(directory_name, ".") != 0)
1777 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1779 free(directory_path);
1786 artwork_new = newTreeInfo();
1789 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1791 setTreeInfoToDefaults(artwork_new, type);
1793 artwork_new->filename = getStringCopy(directory_name);
1795 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1798 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1801 /* set all structure fields according to the token/value pairs */
1803 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1804 setSetupInfo(levelinfo_tokens, i,
1805 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1808 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1810 free(artwork_new->name);
1811 artwork_new->name = getStringCopy(artwork_new->filename);
1815 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1818 if (artwork_new->identifier == NULL)
1819 artwork_new->identifier = getStringCopy(artwork_new->filename);
1821 if (artwork_new->name_sorting == NULL)
1822 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1825 if (node_parent == NULL) /* top level group */
1827 artwork_new->basepath = getStringCopy(base_directory);
1828 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1830 else /* sub level group */
1832 artwork_new->basepath = getStringCopy(node_parent->basepath);
1833 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1836 artwork_new->user_defined =
1837 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1839 /* (may use ".sort_priority" from "setup_file_hash" above) */
1840 artwork_new->color = ARTWORKCOLOR(artwork_new);
1841 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1843 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1845 if (artwork_new->name != NULL)
1846 free(artwork_new->name);
1848 if (strcmp(artwork_new->filename, ".") == 0)
1850 if (artwork_new->user_defined)
1852 artwork_new->identifier = getStringCopy("private");
1853 artwork_new->sort_priority = ARTWORKCLASS_USER;
1857 artwork_new->identifier = getStringCopy("classic");
1858 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1861 /* set to new values after changing ".sort_priority" */
1862 artwork_new->color = ARTWORKCOLOR(artwork_new);
1863 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1867 artwork_new->identifier = getStringCopy(artwork_new->filename);
1870 artwork_new->name = getStringCopy(artwork_new->identifier);
1871 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1874 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1876 pushTreeInfo(node_first, artwork_new);
1878 freeSetupFileHash(setup_file_hash);
1880 free(directory_path);
1886 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1887 TreeInfo *node_parent,
1888 char *base_directory, int type)
1891 struct dirent *dir_entry;
1892 boolean valid_entry_found = FALSE;
1894 if ((dir = opendir(base_directory)) == NULL)
1896 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1897 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1901 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1903 struct stat file_status;
1904 char *directory_name = dir_entry->d_name;
1905 char *directory_path = getPath2(base_directory, directory_name);
1907 /* skip entries for current and parent directory */
1908 if (strcmp(directory_name, ".") == 0 ||
1909 strcmp(directory_name, "..") == 0)
1911 free(directory_path);
1915 /* find out if directory entry is itself a directory */
1916 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1917 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1919 free(directory_path);
1923 free(directory_path);
1925 /* check if this directory contains artwork with or without config file */
1926 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1928 directory_name, type);
1933 /* check if this directory directly contains artwork itself */
1934 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1935 base_directory, ".",
1937 if (!valid_entry_found)
1938 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1942 static TreeInfo *getDummyArtworkInfo(int type)
1944 /* this is only needed when there is completely no artwork available */
1945 TreeInfo *artwork_new = newTreeInfo();
1947 setTreeInfoToDefaults(artwork_new, type);
1949 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1950 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1951 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1953 if (artwork_new->name != NULL)
1954 free(artwork_new->name);
1956 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1957 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1958 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1963 void LoadArtworkInfo()
1965 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1967 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1968 options.graphics_directory,
1969 TREE_TYPE_GRAPHICS_DIR);
1970 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1971 getUserGraphicsDir(),
1972 TREE_TYPE_GRAPHICS_DIR);
1974 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1975 options.sounds_directory,
1976 TREE_TYPE_SOUNDS_DIR);
1977 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1979 TREE_TYPE_SOUNDS_DIR);
1981 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1982 options.music_directory,
1983 TREE_TYPE_MUSIC_DIR);
1984 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1986 TREE_TYPE_MUSIC_DIR);
1988 if (artwork.gfx_first == NULL)
1989 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1990 if (artwork.snd_first == NULL)
1991 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1992 if (artwork.mus_first == NULL)
1993 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1995 /* before sorting, the first entries will be from the user directory */
1996 artwork.gfx_current =
1997 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1998 if (artwork.gfx_current == NULL)
1999 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2001 artwork.snd_current =
2002 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2003 if (artwork.snd_current == NULL)
2004 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2006 artwork.mus_current =
2007 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2008 if (artwork.mus_current == NULL)
2009 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2011 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2012 artwork.snd_current_identifier = artwork.snd_current->identifier;
2013 artwork.mus_current_identifier = artwork.mus_current->identifier;
2016 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2017 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2018 printf("music set == %s\n\n", artwork.mus_current_identifier);
2021 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2022 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2023 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2026 dumpTreeInfo(artwork.gfx_first, 0);
2027 dumpTreeInfo(artwork.snd_first, 0);
2028 dumpTreeInfo(artwork.mus_first, 0);
2032 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2033 LevelDirTree *level_node)
2035 /* recursively check all level directories for artwork sub-directories */
2039 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2040 ARTWORK_DIRECTORY((*artwork_node)->type));
2043 if (!level_node->parent_link)
2044 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2045 level_node->filename, level_node->name);
2048 if (!level_node->parent_link)
2050 TreeInfo *topnode_last = *artwork_node;
2052 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2053 (*artwork_node)->type);
2055 if (topnode_last != *artwork_node)
2057 free((*artwork_node)->identifier);
2058 free((*artwork_node)->name);
2059 free((*artwork_node)->name_sorting);
2061 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2062 (*artwork_node)->name = getStringCopy(level_node->name);
2063 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2065 (*artwork_node)->sort_priority = level_node->sort_priority;
2066 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2072 if (level_node->node_group != NULL)
2073 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2075 level_node = level_node->next;
2079 void LoadLevelArtworkInfo()
2081 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2083 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2084 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2085 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2087 /* needed for reloading level artwork not known at ealier stage */
2088 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2090 artwork.gfx_current =
2091 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2092 if (artwork.gfx_current == NULL)
2093 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2096 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2098 artwork.snd_current =
2099 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2100 if (artwork.snd_current == NULL)
2101 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2104 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2106 artwork.mus_current =
2107 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2108 if (artwork.mus_current == NULL)
2109 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2112 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2113 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2114 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2117 dumpTreeInfo(artwork.gfx_first, 0);
2118 dumpTreeInfo(artwork.snd_first, 0);
2119 dumpTreeInfo(artwork.mus_first, 0);
2123 static void SaveUserLevelInfo()
2129 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2131 if (!(file = fopen(filename, MODE_WRITE)))
2133 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2138 /* always start with reliable default values */
2139 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2141 ldi.name = getStringCopy(getLoginName());
2142 ldi.author = getStringCopy(getRealName());
2144 ldi.first_level = 1;
2145 ldi.sort_priority = LEVELCLASS_USER_START;
2146 ldi.readonly = FALSE;
2147 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2148 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2149 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2151 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2152 getCookie("LEVELINFO")));
2154 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2155 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2156 i != LEVELINFO_TOKEN_NAME_SORTING &&
2157 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2158 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2163 SetFilePermissions(filename, PERMS_PRIVATE);
2166 char *getSetupValue(int type, void *value)
2168 static char value_string[MAX_LINE_LEN];
2176 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2180 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2184 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2188 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2192 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2196 sprintf(value_string, "%d", *(int *)value);
2200 strcpy(value_string, *(char **)value);
2204 value_string[0] = '\0';
2208 return value_string;
2211 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2215 static char token_string[MAX_LINE_LEN];
2216 int token_type = token_info[token_nr].type;
2217 void *setup_value = token_info[token_nr].value;
2218 char *token_text = token_info[token_nr].text;
2219 char *value_string = getSetupValue(token_type, setup_value);
2221 /* build complete token string */
2222 sprintf(token_string, "%s%s", prefix, token_text);
2224 /* build setup entry line */
2225 line = getFormattedSetupEntry(token_string, value_string);
2227 if (token_type == TYPE_KEY_X11)
2229 Key key = *(Key *)setup_value;
2230 char *keyname = getKeyNameFromKey(key);
2232 /* add comment, if useful */
2233 if (strcmp(keyname, "(undefined)") != 0 &&
2234 strcmp(keyname, "(unknown)") != 0)
2236 /* add at least one whitespace */
2238 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2242 strcat(line, keyname);
2249 void LoadLevelSetup_LastSeries()
2252 SetupFileHash *level_setup_hash = NULL;
2254 /* always start with reliable default values */
2255 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2257 /* ----------------------------------------------------------------------- */
2258 /* ~/.<program>/levelsetup.conf */
2259 /* ----------------------------------------------------------------------- */
2261 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2263 if ((level_setup_hash = loadSetupFileHash(filename)))
2265 char *last_level_series =
2266 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2268 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2270 if (leveldir_current == NULL)
2271 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2273 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2275 freeSetupFileHash(level_setup_hash);
2278 Error(ERR_WARN, "using default setup values");
2283 void SaveLevelSetup_LastSeries()
2286 char *level_subdir = leveldir_current->filename;
2289 /* ----------------------------------------------------------------------- */
2290 /* ~/.<program>/levelsetup.conf */
2291 /* ----------------------------------------------------------------------- */
2293 InitUserDataDirectory();
2295 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2297 if (!(file = fopen(filename, MODE_WRITE)))
2299 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2304 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2305 getCookie("LEVELSETUP")));
2306 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2312 SetFilePermissions(filename, PERMS_PRIVATE);
2315 static void checkSeriesInfo()
2317 static char *level_directory = NULL;
2319 struct dirent *dir_entry;
2321 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2323 level_directory = getPath2((leveldir_current->user_defined ?
2324 getUserLevelDir(NULL) :
2325 options.level_directory),
2326 leveldir_current->fullpath);
2328 if ((dir = opendir(level_directory)) == NULL)
2330 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2334 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2336 if (strlen(dir_entry->d_name) > 4 &&
2337 dir_entry->d_name[3] == '.' &&
2338 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2340 char levelnum_str[4];
2343 strncpy(levelnum_str, dir_entry->d_name, 3);
2344 levelnum_str[3] = '\0';
2346 levelnum_value = atoi(levelnum_str);
2348 if (levelnum_value < leveldir_current->first_level)
2350 Error(ERR_WARN, "additional level %d found", levelnum_value);
2351 leveldir_current->first_level = levelnum_value;
2353 else if (levelnum_value > leveldir_current->last_level)
2355 Error(ERR_WARN, "additional level %d found", levelnum_value);
2356 leveldir_current->last_level = levelnum_value;
2364 void LoadLevelSetup_SeriesInfo()
2367 SetupFileHash *level_setup_hash = NULL;
2368 char *level_subdir = leveldir_current->filename;
2370 /* always start with reliable default values */
2371 level_nr = leveldir_current->first_level;
2373 checkSeriesInfo(leveldir_current);
2375 /* ----------------------------------------------------------------------- */
2376 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2377 /* ----------------------------------------------------------------------- */
2379 level_subdir = leveldir_current->filename;
2381 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2383 if ((level_setup_hash = loadSetupFileHash(filename)))
2387 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2391 level_nr = atoi(token_value);
2393 if (level_nr < leveldir_current->first_level)
2394 level_nr = leveldir_current->first_level;
2395 if (level_nr > leveldir_current->last_level)
2396 level_nr = leveldir_current->last_level;
2399 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2403 int level_nr = atoi(token_value);
2405 if (level_nr < leveldir_current->first_level)
2406 level_nr = leveldir_current->first_level;
2407 if (level_nr > leveldir_current->last_level + 1)
2408 level_nr = leveldir_current->last_level;
2410 if (leveldir_current->user_defined)
2411 level_nr = leveldir_current->last_level;
2413 leveldir_current->handicap_level = level_nr;
2416 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2418 freeSetupFileHash(level_setup_hash);
2421 Error(ERR_WARN, "using default setup values");
2426 void SaveLevelSetup_SeriesInfo()
2429 char *level_subdir = leveldir_current->filename;
2430 char *level_nr_str = int2str(level_nr, 0);
2431 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2434 /* ----------------------------------------------------------------------- */
2435 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2436 /* ----------------------------------------------------------------------- */
2438 InitLevelSetupDirectory(level_subdir);
2440 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2442 if (!(file = fopen(filename, MODE_WRITE)))
2444 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2449 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2450 getCookie("LEVELSETUP")));
2451 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2453 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2454 handicap_level_str));
2459 SetFilePermissions(filename, PERMS_PRIVATE);