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.
1134 If you just want to have a good hash function, and cannot wait, djb2
1135 is one of the best string hash functions i know. It has excellent
1136 distribution and speed on many different sets of keys and table sizes.
1137 You are not likely to do better with one of the "well known" functions
1138 such as PJW, K&R, etc.
1140 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1143 char *str = (char *)key;
1144 unsigned int hash = 5381;
1147 while ((c = *str++))
1148 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1153 static int keys_are_equal(void *key1, void *key2)
1155 return (strcmp((char *)key1, (char *)key2) == 0);
1158 SetupFileHash *newSetupFileHash()
1160 SetupFileHash *new_hash =
1161 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1166 void freeSetupFileHash(SetupFileHash *hash)
1171 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1174 char *getHashEntry(SetupFileHash *hash, char *token)
1179 return search_hash_entry(hash, token);
1182 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1189 value_copy = getStringCopy(value);
1191 /* change value; if it does not exist, insert it as new */
1192 if (!change_hash_entry(hash, token, value_copy))
1193 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1194 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1199 static void printSetupFileHash(SetupFileHash *hash)
1201 BEGIN_HASH_ITERATION(hash, itr)
1203 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1204 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1206 END_HASH_ITERATION(hash, itr)
1211 static void *loadSetupFileData(char *filename, boolean use_hash)
1214 char line[MAX_LINE_LEN];
1215 char *token, *value, *line_ptr;
1216 void *setup_file_data;
1220 setup_file_data = newSetupFileHash();
1222 setup_file_data = newSetupFileList("", "");
1224 if (!(file = fopen(filename, MODE_READ)))
1226 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1232 /* read next line of input file */
1233 if (!fgets(line, MAX_LINE_LEN, file))
1236 /* cut trailing comment or whitespace from input line */
1237 for (line_ptr = line; *line_ptr; line_ptr++)
1239 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1246 /* cut trailing whitespaces from input line */
1247 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1248 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1251 /* ignore empty lines */
1255 line_len = strlen(line);
1257 /* cut leading whitespaces from token */
1258 for (token = line; *token; token++)
1259 if (*token != ' ' && *token != '\t')
1262 /* find end of token */
1263 for (line_ptr = token; *line_ptr; line_ptr++)
1265 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1272 if (line_ptr < line + line_len)
1273 value = line_ptr + 1;
1277 /* cut leading whitespaces from value */
1278 for (; *value; value++)
1279 if (*value != ' ' && *value != '\t')
1282 if (*token && *value)
1285 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1287 setListEntry((SetupFileList *)setup_file_data, token, value);
1295 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1296 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1300 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1301 SetupFileList *first_valid_list_entry = setup_file_list->next;
1303 /* free empty list header */
1304 setup_file_list->next = NULL;
1305 freeSetupFileList(setup_file_list);
1306 setup_file_data = first_valid_list_entry;
1308 if (first_valid_list_entry == NULL)
1309 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1312 return setup_file_data;
1315 SetupFileList *loadSetupFileList(char *filename)
1317 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1320 SetupFileHash *loadSetupFileHash(char *filename)
1322 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1325 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1328 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1331 Error(ERR_WARN, "configuration file has no file identifier");
1332 else if (!checkCookieString(value, identifier))
1333 Error(ERR_WARN, "configuration file has wrong file identifier");
1337 /* ========================================================================= */
1338 /* setup file stuff */
1339 /* ========================================================================= */
1341 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1342 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1343 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1345 /* level directory info */
1346 #define LEVELINFO_TOKEN_IDENTIFIER 0
1347 #define LEVELINFO_TOKEN_NAME 1
1348 #define LEVELINFO_TOKEN_NAME_SORTING 2
1349 #define LEVELINFO_TOKEN_AUTHOR 3
1350 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1351 #define LEVELINFO_TOKEN_LEVELS 5
1352 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1353 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1354 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1355 #define LEVELINFO_TOKEN_READONLY 9
1356 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1357 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1358 #define LEVELINFO_TOKEN_MUSIC_SET 12
1360 #define NUM_LEVELINFO_TOKENS 13
1362 static LevelDirTree ldi;
1364 static struct TokenInfo levelinfo_tokens[] =
1366 /* level directory info */
1367 { TYPE_STRING, &ldi.identifier, "identifier" },
1368 { TYPE_STRING, &ldi.name, "name" },
1369 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1370 { TYPE_STRING, &ldi.author, "author" },
1371 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1372 { TYPE_INTEGER, &ldi.levels, "levels" },
1373 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1374 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1375 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1376 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1377 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1378 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1379 { TYPE_STRING, &ldi.music_set, "music_set" }
1382 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1386 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1387 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1388 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1389 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1392 ldi->node_parent = NULL;
1393 ldi->node_group = NULL;
1397 ldi->cl_cursor = -1;
1399 ldi->filename = NULL;
1400 ldi->fullpath = NULL;
1401 ldi->basepath = NULL;
1402 ldi->identifier = NULL;
1403 ldi->name = getStringCopy(ANONYMOUS_NAME);
1404 ldi->name_sorting = NULL;
1405 ldi->author = getStringCopy(ANONYMOUS_NAME);
1407 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1408 ldi->parent_link = FALSE;
1409 ldi->user_defined = FALSE;
1411 ldi->class_desc = NULL;
1413 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1415 ldi->imported_from = NULL;
1416 ldi->graphics_set = NULL;
1417 ldi->sounds_set = NULL;
1418 ldi->music_set = NULL;
1419 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1420 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1421 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1423 ldi->first_level = 0;
1424 ldi->last_level = 0;
1425 ldi->level_group = FALSE;
1426 ldi->handicap_level = 0;
1427 ldi->readonly = TRUE;
1431 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1435 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1437 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1441 /* first copy all values from the parent structure ... */
1444 /* ... then set all fields to default that cannot be inherited from parent.
1445 This is especially important for all those fields that can be set from
1446 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1447 calls 'free()' for all already set token values which requires that no
1448 other structure's pointer may point to them!
1451 ldi->filename = NULL;
1452 ldi->fullpath = NULL;
1453 ldi->basepath = NULL;
1454 ldi->identifier = NULL;
1455 ldi->name = getStringCopy(ANONYMOUS_NAME);
1456 ldi->name_sorting = NULL;
1457 ldi->author = getStringCopy(parent->author);
1458 ldi->imported_from = getStringCopy(parent->imported_from);
1460 ldi->level_group = FALSE;
1461 ldi->parent_link = FALSE;
1463 ldi->node_top = parent->node_top;
1464 ldi->node_parent = parent;
1465 ldi->node_group = NULL;
1469 void setSetupInfo(struct TokenInfo *token_info,
1470 int token_nr, char *token_value)
1472 int token_type = token_info[token_nr].type;
1473 void *setup_value = token_info[token_nr].value;
1475 if (token_value == NULL)
1478 /* set setup field to corresponding token value */
1483 *(boolean *)setup_value = get_boolean_from_string(token_value);
1487 *(Key *)setup_value = getKeyFromKeyName(token_value);
1491 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1495 *(int *)setup_value = get_integer_from_string(token_value);
1499 if (*(char **)setup_value != NULL)
1500 free(*(char **)setup_value);
1501 *(char **)setup_value = getStringCopy(token_value);
1509 static int compareTreeInfoEntries(const void *object1, const void *object2)
1511 const TreeInfo *entry1 = *((TreeInfo **)object1);
1512 const TreeInfo *entry2 = *((TreeInfo **)object2);
1513 int class_sorting1, class_sorting2;
1516 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1518 class_sorting1 = LEVELSORTING(entry1);
1519 class_sorting2 = LEVELSORTING(entry2);
1523 class_sorting1 = ARTWORKSORTING(entry1);
1524 class_sorting2 = ARTWORKSORTING(entry2);
1527 if (entry1->parent_link || entry2->parent_link)
1528 compare_result = (entry1->parent_link ? -1 : +1);
1529 else if (entry1->sort_priority == entry2->sort_priority)
1531 char *name1 = getStringToLower(entry1->name_sorting);
1532 char *name2 = getStringToLower(entry2->name_sorting);
1534 compare_result = strcmp(name1, name2);
1539 else if (class_sorting1 == class_sorting2)
1540 compare_result = entry1->sort_priority - entry2->sort_priority;
1542 compare_result = class_sorting1 - class_sorting2;
1544 return compare_result;
1547 static void createParentTreeInfoNode(TreeInfo *node_parent)
1551 if (node_parent == NULL)
1554 ti_new = newTreeInfo();
1555 setTreeInfoToDefaults(ti_new, node_parent->type);
1557 ti_new->node_parent = node_parent;
1558 ti_new->parent_link = TRUE;
1560 ti_new->identifier = getStringCopy(node_parent->identifier);
1561 ti_new->name = ".. (parent directory)";
1562 ti_new->name_sorting = getStringCopy(ti_new->name);
1564 ti_new->filename = "..";
1565 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1567 ti_new->sort_priority = node_parent->sort_priority;
1568 ti_new->class_desc = getLevelClassDescription(ti_new);
1570 pushTreeInfo(&node_parent->node_group, ti_new);
1573 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1574 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1576 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1577 TreeInfo *node_parent,
1578 char *level_directory,
1579 char *directory_name)
1581 char *directory_path = getPath2(level_directory, directory_name);
1582 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1583 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1584 LevelDirTree *leveldir_new = NULL;
1587 if (setup_file_hash == NULL)
1589 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1591 free(directory_path);
1597 leveldir_new = newTreeInfo();
1600 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1602 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1604 leveldir_new->filename = getStringCopy(directory_name);
1606 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1608 /* set all structure fields according to the token/value pairs */
1609 ldi = *leveldir_new;
1610 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1611 setSetupInfo(levelinfo_tokens, i,
1612 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1613 *leveldir_new = ldi;
1615 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1617 free(leveldir_new->name);
1618 leveldir_new->name = getStringCopy(leveldir_new->filename);
1621 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1623 if (leveldir_new->identifier == NULL)
1624 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1626 if (leveldir_new->name_sorting == NULL)
1627 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1629 if (node_parent == NULL) /* top level group */
1631 leveldir_new->basepath = level_directory;
1632 leveldir_new->fullpath = leveldir_new->filename;
1634 else /* sub level group */
1636 leveldir_new->basepath = node_parent->basepath;
1637 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1640 if (leveldir_new->levels < 1)
1641 leveldir_new->levels = 1;
1643 leveldir_new->last_level =
1644 leveldir_new->first_level + leveldir_new->levels - 1;
1646 leveldir_new->user_defined =
1647 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1649 leveldir_new->color = LEVELCOLOR(leveldir_new);
1650 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1652 leveldir_new->handicap_level = /* set handicap to default value */
1653 (leveldir_new->user_defined ?
1654 leveldir_new->last_level :
1655 leveldir_new->first_level);
1657 pushTreeInfo(node_first, leveldir_new);
1659 freeSetupFileHash(setup_file_hash);
1661 if (leveldir_new->level_group)
1663 /* create node to link back to current level directory */
1664 createParentTreeInfoNode(leveldir_new);
1666 /* step into sub-directory and look for more level series */
1667 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1668 leveldir_new, directory_path);
1671 free(directory_path);
1677 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1678 TreeInfo *node_parent,
1679 char *level_directory)
1682 struct dirent *dir_entry;
1683 boolean valid_entry_found = FALSE;
1685 if ((dir = opendir(level_directory)) == NULL)
1687 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1691 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1693 struct stat file_status;
1694 char *directory_name = dir_entry->d_name;
1695 char *directory_path = getPath2(level_directory, directory_name);
1697 /* skip entries for current and parent directory */
1698 if (strcmp(directory_name, ".") == 0 ||
1699 strcmp(directory_name, "..") == 0)
1701 free(directory_path);
1705 /* find out if directory entry is itself a directory */
1706 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1707 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1709 free(directory_path);
1713 free(directory_path);
1715 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1716 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1717 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1720 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1727 if (!valid_entry_found)
1729 /* check if this directory directly contains a file "levelinfo.conf" */
1730 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1731 level_directory, ".");
1734 if (!valid_entry_found)
1735 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1739 void LoadLevelInfo()
1741 InitUserLevelDirectory(getLoginName());
1743 DrawInitText("Loading level series:", 120, FC_GREEN);
1745 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1746 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1748 /* before sorting, the first entries will be from the user directory */
1749 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1751 if (leveldir_first == NULL)
1752 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1754 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1757 dumpTreeInfo(leveldir_first, 0);
1761 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1762 TreeInfo *node_parent,
1763 char *base_directory,
1764 char *directory_name, int type)
1766 char *directory_path = getPath2(base_directory, directory_name);
1767 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1768 SetupFileHash *setup_file_hash = NULL;
1769 TreeInfo *artwork_new = NULL;
1772 if (access(filename, F_OK) == 0) /* file exists */
1773 setup_file_hash = loadSetupFileHash(filename);
1775 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1778 struct dirent *dir_entry;
1779 boolean valid_file_found = FALSE;
1781 if ((dir = opendir(directory_path)) != NULL)
1783 while ((dir_entry = readdir(dir)) != NULL)
1785 char *entry_name = dir_entry->d_name;
1787 if (FileIsArtworkType(entry_name, type))
1789 valid_file_found = TRUE;
1797 if (!valid_file_found)
1799 if (strcmp(directory_name, ".") != 0)
1800 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1802 free(directory_path);
1809 artwork_new = newTreeInfo();
1812 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1814 setTreeInfoToDefaults(artwork_new, type);
1816 artwork_new->filename = getStringCopy(directory_name);
1818 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1821 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1824 /* set all structure fields according to the token/value pairs */
1826 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1827 setSetupInfo(levelinfo_tokens, i,
1828 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1831 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1833 free(artwork_new->name);
1834 artwork_new->name = getStringCopy(artwork_new->filename);
1838 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1841 if (artwork_new->identifier == NULL)
1842 artwork_new->identifier = getStringCopy(artwork_new->filename);
1844 if (artwork_new->name_sorting == NULL)
1845 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1848 if (node_parent == NULL) /* top level group */
1850 artwork_new->basepath = getStringCopy(base_directory);
1851 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1853 else /* sub level group */
1855 artwork_new->basepath = getStringCopy(node_parent->basepath);
1856 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1859 artwork_new->user_defined =
1860 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1862 /* (may use ".sort_priority" from "setup_file_hash" above) */
1863 artwork_new->color = ARTWORKCOLOR(artwork_new);
1864 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1866 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1868 if (artwork_new->name != NULL)
1869 free(artwork_new->name);
1871 if (strcmp(artwork_new->filename, ".") == 0)
1873 if (artwork_new->user_defined)
1875 artwork_new->identifier = getStringCopy("private");
1876 artwork_new->sort_priority = ARTWORKCLASS_USER;
1880 artwork_new->identifier = getStringCopy("classic");
1881 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1884 /* set to new values after changing ".sort_priority" */
1885 artwork_new->color = ARTWORKCOLOR(artwork_new);
1886 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1890 artwork_new->identifier = getStringCopy(artwork_new->filename);
1893 artwork_new->name = getStringCopy(artwork_new->identifier);
1894 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1897 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1899 pushTreeInfo(node_first, artwork_new);
1901 freeSetupFileHash(setup_file_hash);
1903 free(directory_path);
1909 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1910 TreeInfo *node_parent,
1911 char *base_directory, int type)
1914 struct dirent *dir_entry;
1915 boolean valid_entry_found = FALSE;
1917 if ((dir = opendir(base_directory)) == NULL)
1919 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1920 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1924 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1926 struct stat file_status;
1927 char *directory_name = dir_entry->d_name;
1928 char *directory_path = getPath2(base_directory, directory_name);
1930 /* skip entries for current and parent directory */
1931 if (strcmp(directory_name, ".") == 0 ||
1932 strcmp(directory_name, "..") == 0)
1934 free(directory_path);
1938 /* find out if directory entry is itself a directory */
1939 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1940 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1942 free(directory_path);
1946 free(directory_path);
1948 /* check if this directory contains artwork with or without config file */
1949 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1951 directory_name, type);
1956 /* check if this directory directly contains artwork itself */
1957 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1958 base_directory, ".",
1960 if (!valid_entry_found)
1961 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1965 static TreeInfo *getDummyArtworkInfo(int type)
1967 /* this is only needed when there is completely no artwork available */
1968 TreeInfo *artwork_new = newTreeInfo();
1970 setTreeInfoToDefaults(artwork_new, type);
1972 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1973 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1974 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1976 if (artwork_new->name != NULL)
1977 free(artwork_new->name);
1979 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1980 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1981 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1986 void LoadArtworkInfo()
1988 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1990 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1991 options.graphics_directory,
1992 TREE_TYPE_GRAPHICS_DIR);
1993 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1994 getUserGraphicsDir(),
1995 TREE_TYPE_GRAPHICS_DIR);
1997 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1998 options.sounds_directory,
1999 TREE_TYPE_SOUNDS_DIR);
2000 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2002 TREE_TYPE_SOUNDS_DIR);
2004 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2005 options.music_directory,
2006 TREE_TYPE_MUSIC_DIR);
2007 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2009 TREE_TYPE_MUSIC_DIR);
2011 if (artwork.gfx_first == NULL)
2012 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2013 if (artwork.snd_first == NULL)
2014 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2015 if (artwork.mus_first == NULL)
2016 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2018 /* before sorting, the first entries will be from the user directory */
2019 artwork.gfx_current =
2020 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2021 if (artwork.gfx_current == NULL)
2022 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2024 artwork.snd_current =
2025 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2026 if (artwork.snd_current == NULL)
2027 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2029 artwork.mus_current =
2030 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2031 if (artwork.mus_current == NULL)
2032 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2034 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2035 artwork.snd_current_identifier = artwork.snd_current->identifier;
2036 artwork.mus_current_identifier = artwork.mus_current->identifier;
2039 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2040 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2041 printf("music set == %s\n\n", artwork.mus_current_identifier);
2044 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2045 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2046 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2049 dumpTreeInfo(artwork.gfx_first, 0);
2050 dumpTreeInfo(artwork.snd_first, 0);
2051 dumpTreeInfo(artwork.mus_first, 0);
2055 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2056 LevelDirTree *level_node)
2058 /* recursively check all level directories for artwork sub-directories */
2062 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2063 ARTWORK_DIRECTORY((*artwork_node)->type));
2066 if (!level_node->parent_link)
2067 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2068 level_node->filename, level_node->name);
2071 if (!level_node->parent_link)
2073 TreeInfo *topnode_last = *artwork_node;
2075 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2076 (*artwork_node)->type);
2078 if (topnode_last != *artwork_node)
2080 free((*artwork_node)->identifier);
2081 free((*artwork_node)->name);
2082 free((*artwork_node)->name_sorting);
2084 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2085 (*artwork_node)->name = getStringCopy(level_node->name);
2086 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2088 (*artwork_node)->sort_priority = level_node->sort_priority;
2089 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2095 if (level_node->node_group != NULL)
2096 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2098 level_node = level_node->next;
2102 void LoadLevelArtworkInfo()
2104 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2106 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2107 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2108 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2110 /* needed for reloading level artwork not known at ealier stage */
2111 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2113 artwork.gfx_current =
2114 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2115 if (artwork.gfx_current == NULL)
2116 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2119 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2121 artwork.snd_current =
2122 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2123 if (artwork.snd_current == NULL)
2124 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2127 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2129 artwork.mus_current =
2130 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2131 if (artwork.mus_current == NULL)
2132 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2135 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2136 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2137 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2140 dumpTreeInfo(artwork.gfx_first, 0);
2141 dumpTreeInfo(artwork.snd_first, 0);
2142 dumpTreeInfo(artwork.mus_first, 0);
2146 static void SaveUserLevelInfo()
2152 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2154 if (!(file = fopen(filename, MODE_WRITE)))
2156 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2161 /* always start with reliable default values */
2162 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2164 ldi.name = getStringCopy(getLoginName());
2165 ldi.author = getStringCopy(getRealName());
2167 ldi.first_level = 1;
2168 ldi.sort_priority = LEVELCLASS_USER_START;
2169 ldi.readonly = FALSE;
2170 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2171 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2172 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2174 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2175 getCookie("LEVELINFO")));
2177 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2178 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2179 i != LEVELINFO_TOKEN_NAME_SORTING &&
2180 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2181 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2186 SetFilePermissions(filename, PERMS_PRIVATE);
2189 char *getSetupValue(int type, void *value)
2191 static char value_string[MAX_LINE_LEN];
2199 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2203 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2207 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2211 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2215 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2219 sprintf(value_string, "%d", *(int *)value);
2223 strcpy(value_string, *(char **)value);
2227 value_string[0] = '\0';
2231 return value_string;
2234 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2238 static char token_string[MAX_LINE_LEN];
2239 int token_type = token_info[token_nr].type;
2240 void *setup_value = token_info[token_nr].value;
2241 char *token_text = token_info[token_nr].text;
2242 char *value_string = getSetupValue(token_type, setup_value);
2244 /* build complete token string */
2245 sprintf(token_string, "%s%s", prefix, token_text);
2247 /* build setup entry line */
2248 line = getFormattedSetupEntry(token_string, value_string);
2250 if (token_type == TYPE_KEY_X11)
2252 Key key = *(Key *)setup_value;
2253 char *keyname = getKeyNameFromKey(key);
2255 /* add comment, if useful */
2256 if (strcmp(keyname, "(undefined)") != 0 &&
2257 strcmp(keyname, "(unknown)") != 0)
2259 /* add at least one whitespace */
2261 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2265 strcat(line, keyname);
2272 void LoadLevelSetup_LastSeries()
2275 SetupFileHash *level_setup_hash = NULL;
2277 /* always start with reliable default values */
2278 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2280 /* ----------------------------------------------------------------------- */
2281 /* ~/.<program>/levelsetup.conf */
2282 /* ----------------------------------------------------------------------- */
2284 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2286 if ((level_setup_hash = loadSetupFileHash(filename)))
2288 char *last_level_series =
2289 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2291 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2293 if (leveldir_current == NULL)
2294 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2296 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2298 freeSetupFileHash(level_setup_hash);
2301 Error(ERR_WARN, "using default setup values");
2306 void SaveLevelSetup_LastSeries()
2309 char *level_subdir = leveldir_current->filename;
2312 /* ----------------------------------------------------------------------- */
2313 /* ~/.<program>/levelsetup.conf */
2314 /* ----------------------------------------------------------------------- */
2316 InitUserDataDirectory();
2318 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2320 if (!(file = fopen(filename, MODE_WRITE)))
2322 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2327 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2328 getCookie("LEVELSETUP")));
2329 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2335 SetFilePermissions(filename, PERMS_PRIVATE);
2338 static void checkSeriesInfo()
2340 static char *level_directory = NULL;
2342 struct dirent *dir_entry;
2344 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2346 level_directory = getPath2((leveldir_current->user_defined ?
2347 getUserLevelDir(NULL) :
2348 options.level_directory),
2349 leveldir_current->fullpath);
2351 if ((dir = opendir(level_directory)) == NULL)
2353 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2357 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2359 if (strlen(dir_entry->d_name) > 4 &&
2360 dir_entry->d_name[3] == '.' &&
2361 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2363 char levelnum_str[4];
2366 strncpy(levelnum_str, dir_entry->d_name, 3);
2367 levelnum_str[3] = '\0';
2369 levelnum_value = atoi(levelnum_str);
2372 if (levelnum_value < leveldir_current->first_level)
2374 Error(ERR_WARN, "additional level %d found", levelnum_value);
2375 leveldir_current->first_level = levelnum_value;
2377 else if (levelnum_value > leveldir_current->last_level)
2379 Error(ERR_WARN, "additional level %d found", levelnum_value);
2380 leveldir_current->last_level = levelnum_value;
2389 void LoadLevelSetup_SeriesInfo()
2392 SetupFileHash *level_setup_hash = NULL;
2393 char *level_subdir = leveldir_current->filename;
2395 /* always start with reliable default values */
2396 level_nr = leveldir_current->first_level;
2398 checkSeriesInfo(leveldir_current);
2400 /* ----------------------------------------------------------------------- */
2401 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2402 /* ----------------------------------------------------------------------- */
2404 level_subdir = leveldir_current->filename;
2406 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2408 if ((level_setup_hash = loadSetupFileHash(filename)))
2412 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2416 level_nr = atoi(token_value);
2418 if (level_nr < leveldir_current->first_level)
2419 level_nr = leveldir_current->first_level;
2420 if (level_nr > leveldir_current->last_level)
2421 level_nr = leveldir_current->last_level;
2424 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2428 int level_nr = atoi(token_value);
2430 if (level_nr < leveldir_current->first_level)
2431 level_nr = leveldir_current->first_level;
2432 if (level_nr > leveldir_current->last_level + 1)
2433 level_nr = leveldir_current->last_level;
2435 if (leveldir_current->user_defined)
2436 level_nr = leveldir_current->last_level;
2438 leveldir_current->handicap_level = level_nr;
2441 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2443 freeSetupFileHash(level_setup_hash);
2446 Error(ERR_WARN, "using default setup values");
2451 void SaveLevelSetup_SeriesInfo()
2454 char *level_subdir = leveldir_current->filename;
2455 char *level_nr_str = int2str(level_nr, 0);
2456 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2459 /* ----------------------------------------------------------------------- */
2460 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2461 /* ----------------------------------------------------------------------- */
2463 InitLevelSetupDirectory(level_subdir);
2465 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2467 if (!(file = fopen(filename, MODE_WRITE)))
2469 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2474 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2475 getCookie("LEVELSETUP")));
2476 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2478 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2479 handicap_level_str));
2484 SetFilePermissions(filename, PERMS_PRIVATE);