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 SetupFileList *newSetupFileList(char *token, char *value)
1045 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1047 new->token = getStringCopy(token);
1048 new->value = getStringCopy(value);
1055 void freeSetupFileList(SetupFileList *list)
1065 freeSetupFileList(list->next);
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 SetupFileHash *newSetupFileHash()
1152 SetupFileHash *new_hash =
1153 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1158 void freeSetupFileHash(SetupFileHash *hash)
1163 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1166 char *getHashEntry(SetupFileHash *hash, char *token)
1171 return search_hash_entry(hash, token);
1174 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1181 value_copy = getStringCopy(value);
1183 /* change value; if it does not exist, insert it as new */
1184 if (!change_hash_entry(hash, token, value_copy))
1185 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1186 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1191 static void printSetupFileHash(SetupFileHash *hash)
1197 /* iterator constructor only returns valid iterator for non-empty hash */
1198 if (hash != NULL && hashtable_count(hash) > 0)
1200 struct hashtable_itr *itr = hashtable_iterator(hash);
1204 printf("token: '%s'\n", (char *)hashtable_iterator_key(itr));
1205 printf("value: '%s'\n", (char *)hashtable_iterator_value(itr));
1207 while (hashtable_iterator_advance(itr));
1213 BEGIN_HASH_ITERATION(hash, itr)
1215 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1216 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1218 END_HASH_ITERATION(hash, itr)
1223 static void *loadSetupFileData(char *filename, boolean use_hash)
1226 char line[MAX_LINE_LEN];
1227 char *token, *value, *line_ptr;
1228 void *setup_file_data;
1232 setup_file_data = newSetupFileHash();
1234 setup_file_data = newSetupFileList("", "");
1236 if (!(file = fopen(filename, MODE_READ)))
1238 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1244 /* read next line of input file */
1245 if (!fgets(line, MAX_LINE_LEN, file))
1248 /* cut trailing comment or whitespace from input line */
1249 for (line_ptr = line; *line_ptr; line_ptr++)
1251 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1258 /* cut trailing whitespaces from input line */
1259 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1260 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1263 /* ignore empty lines */
1267 line_len = strlen(line);
1269 /* cut leading whitespaces from token */
1270 for (token = line; *token; token++)
1271 if (*token != ' ' && *token != '\t')
1274 /* find end of token */
1275 for (line_ptr = token; *line_ptr; line_ptr++)
1277 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1284 if (line_ptr < line + line_len)
1285 value = line_ptr + 1;
1289 /* cut leading whitespaces from value */
1290 for (; *value; value++)
1291 if (*value != ' ' && *value != '\t')
1294 if (*token && *value)
1297 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1299 setListEntry((SetupFileList *)setup_file_data, token, value);
1307 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1308 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1312 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1313 SetupFileList *first_valid_list_entry = setup_file_list->next;
1315 /* free empty list header */
1316 setup_file_list->next = NULL;
1317 freeSetupFileList(setup_file_list);
1318 setup_file_data = first_valid_list_entry;
1320 if (first_valid_list_entry == NULL)
1321 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1324 return setup_file_data;
1327 SetupFileList *loadSetupFileList(char *filename)
1329 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1332 SetupFileHash *loadSetupFileHash(char *filename)
1334 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1337 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1340 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1343 Error(ERR_WARN, "configuration file has no file identifier");
1344 else if (!checkCookieString(value, identifier))
1345 Error(ERR_WARN, "configuration file has wrong file identifier");
1349 /* ========================================================================= */
1350 /* setup file stuff */
1351 /* ========================================================================= */
1353 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1354 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1355 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1357 /* level directory info */
1358 #define LEVELINFO_TOKEN_IDENTIFIER 0
1359 #define LEVELINFO_TOKEN_NAME 1
1360 #define LEVELINFO_TOKEN_NAME_SORTING 2
1361 #define LEVELINFO_TOKEN_AUTHOR 3
1362 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1363 #define LEVELINFO_TOKEN_LEVELS 5
1364 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1365 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1366 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1367 #define LEVELINFO_TOKEN_READONLY 9
1368 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1369 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1370 #define LEVELINFO_TOKEN_MUSIC_SET 12
1372 #define NUM_LEVELINFO_TOKENS 13
1374 static LevelDirTree ldi;
1376 static struct TokenInfo levelinfo_tokens[] =
1378 /* level directory info */
1379 { TYPE_STRING, &ldi.identifier, "identifier" },
1380 { TYPE_STRING, &ldi.name, "name" },
1381 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1382 { TYPE_STRING, &ldi.author, "author" },
1383 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1384 { TYPE_INTEGER, &ldi.levels, "levels" },
1385 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1386 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1387 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1388 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1389 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1390 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1391 { TYPE_STRING, &ldi.music_set, "music_set" }
1394 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1398 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1399 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1400 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1401 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1404 ldi->node_parent = NULL;
1405 ldi->node_group = NULL;
1409 ldi->cl_cursor = -1;
1411 ldi->filename = NULL;
1412 ldi->fullpath = NULL;
1413 ldi->basepath = NULL;
1414 ldi->identifier = NULL;
1415 ldi->name = getStringCopy(ANONYMOUS_NAME);
1416 ldi->name_sorting = NULL;
1417 ldi->author = getStringCopy(ANONYMOUS_NAME);
1419 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1420 ldi->parent_link = FALSE;
1421 ldi->user_defined = FALSE;
1423 ldi->class_desc = NULL;
1425 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1427 ldi->imported_from = NULL;
1428 ldi->graphics_set = NULL;
1429 ldi->sounds_set = NULL;
1430 ldi->music_set = NULL;
1431 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1432 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1433 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1435 ldi->first_level = 0;
1436 ldi->last_level = 0;
1437 ldi->level_group = FALSE;
1438 ldi->handicap_level = 0;
1439 ldi->readonly = TRUE;
1443 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1447 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1449 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1453 /* first copy all values from the parent structure ... */
1456 /* ... then set all fields to default that cannot be inherited from parent.
1457 This is especially important for all those fields that can be set from
1458 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1459 calls 'free()' for all already set token values which requires that no
1460 other structure's pointer may point to them!
1463 ldi->filename = NULL;
1464 ldi->fullpath = NULL;
1465 ldi->basepath = NULL;
1466 ldi->identifier = NULL;
1467 ldi->name = getStringCopy(ANONYMOUS_NAME);
1468 ldi->name_sorting = NULL;
1469 ldi->author = getStringCopy(parent->author);
1470 ldi->imported_from = getStringCopy(parent->imported_from);
1472 ldi->level_group = FALSE;
1473 ldi->parent_link = FALSE;
1475 ldi->node_top = parent->node_top;
1476 ldi->node_parent = parent;
1477 ldi->node_group = NULL;
1481 void setSetupInfo(struct TokenInfo *token_info,
1482 int token_nr, char *token_value)
1484 int token_type = token_info[token_nr].type;
1485 void *setup_value = token_info[token_nr].value;
1487 if (token_value == NULL)
1490 /* set setup field to corresponding token value */
1495 *(boolean *)setup_value = get_boolean_from_string(token_value);
1499 *(Key *)setup_value = getKeyFromKeyName(token_value);
1503 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1507 *(int *)setup_value = get_integer_from_string(token_value);
1511 if (*(char **)setup_value != NULL)
1512 free(*(char **)setup_value);
1513 *(char **)setup_value = getStringCopy(token_value);
1521 static int compareTreeInfoEntries(const void *object1, const void *object2)
1523 const TreeInfo *entry1 = *((TreeInfo **)object1);
1524 const TreeInfo *entry2 = *((TreeInfo **)object2);
1525 int class_sorting1, class_sorting2;
1528 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1530 class_sorting1 = LEVELSORTING(entry1);
1531 class_sorting2 = LEVELSORTING(entry2);
1535 class_sorting1 = ARTWORKSORTING(entry1);
1536 class_sorting2 = ARTWORKSORTING(entry2);
1539 if (entry1->parent_link || entry2->parent_link)
1540 compare_result = (entry1->parent_link ? -1 : +1);
1541 else if (entry1->sort_priority == entry2->sort_priority)
1543 char *name1 = getStringToLower(entry1->name_sorting);
1544 char *name2 = getStringToLower(entry2->name_sorting);
1546 compare_result = strcmp(name1, name2);
1551 else if (class_sorting1 == class_sorting2)
1552 compare_result = entry1->sort_priority - entry2->sort_priority;
1554 compare_result = class_sorting1 - class_sorting2;
1556 return compare_result;
1559 static void createParentTreeInfoNode(TreeInfo *node_parent)
1563 if (node_parent == NULL)
1566 ti_new = newTreeInfo();
1567 setTreeInfoToDefaults(ti_new, node_parent->type);
1569 ti_new->node_parent = node_parent;
1570 ti_new->parent_link = TRUE;
1572 ti_new->identifier = getStringCopy(node_parent->identifier);
1573 ti_new->name = ".. (parent directory)";
1574 ti_new->name_sorting = getStringCopy(ti_new->name);
1576 ti_new->filename = "..";
1577 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1579 ti_new->sort_priority = node_parent->sort_priority;
1580 ti_new->class_desc = getLevelClassDescription(ti_new);
1582 pushTreeInfo(&node_parent->node_group, ti_new);
1585 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1586 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1588 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1589 TreeInfo *node_parent,
1590 char *level_directory,
1591 char *directory_name)
1593 char *directory_path = getPath2(level_directory, directory_name);
1594 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1595 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1596 LevelDirTree *leveldir_new = NULL;
1599 if (setup_file_hash == NULL)
1601 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1603 free(directory_path);
1609 leveldir_new = newTreeInfo();
1612 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1614 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1616 leveldir_new->filename = getStringCopy(directory_name);
1618 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1620 /* set all structure fields according to the token/value pairs */
1621 ldi = *leveldir_new;
1622 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1623 setSetupInfo(levelinfo_tokens, i,
1624 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1625 *leveldir_new = ldi;
1627 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1629 free(leveldir_new->name);
1630 leveldir_new->name = getStringCopy(leveldir_new->filename);
1633 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1635 if (leveldir_new->identifier == NULL)
1636 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1638 if (leveldir_new->name_sorting == NULL)
1639 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1641 if (node_parent == NULL) /* top level group */
1643 leveldir_new->basepath = level_directory;
1644 leveldir_new->fullpath = leveldir_new->filename;
1646 else /* sub level group */
1648 leveldir_new->basepath = node_parent->basepath;
1649 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1652 if (leveldir_new->levels < 1)
1653 leveldir_new->levels = 1;
1655 leveldir_new->last_level =
1656 leveldir_new->first_level + leveldir_new->levels - 1;
1658 leveldir_new->user_defined =
1659 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1661 leveldir_new->color = LEVELCOLOR(leveldir_new);
1662 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1664 leveldir_new->handicap_level = /* set handicap to default value */
1665 (leveldir_new->user_defined ?
1666 leveldir_new->last_level :
1667 leveldir_new->first_level);
1669 pushTreeInfo(node_first, leveldir_new);
1671 freeSetupFileHash(setup_file_hash);
1673 if (leveldir_new->level_group)
1675 /* create node to link back to current level directory */
1676 createParentTreeInfoNode(leveldir_new);
1678 /* step into sub-directory and look for more level series */
1679 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1680 leveldir_new, directory_path);
1683 free(directory_path);
1689 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1690 TreeInfo *node_parent,
1691 char *level_directory)
1694 struct dirent *dir_entry;
1695 boolean valid_entry_found = FALSE;
1697 if ((dir = opendir(level_directory)) == NULL)
1699 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1703 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1705 struct stat file_status;
1706 char *directory_name = dir_entry->d_name;
1707 char *directory_path = getPath2(level_directory, directory_name);
1709 /* skip entries for current and parent directory */
1710 if (strcmp(directory_name, ".") == 0 ||
1711 strcmp(directory_name, "..") == 0)
1713 free(directory_path);
1717 /* find out if directory entry is itself a directory */
1718 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1719 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1721 free(directory_path);
1725 free(directory_path);
1727 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1728 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1729 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1732 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1739 if (!valid_entry_found)
1741 /* check if this directory directly contains a file "levelinfo.conf" */
1742 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1743 level_directory, ".");
1746 if (!valid_entry_found)
1747 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1751 void LoadLevelInfo()
1753 InitUserLevelDirectory(getLoginName());
1755 DrawInitText("Loading level series:", 120, FC_GREEN);
1757 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1758 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1760 /* before sorting, the first entries will be from the user directory */
1761 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1763 if (leveldir_first == NULL)
1764 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1766 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1769 dumpTreeInfo(leveldir_first, 0);
1773 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1774 TreeInfo *node_parent,
1775 char *base_directory,
1776 char *directory_name, int type)
1778 char *directory_path = getPath2(base_directory, directory_name);
1779 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1780 SetupFileHash *setup_file_hash = NULL;
1781 TreeInfo *artwork_new = NULL;
1784 if (access(filename, F_OK) == 0) /* file exists */
1785 setup_file_hash = loadSetupFileHash(filename);
1787 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1790 struct dirent *dir_entry;
1791 boolean valid_file_found = FALSE;
1793 if ((dir = opendir(directory_path)) != NULL)
1795 while ((dir_entry = readdir(dir)) != NULL)
1797 char *entry_name = dir_entry->d_name;
1799 if (FileIsArtworkType(entry_name, type))
1801 valid_file_found = TRUE;
1809 if (!valid_file_found)
1811 if (strcmp(directory_name, ".") != 0)
1812 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1814 free(directory_path);
1821 artwork_new = newTreeInfo();
1824 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1826 setTreeInfoToDefaults(artwork_new, type);
1828 artwork_new->filename = getStringCopy(directory_name);
1830 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1833 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1836 /* set all structure fields according to the token/value pairs */
1838 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1839 setSetupInfo(levelinfo_tokens, i,
1840 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1843 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1845 free(artwork_new->name);
1846 artwork_new->name = getStringCopy(artwork_new->filename);
1850 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1853 if (artwork_new->identifier == NULL)
1854 artwork_new->identifier = getStringCopy(artwork_new->filename);
1856 if (artwork_new->name_sorting == NULL)
1857 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1860 if (node_parent == NULL) /* top level group */
1862 artwork_new->basepath = getStringCopy(base_directory);
1863 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1865 else /* sub level group */
1867 artwork_new->basepath = getStringCopy(node_parent->basepath);
1868 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1871 artwork_new->user_defined =
1872 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1874 /* (may use ".sort_priority" from "setup_file_hash" above) */
1875 artwork_new->color = ARTWORKCOLOR(artwork_new);
1876 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1878 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1880 if (artwork_new->name != NULL)
1881 free(artwork_new->name);
1883 if (strcmp(artwork_new->filename, ".") == 0)
1885 if (artwork_new->user_defined)
1887 artwork_new->identifier = getStringCopy("private");
1888 artwork_new->sort_priority = ARTWORKCLASS_USER;
1892 artwork_new->identifier = getStringCopy("classic");
1893 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1896 /* set to new values after changing ".sort_priority" */
1897 artwork_new->color = ARTWORKCOLOR(artwork_new);
1898 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1902 artwork_new->identifier = getStringCopy(artwork_new->filename);
1905 artwork_new->name = getStringCopy(artwork_new->identifier);
1906 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1909 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1911 pushTreeInfo(node_first, artwork_new);
1913 freeSetupFileHash(setup_file_hash);
1915 free(directory_path);
1921 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1922 TreeInfo *node_parent,
1923 char *base_directory, int type)
1926 struct dirent *dir_entry;
1927 boolean valid_entry_found = FALSE;
1929 if ((dir = opendir(base_directory)) == NULL)
1931 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1932 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1936 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1938 struct stat file_status;
1939 char *directory_name = dir_entry->d_name;
1940 char *directory_path = getPath2(base_directory, directory_name);
1942 /* skip entries for current and parent directory */
1943 if (strcmp(directory_name, ".") == 0 ||
1944 strcmp(directory_name, "..") == 0)
1946 free(directory_path);
1950 /* find out if directory entry is itself a directory */
1951 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1952 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1954 free(directory_path);
1958 free(directory_path);
1960 /* check if this directory contains artwork with or without config file */
1961 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1963 directory_name, type);
1968 /* check if this directory directly contains artwork itself */
1969 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1970 base_directory, ".",
1972 if (!valid_entry_found)
1973 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1977 static TreeInfo *getDummyArtworkInfo(int type)
1979 /* this is only needed when there is completely no artwork available */
1980 TreeInfo *artwork_new = newTreeInfo();
1982 setTreeInfoToDefaults(artwork_new, type);
1984 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1985 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1986 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1988 if (artwork_new->name != NULL)
1989 free(artwork_new->name);
1991 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1992 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1993 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1998 void LoadArtworkInfo()
2000 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2002 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2003 options.graphics_directory,
2004 TREE_TYPE_GRAPHICS_DIR);
2005 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2006 getUserGraphicsDir(),
2007 TREE_TYPE_GRAPHICS_DIR);
2009 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2010 options.sounds_directory,
2011 TREE_TYPE_SOUNDS_DIR);
2012 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2014 TREE_TYPE_SOUNDS_DIR);
2016 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2017 options.music_directory,
2018 TREE_TYPE_MUSIC_DIR);
2019 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2021 TREE_TYPE_MUSIC_DIR);
2023 if (artwork.gfx_first == NULL)
2024 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2025 if (artwork.snd_first == NULL)
2026 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2027 if (artwork.mus_first == NULL)
2028 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2030 /* before sorting, the first entries will be from the user directory */
2031 artwork.gfx_current =
2032 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2033 if (artwork.gfx_current == NULL)
2034 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2036 artwork.snd_current =
2037 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2038 if (artwork.snd_current == NULL)
2039 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2041 artwork.mus_current =
2042 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2043 if (artwork.mus_current == NULL)
2044 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2046 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2047 artwork.snd_current_identifier = artwork.snd_current->identifier;
2048 artwork.mus_current_identifier = artwork.mus_current->identifier;
2051 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2052 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2053 printf("music set == %s\n\n", artwork.mus_current_identifier);
2056 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2057 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2058 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2061 dumpTreeInfo(artwork.gfx_first, 0);
2062 dumpTreeInfo(artwork.snd_first, 0);
2063 dumpTreeInfo(artwork.mus_first, 0);
2067 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2068 LevelDirTree *level_node)
2070 /* recursively check all level directories for artwork sub-directories */
2074 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2075 ARTWORK_DIRECTORY((*artwork_node)->type));
2078 if (!level_node->parent_link)
2079 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2080 level_node->filename, level_node->name);
2083 if (!level_node->parent_link)
2085 TreeInfo *topnode_last = *artwork_node;
2087 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2088 (*artwork_node)->type);
2090 if (topnode_last != *artwork_node)
2092 free((*artwork_node)->identifier);
2093 free((*artwork_node)->name);
2094 free((*artwork_node)->name_sorting);
2096 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2097 (*artwork_node)->name = getStringCopy(level_node->name);
2098 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2100 (*artwork_node)->sort_priority = level_node->sort_priority;
2101 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2107 if (level_node->node_group != NULL)
2108 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2110 level_node = level_node->next;
2114 void LoadLevelArtworkInfo()
2116 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2118 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2119 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2120 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2122 /* needed for reloading level artwork not known at ealier stage */
2123 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2125 artwork.gfx_current =
2126 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2127 if (artwork.gfx_current == NULL)
2128 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2131 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2133 artwork.snd_current =
2134 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2135 if (artwork.snd_current == NULL)
2136 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2139 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2141 artwork.mus_current =
2142 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2143 if (artwork.mus_current == NULL)
2144 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2147 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2148 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2149 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2152 dumpTreeInfo(artwork.gfx_first, 0);
2153 dumpTreeInfo(artwork.snd_first, 0);
2154 dumpTreeInfo(artwork.mus_first, 0);
2158 static void SaveUserLevelInfo()
2164 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2166 if (!(file = fopen(filename, MODE_WRITE)))
2168 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2173 /* always start with reliable default values */
2174 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2176 ldi.name = getStringCopy(getLoginName());
2177 ldi.author = getStringCopy(getRealName());
2179 ldi.first_level = 1;
2180 ldi.sort_priority = LEVELCLASS_USER_START;
2181 ldi.readonly = FALSE;
2182 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2183 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2184 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2186 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2187 getCookie("LEVELINFO")));
2189 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2190 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2191 i != LEVELINFO_TOKEN_NAME_SORTING &&
2192 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2193 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2198 SetFilePermissions(filename, PERMS_PRIVATE);
2201 char *getSetupValue(int type, void *value)
2203 static char value_string[MAX_LINE_LEN];
2211 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2215 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2219 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2223 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2227 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2231 sprintf(value_string, "%d", *(int *)value);
2235 strcpy(value_string, *(char **)value);
2239 value_string[0] = '\0';
2243 return value_string;
2246 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2250 static char token_string[MAX_LINE_LEN];
2251 int token_type = token_info[token_nr].type;
2252 void *setup_value = token_info[token_nr].value;
2253 char *token_text = token_info[token_nr].text;
2254 char *value_string = getSetupValue(token_type, setup_value);
2256 /* build complete token string */
2257 sprintf(token_string, "%s%s", prefix, token_text);
2259 /* build setup entry line */
2260 line = getFormattedSetupEntry(token_string, value_string);
2262 if (token_type == TYPE_KEY_X11)
2264 Key key = *(Key *)setup_value;
2265 char *keyname = getKeyNameFromKey(key);
2267 /* add comment, if useful */
2268 if (strcmp(keyname, "(undefined)") != 0 &&
2269 strcmp(keyname, "(unknown)") != 0)
2271 /* add at least one whitespace */
2273 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2277 strcat(line, keyname);
2284 void LoadLevelSetup_LastSeries()
2287 SetupFileHash *level_setup_hash = NULL;
2289 /* always start with reliable default values */
2290 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2292 /* ----------------------------------------------------------------------- */
2293 /* ~/.<program>/levelsetup.conf */
2294 /* ----------------------------------------------------------------------- */
2296 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2298 if ((level_setup_hash = loadSetupFileHash(filename)))
2300 char *last_level_series =
2301 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2303 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2305 if (leveldir_current == NULL)
2306 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2308 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2310 freeSetupFileHash(level_setup_hash);
2313 Error(ERR_WARN, "using default setup values");
2318 void SaveLevelSetup_LastSeries()
2321 char *level_subdir = leveldir_current->filename;
2324 /* ----------------------------------------------------------------------- */
2325 /* ~/.<program>/levelsetup.conf */
2326 /* ----------------------------------------------------------------------- */
2328 InitUserDataDirectory();
2330 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2332 if (!(file = fopen(filename, MODE_WRITE)))
2334 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2339 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2340 getCookie("LEVELSETUP")));
2341 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2347 SetFilePermissions(filename, PERMS_PRIVATE);
2350 static void checkSeriesInfo()
2352 static char *level_directory = NULL;
2354 struct dirent *dir_entry;
2356 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2358 level_directory = getPath2((leveldir_current->user_defined ?
2359 getUserLevelDir(NULL) :
2360 options.level_directory),
2361 leveldir_current->fullpath);
2363 if ((dir = opendir(level_directory)) == NULL)
2365 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2369 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2371 if (strlen(dir_entry->d_name) > 4 &&
2372 dir_entry->d_name[3] == '.' &&
2373 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2375 char levelnum_str[4];
2378 strncpy(levelnum_str, dir_entry->d_name, 3);
2379 levelnum_str[3] = '\0';
2381 levelnum_value = atoi(levelnum_str);
2383 if (levelnum_value < leveldir_current->first_level)
2385 Error(ERR_WARN, "additional level %d found", levelnum_value);
2386 leveldir_current->first_level = levelnum_value;
2388 else if (levelnum_value > leveldir_current->last_level)
2390 Error(ERR_WARN, "additional level %d found", levelnum_value);
2391 leveldir_current->last_level = levelnum_value;
2399 void LoadLevelSetup_SeriesInfo()
2402 SetupFileHash *level_setup_hash = NULL;
2403 char *level_subdir = leveldir_current->filename;
2405 /* always start with reliable default values */
2406 level_nr = leveldir_current->first_level;
2408 checkSeriesInfo(leveldir_current);
2410 /* ----------------------------------------------------------------------- */
2411 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2412 /* ----------------------------------------------------------------------- */
2414 level_subdir = leveldir_current->filename;
2416 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2418 if ((level_setup_hash = loadSetupFileHash(filename)))
2422 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2426 level_nr = atoi(token_value);
2428 if (level_nr < leveldir_current->first_level)
2429 level_nr = leveldir_current->first_level;
2430 if (level_nr > leveldir_current->last_level)
2431 level_nr = leveldir_current->last_level;
2434 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2438 int level_nr = atoi(token_value);
2440 if (level_nr < leveldir_current->first_level)
2441 level_nr = leveldir_current->first_level;
2442 if (level_nr > leveldir_current->last_level + 1)
2443 level_nr = leveldir_current->last_level;
2445 if (leveldir_current->user_defined)
2446 level_nr = leveldir_current->last_level;
2448 leveldir_current->handicap_level = level_nr;
2451 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2453 freeSetupFileHash(level_setup_hash);
2456 Error(ERR_WARN, "using default setup values");
2461 void SaveLevelSetup_SeriesInfo()
2464 char *level_subdir = leveldir_current->filename;
2465 char *level_nr_str = int2str(level_nr, 0);
2466 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2469 /* ----------------------------------------------------------------------- */
2470 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2471 /* ----------------------------------------------------------------------- */
2473 InitLevelSetupDirectory(level_subdir);
2475 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2477 if (!(file = fopen(filename, MODE_WRITE)))
2479 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2484 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2485 getCookie("LEVELSETUP")));
2486 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2488 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2489 handicap_level_str));
2494 SetFilePermissions(filename, PERMS_PRIVATE);