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 *getListEntry(SetupFileList *list, char *token)
1074 if (strcmp(list->token, token) == 0)
1077 return getListEntry(list->next, token);
1080 void setListEntry(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 setListEntry(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 static void *loadSetupFileData(char *filename, boolean use_hash)
1227 char line[MAX_LINE_LEN];
1228 char *token, *value, *line_ptr;
1229 void *setup_file_data;
1233 setup_file_data = newSetupFileHash();
1235 setup_file_data = newSetupFileList("", "");
1237 if (!(file = fopen(filename, MODE_READ)))
1239 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1245 /* read next line of input file */
1246 if (!fgets(line, MAX_LINE_LEN, file))
1249 /* cut trailing comment or whitespace from input line */
1250 for (line_ptr = line; *line_ptr; line_ptr++)
1252 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1259 /* cut trailing whitespaces from input line */
1260 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1261 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1264 /* ignore empty lines */
1268 line_len = strlen(line);
1270 /* cut leading whitespaces from token */
1271 for (token = line; *token; token++)
1272 if (*token != ' ' && *token != '\t')
1275 /* find end of token */
1276 for (line_ptr = token; *line_ptr; line_ptr++)
1278 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1285 if (line_ptr < line + line_len)
1286 value = line_ptr + 1;
1290 /* cut leading whitespaces from value */
1291 for (; *value; value++)
1292 if (*value != ' ' && *value != '\t')
1295 if (*token && *value)
1298 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1300 setListEntry((SetupFileList *)setup_file_data, token, value);
1308 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1309 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1313 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1314 SetupFileList *first_valid_list_entry = setup_file_list->next;
1316 /* free empty list header */
1317 setup_file_list->next = NULL;
1318 freeSetupFileList(setup_file_list);
1319 setup_file_data = first_valid_list_entry;
1321 if (first_valid_list_entry == NULL)
1322 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1325 return setup_file_data;
1328 SetupFileList *loadSetupFileList(char *filename)
1330 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1333 SetupFileHash *loadSetupFileHash(char *filename)
1335 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1338 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1341 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1344 Error(ERR_WARN, "configuration file has no file identifier");
1345 else if (!checkCookieString(value, identifier))
1346 Error(ERR_WARN, "configuration file has wrong file identifier");
1350 /* ========================================================================= */
1351 /* setup file stuff */
1352 /* ========================================================================= */
1354 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1355 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1356 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1358 /* level directory info */
1359 #define LEVELINFO_TOKEN_IDENTIFIER 0
1360 #define LEVELINFO_TOKEN_NAME 1
1361 #define LEVELINFO_TOKEN_NAME_SORTING 2
1362 #define LEVELINFO_TOKEN_AUTHOR 3
1363 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1364 #define LEVELINFO_TOKEN_LEVELS 5
1365 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1366 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1367 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1368 #define LEVELINFO_TOKEN_READONLY 9
1369 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1370 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1371 #define LEVELINFO_TOKEN_MUSIC_SET 12
1373 #define NUM_LEVELINFO_TOKENS 13
1375 static LevelDirTree ldi;
1377 static struct TokenInfo levelinfo_tokens[] =
1379 /* level directory info */
1380 { TYPE_STRING, &ldi.identifier, "identifier" },
1381 { TYPE_STRING, &ldi.name, "name" },
1382 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1383 { TYPE_STRING, &ldi.author, "author" },
1384 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1385 { TYPE_INTEGER, &ldi.levels, "levels" },
1386 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1387 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1388 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1389 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1390 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1391 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1392 { TYPE_STRING, &ldi.music_set, "music_set" }
1395 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1399 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1400 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1401 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1402 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1405 ldi->node_parent = NULL;
1406 ldi->node_group = NULL;
1410 ldi->cl_cursor = -1;
1412 ldi->filename = NULL;
1413 ldi->fullpath = NULL;
1414 ldi->basepath = NULL;
1415 ldi->identifier = NULL;
1416 ldi->name = getStringCopy(ANONYMOUS_NAME);
1417 ldi->name_sorting = NULL;
1418 ldi->author = getStringCopy(ANONYMOUS_NAME);
1420 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1421 ldi->parent_link = FALSE;
1422 ldi->user_defined = FALSE;
1424 ldi->class_desc = NULL;
1426 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1428 ldi->imported_from = NULL;
1429 ldi->graphics_set = NULL;
1430 ldi->sounds_set = NULL;
1431 ldi->music_set = NULL;
1432 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1433 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1434 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1436 ldi->first_level = 0;
1437 ldi->last_level = 0;
1438 ldi->level_group = FALSE;
1439 ldi->handicap_level = 0;
1440 ldi->readonly = TRUE;
1444 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1448 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1450 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1454 /* first copy all values from the parent structure ... */
1457 /* ... then set all fields to default that cannot be inherited from parent.
1458 This is especially important for all those fields that can be set from
1459 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1460 calls 'free()' for all already set token values which requires that no
1461 other structure's pointer may point to them!
1464 ldi->filename = NULL;
1465 ldi->fullpath = NULL;
1466 ldi->basepath = NULL;
1467 ldi->identifier = NULL;
1468 ldi->name = getStringCopy(ANONYMOUS_NAME);
1469 ldi->name_sorting = NULL;
1470 ldi->author = getStringCopy(parent->author);
1471 ldi->imported_from = getStringCopy(parent->imported_from);
1473 ldi->level_group = FALSE;
1474 ldi->parent_link = FALSE;
1476 ldi->node_top = parent->node_top;
1477 ldi->node_parent = parent;
1478 ldi->node_group = NULL;
1482 void setSetupInfo(struct TokenInfo *token_info,
1483 int token_nr, char *token_value)
1485 int token_type = token_info[token_nr].type;
1486 void *setup_value = token_info[token_nr].value;
1488 if (token_value == NULL)
1491 /* set setup field to corresponding token value */
1496 *(boolean *)setup_value = get_boolean_from_string(token_value);
1500 *(Key *)setup_value = getKeyFromKeyName(token_value);
1504 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1508 *(int *)setup_value = get_integer_from_string(token_value);
1512 if (*(char **)setup_value != NULL)
1513 free(*(char **)setup_value);
1514 *(char **)setup_value = getStringCopy(token_value);
1522 static int compareTreeInfoEntries(const void *object1, const void *object2)
1524 const TreeInfo *entry1 = *((TreeInfo **)object1);
1525 const TreeInfo *entry2 = *((TreeInfo **)object2);
1526 int class_sorting1, class_sorting2;
1529 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1531 class_sorting1 = LEVELSORTING(entry1);
1532 class_sorting2 = LEVELSORTING(entry2);
1536 class_sorting1 = ARTWORKSORTING(entry1);
1537 class_sorting2 = ARTWORKSORTING(entry2);
1540 if (entry1->parent_link || entry2->parent_link)
1541 compare_result = (entry1->parent_link ? -1 : +1);
1542 else if (entry1->sort_priority == entry2->sort_priority)
1544 char *name1 = getStringToLower(entry1->name_sorting);
1545 char *name2 = getStringToLower(entry2->name_sorting);
1547 compare_result = strcmp(name1, name2);
1552 else if (class_sorting1 == class_sorting2)
1553 compare_result = entry1->sort_priority - entry2->sort_priority;
1555 compare_result = class_sorting1 - class_sorting2;
1557 return compare_result;
1560 static void createParentTreeInfoNode(TreeInfo *node_parent)
1564 if (node_parent == NULL)
1567 ti_new = newTreeInfo();
1568 setTreeInfoToDefaults(ti_new, node_parent->type);
1570 ti_new->node_parent = node_parent;
1571 ti_new->parent_link = TRUE;
1573 ti_new->identifier = getStringCopy(node_parent->identifier);
1574 ti_new->name = ".. (parent directory)";
1575 ti_new->name_sorting = getStringCopy(ti_new->name);
1577 ti_new->filename = "..";
1578 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1580 ti_new->sort_priority = node_parent->sort_priority;
1581 ti_new->class_desc = getLevelClassDescription(ti_new);
1583 pushTreeInfo(&node_parent->node_group, ti_new);
1586 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1587 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1589 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1590 TreeInfo *node_parent,
1591 char *level_directory,
1592 char *directory_name)
1594 char *directory_path = getPath2(level_directory, directory_name);
1595 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1596 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1597 LevelDirTree *leveldir_new = NULL;
1600 if (setup_file_hash == NULL)
1602 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1604 free(directory_path);
1610 leveldir_new = newTreeInfo();
1613 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1615 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1617 leveldir_new->filename = getStringCopy(directory_name);
1619 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1621 /* set all structure fields according to the token/value pairs */
1622 ldi = *leveldir_new;
1623 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1624 setSetupInfo(levelinfo_tokens, i,
1625 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1626 *leveldir_new = ldi;
1628 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1630 free(leveldir_new->name);
1631 leveldir_new->name = getStringCopy(leveldir_new->filename);
1634 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1636 if (leveldir_new->identifier == NULL)
1637 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1639 if (leveldir_new->name_sorting == NULL)
1640 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1642 if (node_parent == NULL) /* top level group */
1644 leveldir_new->basepath = level_directory;
1645 leveldir_new->fullpath = leveldir_new->filename;
1647 else /* sub level group */
1649 leveldir_new->basepath = node_parent->basepath;
1650 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1653 if (leveldir_new->levels < 1)
1654 leveldir_new->levels = 1;
1656 leveldir_new->last_level =
1657 leveldir_new->first_level + leveldir_new->levels - 1;
1659 leveldir_new->user_defined =
1660 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1662 leveldir_new->color = LEVELCOLOR(leveldir_new);
1663 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1665 leveldir_new->handicap_level = /* set handicap to default value */
1666 (leveldir_new->user_defined ?
1667 leveldir_new->last_level :
1668 leveldir_new->first_level);
1670 pushTreeInfo(node_first, leveldir_new);
1672 freeSetupFileHash(setup_file_hash);
1674 if (leveldir_new->level_group)
1676 /* create node to link back to current level directory */
1677 createParentTreeInfoNode(leveldir_new);
1679 /* step into sub-directory and look for more level series */
1680 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1681 leveldir_new, directory_path);
1684 free(directory_path);
1690 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1691 TreeInfo *node_parent,
1692 char *level_directory)
1695 struct dirent *dir_entry;
1696 boolean valid_entry_found = FALSE;
1698 if ((dir = opendir(level_directory)) == NULL)
1700 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1704 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1706 struct stat file_status;
1707 char *directory_name = dir_entry->d_name;
1708 char *directory_path = getPath2(level_directory, directory_name);
1710 /* skip entries for current and parent directory */
1711 if (strcmp(directory_name, ".") == 0 ||
1712 strcmp(directory_name, "..") == 0)
1714 free(directory_path);
1718 /* find out if directory entry is itself a directory */
1719 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1720 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1722 free(directory_path);
1726 free(directory_path);
1728 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1729 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1730 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1733 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1740 if (!valid_entry_found)
1742 /* check if this directory directly contains a file "levelinfo.conf" */
1743 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1744 level_directory, ".");
1747 if (!valid_entry_found)
1748 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1752 void LoadLevelInfo()
1754 InitUserLevelDirectory(getLoginName());
1756 DrawInitText("Loading level series:", 120, FC_GREEN);
1758 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1759 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1761 /* before sorting, the first entries will be from the user directory */
1762 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1764 if (leveldir_first == NULL)
1765 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1767 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1770 dumpTreeInfo(leveldir_first, 0);
1774 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1775 TreeInfo *node_parent,
1776 char *base_directory,
1777 char *directory_name, int type)
1779 char *directory_path = getPath2(base_directory, directory_name);
1780 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1781 SetupFileHash *setup_file_hash = NULL;
1782 TreeInfo *artwork_new = NULL;
1785 if (access(filename, F_OK) == 0) /* file exists */
1786 setup_file_hash = loadSetupFileHash(filename);
1788 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1791 struct dirent *dir_entry;
1792 boolean valid_file_found = FALSE;
1794 if ((dir = opendir(directory_path)) != NULL)
1796 while ((dir_entry = readdir(dir)) != NULL)
1798 char *entry_name = dir_entry->d_name;
1800 if (FileIsArtworkType(entry_name, type))
1802 valid_file_found = TRUE;
1810 if (!valid_file_found)
1812 if (strcmp(directory_name, ".") != 0)
1813 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1815 free(directory_path);
1822 artwork_new = newTreeInfo();
1825 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1827 setTreeInfoToDefaults(artwork_new, type);
1829 artwork_new->filename = getStringCopy(directory_name);
1831 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1834 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1837 /* set all structure fields according to the token/value pairs */
1839 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1840 setSetupInfo(levelinfo_tokens, i,
1841 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1844 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1846 free(artwork_new->name);
1847 artwork_new->name = getStringCopy(artwork_new->filename);
1851 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1854 if (artwork_new->identifier == NULL)
1855 artwork_new->identifier = getStringCopy(artwork_new->filename);
1857 if (artwork_new->name_sorting == NULL)
1858 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1861 if (node_parent == NULL) /* top level group */
1863 artwork_new->basepath = getStringCopy(base_directory);
1864 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1866 else /* sub level group */
1868 artwork_new->basepath = getStringCopy(node_parent->basepath);
1869 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1872 artwork_new->user_defined =
1873 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1875 /* (may use ".sort_priority" from "setup_file_hash" above) */
1876 artwork_new->color = ARTWORKCOLOR(artwork_new);
1877 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1879 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1881 if (artwork_new->name != NULL)
1882 free(artwork_new->name);
1884 if (strcmp(artwork_new->filename, ".") == 0)
1886 if (artwork_new->user_defined)
1888 artwork_new->identifier = getStringCopy("private");
1889 artwork_new->sort_priority = ARTWORKCLASS_USER;
1893 artwork_new->identifier = getStringCopy("classic");
1894 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1897 /* set to new values after changing ".sort_priority" */
1898 artwork_new->color = ARTWORKCOLOR(artwork_new);
1899 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1903 artwork_new->identifier = getStringCopy(artwork_new->filename);
1906 artwork_new->name = getStringCopy(artwork_new->identifier);
1907 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1910 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1912 pushTreeInfo(node_first, artwork_new);
1914 freeSetupFileHash(setup_file_hash);
1916 free(directory_path);
1922 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1923 TreeInfo *node_parent,
1924 char *base_directory, int type)
1927 struct dirent *dir_entry;
1928 boolean valid_entry_found = FALSE;
1930 if ((dir = opendir(base_directory)) == NULL)
1932 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1933 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1937 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1939 struct stat file_status;
1940 char *directory_name = dir_entry->d_name;
1941 char *directory_path = getPath2(base_directory, directory_name);
1943 /* skip entries for current and parent directory */
1944 if (strcmp(directory_name, ".") == 0 ||
1945 strcmp(directory_name, "..") == 0)
1947 free(directory_path);
1951 /* find out if directory entry is itself a directory */
1952 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1953 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1955 free(directory_path);
1959 free(directory_path);
1961 /* check if this directory contains artwork with or without config file */
1962 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1964 directory_name, type);
1969 /* check if this directory directly contains artwork itself */
1970 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1971 base_directory, ".",
1973 if (!valid_entry_found)
1974 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1978 static TreeInfo *getDummyArtworkInfo(int type)
1980 /* this is only needed when there is completely no artwork available */
1981 TreeInfo *artwork_new = newTreeInfo();
1983 setTreeInfoToDefaults(artwork_new, type);
1985 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1986 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1987 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1989 if (artwork_new->name != NULL)
1990 free(artwork_new->name);
1992 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1993 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1994 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1999 void LoadArtworkInfo()
2001 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2003 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2004 options.graphics_directory,
2005 TREE_TYPE_GRAPHICS_DIR);
2006 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2007 getUserGraphicsDir(),
2008 TREE_TYPE_GRAPHICS_DIR);
2010 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2011 options.sounds_directory,
2012 TREE_TYPE_SOUNDS_DIR);
2013 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2015 TREE_TYPE_SOUNDS_DIR);
2017 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2018 options.music_directory,
2019 TREE_TYPE_MUSIC_DIR);
2020 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2022 TREE_TYPE_MUSIC_DIR);
2024 if (artwork.gfx_first == NULL)
2025 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2026 if (artwork.snd_first == NULL)
2027 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2028 if (artwork.mus_first == NULL)
2029 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2031 /* before sorting, the first entries will be from the user directory */
2032 artwork.gfx_current =
2033 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2034 if (artwork.gfx_current == NULL)
2035 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2037 artwork.snd_current =
2038 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2039 if (artwork.snd_current == NULL)
2040 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2042 artwork.mus_current =
2043 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2044 if (artwork.mus_current == NULL)
2045 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2047 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2048 artwork.snd_current_identifier = artwork.snd_current->identifier;
2049 artwork.mus_current_identifier = artwork.mus_current->identifier;
2052 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2053 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2054 printf("music set == %s\n\n", artwork.mus_current_identifier);
2057 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2058 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2059 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2062 dumpTreeInfo(artwork.gfx_first, 0);
2063 dumpTreeInfo(artwork.snd_first, 0);
2064 dumpTreeInfo(artwork.mus_first, 0);
2068 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2069 LevelDirTree *level_node)
2071 /* recursively check all level directories for artwork sub-directories */
2075 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2076 ARTWORK_DIRECTORY((*artwork_node)->type));
2079 if (!level_node->parent_link)
2080 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2081 level_node->filename, level_node->name);
2084 if (!level_node->parent_link)
2086 TreeInfo *topnode_last = *artwork_node;
2088 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2089 (*artwork_node)->type);
2091 if (topnode_last != *artwork_node)
2093 free((*artwork_node)->identifier);
2094 free((*artwork_node)->name);
2095 free((*artwork_node)->name_sorting);
2097 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2098 (*artwork_node)->name = getStringCopy(level_node->name);
2099 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2101 (*artwork_node)->sort_priority = level_node->sort_priority;
2102 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2108 if (level_node->node_group != NULL)
2109 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2111 level_node = level_node->next;
2115 void LoadLevelArtworkInfo()
2117 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2119 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2120 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2121 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2123 /* needed for reloading level artwork not known at ealier stage */
2124 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2126 artwork.gfx_current =
2127 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2128 if (artwork.gfx_current == NULL)
2129 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2132 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2134 artwork.snd_current =
2135 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2136 if (artwork.snd_current == NULL)
2137 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2140 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2142 artwork.mus_current =
2143 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2144 if (artwork.mus_current == NULL)
2145 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2148 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2149 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2150 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2153 dumpTreeInfo(artwork.gfx_first, 0);
2154 dumpTreeInfo(artwork.snd_first, 0);
2155 dumpTreeInfo(artwork.mus_first, 0);
2159 static void SaveUserLevelInfo()
2165 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2167 if (!(file = fopen(filename, MODE_WRITE)))
2169 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2174 /* always start with reliable default values */
2175 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2177 ldi.name = getStringCopy(getLoginName());
2178 ldi.author = getStringCopy(getRealName());
2180 ldi.first_level = 1;
2181 ldi.sort_priority = LEVELCLASS_USER_START;
2182 ldi.readonly = FALSE;
2183 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2184 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2185 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2187 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2188 getCookie("LEVELINFO")));
2190 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2191 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2192 i != LEVELINFO_TOKEN_NAME_SORTING &&
2193 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2194 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2199 SetFilePermissions(filename, PERMS_PRIVATE);
2202 char *getSetupValue(int type, void *value)
2204 static char value_string[MAX_LINE_LEN];
2212 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2216 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2220 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2224 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2228 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2232 sprintf(value_string, "%d", *(int *)value);
2236 strcpy(value_string, *(char **)value);
2240 value_string[0] = '\0';
2244 return value_string;
2247 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2251 static char token_string[MAX_LINE_LEN];
2252 int token_type = token_info[token_nr].type;
2253 void *setup_value = token_info[token_nr].value;
2254 char *token_text = token_info[token_nr].text;
2255 char *value_string = getSetupValue(token_type, setup_value);
2257 /* build complete token string */
2258 sprintf(token_string, "%s%s", prefix, token_text);
2260 /* build setup entry line */
2261 line = getFormattedSetupEntry(token_string, value_string);
2263 if (token_type == TYPE_KEY_X11)
2265 Key key = *(Key *)setup_value;
2266 char *keyname = getKeyNameFromKey(key);
2268 /* add comment, if useful */
2269 if (strcmp(keyname, "(undefined)") != 0 &&
2270 strcmp(keyname, "(unknown)") != 0)
2272 /* add at least one whitespace */
2274 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2278 strcat(line, keyname);
2285 void LoadLevelSetup_LastSeries()
2288 SetupFileHash *level_setup_hash = NULL;
2290 /* always start with reliable default values */
2291 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2293 /* ----------------------------------------------------------------------- */
2294 /* ~/.<program>/levelsetup.conf */
2295 /* ----------------------------------------------------------------------- */
2297 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2299 if ((level_setup_hash = loadSetupFileHash(filename)))
2301 char *last_level_series =
2302 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2304 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2306 if (leveldir_current == NULL)
2307 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2309 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2311 freeSetupFileHash(level_setup_hash);
2314 Error(ERR_WARN, "using default setup values");
2319 void SaveLevelSetup_LastSeries()
2322 char *level_subdir = leveldir_current->filename;
2325 /* ----------------------------------------------------------------------- */
2326 /* ~/.<program>/levelsetup.conf */
2327 /* ----------------------------------------------------------------------- */
2329 InitUserDataDirectory();
2331 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2333 if (!(file = fopen(filename, MODE_WRITE)))
2335 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2340 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2341 getCookie("LEVELSETUP")));
2342 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2348 SetFilePermissions(filename, PERMS_PRIVATE);
2351 static void checkSeriesInfo()
2353 static char *level_directory = NULL;
2355 struct dirent *dir_entry;
2357 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2359 level_directory = getPath2((leveldir_current->user_defined ?
2360 getUserLevelDir(NULL) :
2361 options.level_directory),
2362 leveldir_current->fullpath);
2364 if ((dir = opendir(level_directory)) == NULL)
2366 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2370 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2372 if (strlen(dir_entry->d_name) > 4 &&
2373 dir_entry->d_name[3] == '.' &&
2374 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2376 char levelnum_str[4];
2379 strncpy(levelnum_str, dir_entry->d_name, 3);
2380 levelnum_str[3] = '\0';
2382 levelnum_value = atoi(levelnum_str);
2384 if (levelnum_value < leveldir_current->first_level)
2386 Error(ERR_WARN, "additional level %d found", levelnum_value);
2387 leveldir_current->first_level = levelnum_value;
2389 else if (levelnum_value > leveldir_current->last_level)
2391 Error(ERR_WARN, "additional level %d found", levelnum_value);
2392 leveldir_current->last_level = levelnum_value;
2400 void LoadLevelSetup_SeriesInfo()
2403 SetupFileHash *level_setup_hash = NULL;
2404 char *level_subdir = leveldir_current->filename;
2406 /* always start with reliable default values */
2407 level_nr = leveldir_current->first_level;
2409 checkSeriesInfo(leveldir_current);
2411 /* ----------------------------------------------------------------------- */
2412 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2413 /* ----------------------------------------------------------------------- */
2415 level_subdir = leveldir_current->filename;
2417 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2419 if ((level_setup_hash = loadSetupFileHash(filename)))
2423 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2427 level_nr = atoi(token_value);
2429 if (level_nr < leveldir_current->first_level)
2430 level_nr = leveldir_current->first_level;
2431 if (level_nr > leveldir_current->last_level)
2432 level_nr = leveldir_current->last_level;
2435 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2439 int level_nr = atoi(token_value);
2441 if (level_nr < leveldir_current->first_level)
2442 level_nr = leveldir_current->first_level;
2443 if (level_nr > leveldir_current->last_level + 1)
2444 level_nr = leveldir_current->last_level;
2446 if (leveldir_current->user_defined)
2447 level_nr = leveldir_current->last_level;
2449 leveldir_current->handicap_level = level_nr;
2452 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2454 freeSetupFileHash(level_setup_hash);
2457 Error(ERR_WARN, "using default setup values");
2462 void SaveLevelSetup_SeriesInfo()
2465 char *level_subdir = leveldir_current->filename;
2466 char *level_nr_str = int2str(level_nr, 0);
2467 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2470 /* ----------------------------------------------------------------------- */
2471 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2472 /* ----------------------------------------------------------------------- */
2474 InitLevelSetupDirectory(level_subdir);
2476 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2478 if (!(file = fopen(filename, MODE_WRITE)))
2480 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2485 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2486 getCookie("LEVELSETUP")));
2487 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2489 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2490 handicap_level_str));
2495 SetFilePermissions(filename, PERMS_PRIVATE);