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)
365 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
367 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
369 filename = getPath2(getCurrentLevelDir(), basename);
374 char *getTapeFilename(int nr)
376 static char *filename = NULL;
377 char basename[MAX_FILENAME_LEN];
379 if (filename != NULL)
382 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
383 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
388 char *getScoreFilename(int nr)
390 static char *filename = NULL;
391 char basename[MAX_FILENAME_LEN];
393 if (filename != NULL)
396 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
397 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
402 char *getSetupFilename()
404 static char *filename = NULL;
406 if (filename != NULL)
409 filename = getPath2(getSetupDir(), SETUP_FILENAME);
414 static char *getCorrectedImageBasename(char *basename)
416 char *basename_corrected = basename;
418 #if defined(PLATFORM_MSDOS)
419 if (program.filename_prefix != NULL)
421 int prefix_len = strlen(program.filename_prefix);
423 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
424 basename_corrected = &basename[prefix_len];
426 /* if corrected filename is still longer than standard MS-DOS filename
427 size (8 characters + 1 dot + 3 characters file extension), shorten
428 filename by writing file extension after 8th basename character */
429 if (strlen(basename_corrected) > 8+1+3)
431 static char *msdos_filename = NULL;
433 if (msdos_filename != NULL)
434 free(msdos_filename);
436 msdos_filename = getStringCopy(basename_corrected);
437 strncpy(&msdos_filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
442 return basename_corrected;
445 char *getCustomImageFilename(char *basename)
447 static char *filename = NULL;
449 if (filename != NULL)
452 basename = getCorrectedImageBasename(basename);
454 if (!setup.override_level_graphics)
456 /* 1st try: look for special artwork configured in level series config */
457 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
459 if (strcmp(basename, "RocksScreen.pcx") == 0)
460 printf("::: check 1: filename '%s'\n", filename);
462 if (fileExists(filename))
467 /* 2nd try: look for special artwork in current level series directory */
468 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
470 if (strcmp(basename, "RocksScreen.pcx") == 0)
471 printf("::: check 2: filename '%s'\n", filename);
473 if (fileExists(filename))
479 /* 3rd try: look for special artwork in configured artwork directory */
480 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
482 if (strcmp(basename, "RocksScreen.pcx") == 0)
483 printf("::: check 3: filename '%s'\n", filename);
485 if (fileExists(filename))
490 /* 4th try: look for default artwork in new default artwork directory */
491 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
493 if (strcmp(basename, "RocksScreen.pcx") == 0)
494 printf("::: check 4: filename '%s'\n", filename);
496 if (fileExists(filename))
501 /* 5th try: look for default artwork in old default artwork directory */
502 filename = getPath2(options.graphics_directory, basename);
504 if (strcmp(basename, "RocksScreen.pcx") == 0)
505 printf("::: check 5: filename '%s'\n", filename);
507 if (fileExists(filename))
510 return NULL; /* cannot find specified artwork file anywhere */
513 char *getCustomSoundFilename(char *basename)
515 static char *filename = NULL;
517 if (filename != NULL)
520 if (!setup.override_level_sounds)
522 /* 1st try: look for special artwork configured in level series config */
523 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
524 if (fileExists(filename))
529 /* 2nd try: look for special artwork in current level series directory */
530 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
531 if (fileExists(filename))
537 /* 3rd try: look for special artwork in configured artwork directory */
538 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
539 if (fileExists(filename))
544 /* 4th try: look for default artwork in new default artwork directory */
545 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
546 if (fileExists(filename))
551 /* 5th try: look for default artwork in old default artwork directory */
552 filename = getPath2(options.sounds_directory, basename);
553 if (fileExists(filename))
556 return NULL; /* cannot find specified artwork file anywhere */
559 char *getCustomArtworkFilename(char *basename, int type)
561 if (type == ARTWORK_TYPE_GRAPHICS)
562 return getCustomImageFilename(basename);
563 else if (type == ARTWORK_TYPE_SOUNDS)
564 return getCustomSoundFilename(basename);
566 return UNDEFINED_FILENAME;
569 char *getCustomArtworkConfigFilename(int type)
571 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
574 char *getCustomMusicDirectory(void)
576 static char *directory = NULL;
578 if (directory != NULL)
581 if (!setup.override_level_music)
583 /* 1st try: look for special artwork configured in level series config */
584 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
585 if (fileExists(directory))
590 /* 2nd try: look for special artwork in current level series directory */
591 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
592 if (fileExists(directory))
598 /* 3rd try: look for special artwork in configured artwork directory */
599 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
600 if (fileExists(directory))
605 /* 4th try: look for default artwork in new default artwork directory */
606 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
607 if (fileExists(directory))
612 /* 5th try: look for default artwork in old default artwork directory */
613 directory = getStringCopy(options.music_directory);
614 if (fileExists(directory))
617 return NULL; /* cannot find specified artwork file anywhere */
620 void InitTapeDirectory(char *level_subdir)
622 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
623 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
624 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
627 void InitScoreDirectory(char *level_subdir)
629 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
630 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
631 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
634 static void SaveUserLevelInfo();
636 void InitUserLevelDirectory(char *level_subdir)
638 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
640 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
641 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
642 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
648 void InitLevelSetupDirectory(char *level_subdir)
650 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
651 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
652 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
656 /* ------------------------------------------------------------------------- */
657 /* some functions to handle lists of level directories */
658 /* ------------------------------------------------------------------------- */
660 TreeInfo *newTreeInfo()
662 return checked_calloc(sizeof(TreeInfo));
665 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
667 node_new->next = *node_first;
668 *node_first = node_new;
671 int numTreeInfo(TreeInfo *node)
684 boolean validLevelSeries(TreeInfo *node)
686 return (node != NULL && !node->node_group && !node->parent_link);
689 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
694 if (node->node_group) /* enter level group (step down into tree) */
695 return getFirstValidTreeInfoEntry(node->node_group);
696 else if (node->parent_link) /* skip start entry of level group */
698 if (node->next) /* get first real level series entry */
699 return getFirstValidTreeInfoEntry(node->next);
700 else /* leave empty level group and go on */
701 return getFirstValidTreeInfoEntry(node->node_parent->next);
703 else /* this seems to be a regular level series */
707 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
712 if (node->node_parent == NULL) /* top level group */
713 return *node->node_top;
714 else /* sub level group */
715 return node->node_parent->node_group;
718 int numTreeInfoInGroup(TreeInfo *node)
720 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
723 int posTreeInfo(TreeInfo *node)
725 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
730 if (node_cmp == node)
734 node_cmp = node_cmp->next;
740 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
742 TreeInfo *node_default = node;
757 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
759 if (identifier == NULL)
764 if (node->node_group)
766 TreeInfo *node_group;
768 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
773 else if (!node->parent_link)
775 if (strcmp(identifier, node->identifier) == 0)
785 void dumpTreeInfo(TreeInfo *node, int depth)
789 printf("Dumping TreeInfo:\n");
793 for (i=0; i<(depth + 1) * 3; i++)
796 printf("filename == '%s' (%s) [%s] (%d)\n",
797 node->filename, node->name, node->identifier, node->sort_priority);
799 if (node->node_group != NULL)
800 dumpTreeInfo(node->node_group, depth + 1);
806 void sortTreeInfo(TreeInfo **node_first,
807 int (*compare_function)(const void *, const void *))
809 int num_nodes = numTreeInfo(*node_first);
810 TreeInfo **sort_array;
811 TreeInfo *node = *node_first;
817 /* allocate array for sorting structure pointers */
818 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
820 /* writing structure pointers to sorting array */
821 while (i < num_nodes && node) /* double boundary check... */
823 sort_array[i] = node;
829 /* sorting the structure pointers in the sorting array */
830 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
833 /* update the linkage of list elements with the sorted node array */
834 for (i=0; i<num_nodes - 1; i++)
835 sort_array[i]->next = sort_array[i + 1];
836 sort_array[num_nodes - 1]->next = NULL;
838 /* update the linkage of the main list anchor pointer */
839 *node_first = sort_array[0];
843 /* now recursively sort the level group structures */
847 if (node->node_group != NULL)
848 sortTreeInfo(&node->node_group, compare_function);
855 /* ========================================================================= */
856 /* some stuff from "files.c" */
857 /* ========================================================================= */
859 #if defined(PLATFORM_WIN32)
861 #define S_IRGRP S_IRUSR
864 #define S_IROTH S_IRUSR
867 #define S_IWGRP S_IWUSR
870 #define S_IWOTH S_IWUSR
873 #define S_IXGRP S_IXUSR
876 #define S_IXOTH S_IXUSR
879 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
884 #endif /* PLATFORM_WIN32 */
886 /* file permissions for newly written files */
887 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
888 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
889 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
891 #define MODE_W_PRIVATE (S_IWUSR)
892 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
893 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
895 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
896 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
898 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
899 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
901 char *getUserDataDir(void)
903 static char *userdata_dir = NULL;
905 if (userdata_dir == NULL)
906 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
911 char *getCommonDataDir(void)
913 static char *common_data_dir = NULL;
915 #if defined(PLATFORM_WIN32)
916 if (common_data_dir == NULL)
918 char *dir = checked_malloc(MAX_PATH + 1);
920 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
921 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
922 common_data_dir = getPath2(dir, program.userdata_directory);
924 common_data_dir = options.rw_base_directory;
927 if (common_data_dir == NULL)
928 common_data_dir = options.rw_base_directory;
931 return common_data_dir;
936 return getUserDataDir();
939 static mode_t posix_umask(mode_t mask)
941 #if defined(PLATFORM_UNIX)
948 static int posix_mkdir(const char *pathname, mode_t mode)
950 #if defined(PLATFORM_WIN32)
951 return mkdir(pathname);
953 return mkdir(pathname, mode);
957 void createDirectory(char *dir, char *text, int permission_class)
959 /* leave "other" permissions in umask untouched, but ensure group parts
960 of USERDATA_DIR_MODE are not masked */
961 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
962 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
963 mode_t normal_umask = posix_umask(0);
964 mode_t group_umask = ~(dir_mode & S_IRWXG);
965 posix_umask(normal_umask & group_umask);
967 if (access(dir, F_OK) != 0)
968 if (posix_mkdir(dir, dir_mode) != 0)
969 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
971 posix_umask(normal_umask); /* reset normal umask */
974 void InitUserDataDirectory()
976 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
979 void SetFilePermissions(char *filename, int permission_class)
981 chmod(filename, (permission_class == PERMS_PRIVATE ?
982 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
985 char *getCookie(char *file_type)
987 static char cookie[MAX_COOKIE_LEN + 1];
989 if (strlen(program.cookie_prefix) + 1 +
990 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
991 return "[COOKIE ERROR]"; /* should never happen */
993 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
994 program.cookie_prefix, file_type,
995 program.version_major, program.version_minor);
1000 int getFileVersionFromCookieString(const char *cookie)
1002 const char *ptr_cookie1, *ptr_cookie2;
1003 const char *pattern1 = "_FILE_VERSION_";
1004 const char *pattern2 = "?.?";
1005 const int len_cookie = strlen(cookie);
1006 const int len_pattern1 = strlen(pattern1);
1007 const int len_pattern2 = strlen(pattern2);
1008 const int len_pattern = len_pattern1 + len_pattern2;
1009 int version_major, version_minor;
1011 if (len_cookie <= len_pattern)
1014 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1015 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1017 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1020 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1021 ptr_cookie2[1] != '.' ||
1022 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1025 version_major = ptr_cookie2[0] - '0';
1026 version_minor = ptr_cookie2[2] - '0';
1028 return VERSION_IDENT(version_major, version_minor, 0);
1031 boolean checkCookieString(const char *cookie, const char *template)
1033 const char *pattern = "_FILE_VERSION_?.?";
1034 const int len_cookie = strlen(cookie);
1035 const int len_template = strlen(template);
1036 const int len_pattern = strlen(pattern);
1038 if (len_cookie != len_template)
1041 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1047 /* ------------------------------------------------------------------------- */
1048 /* setup file list and hash handling functions */
1049 /* ------------------------------------------------------------------------- */
1051 char *getFormattedSetupEntry(char *token, char *value)
1054 static char entry[MAX_LINE_LEN];
1056 /* start with the token and some spaces to format output line */
1057 sprintf(entry, "%s:", token);
1058 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1061 /* continue with the token's value */
1062 strcat(entry, value);
1067 SetupFileList *newSetupFileList(char *token, char *value)
1069 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1071 new->token = getStringCopy(token);
1072 new->value = getStringCopy(value);
1079 void freeSetupFileList(SetupFileList *list)
1089 freeSetupFileList(list->next);
1093 char *getListEntry(SetupFileList *list, char *token)
1098 if (strcmp(list->token, token) == 0)
1101 return getListEntry(list->next, token);
1104 void setListEntry(SetupFileList *list, char *token, char *value)
1109 if (strcmp(list->token, token) == 0)
1114 list->value = getStringCopy(value);
1116 else if (list->next == NULL)
1117 list->next = newSetupFileList(token, value);
1119 setListEntry(list->next, token, value);
1123 static void printSetupFileList(SetupFileList *list)
1128 printf("token: '%s'\n", list->token);
1129 printf("value: '%s'\n", list->value);
1131 printSetupFileList(list->next);
1136 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1137 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1138 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1139 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1141 #define insert_hash_entry hashtable_insert
1142 #define search_hash_entry hashtable_search
1143 #define change_hash_entry hashtable_change
1144 #define remove_hash_entry hashtable_remove
1147 static unsigned int get_hash_from_key(void *key)
1152 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1153 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1154 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1155 it works better than many other constants, prime or not) has never been
1156 adequately explained.
1158 If you just want to have a good hash function, and cannot wait, djb2
1159 is one of the best string hash functions i know. It has excellent
1160 distribution and speed on many different sets of keys and table sizes.
1161 You are not likely to do better with one of the "well known" functions
1162 such as PJW, K&R, etc.
1164 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1167 char *str = (char *)key;
1168 unsigned int hash = 5381;
1171 while ((c = *str++))
1172 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1177 static int keys_are_equal(void *key1, void *key2)
1179 return (strcmp((char *)key1, (char *)key2) == 0);
1182 SetupFileHash *newSetupFileHash()
1184 SetupFileHash *new_hash =
1185 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1190 void freeSetupFileHash(SetupFileHash *hash)
1195 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1198 char *getHashEntry(SetupFileHash *hash, char *token)
1203 return search_hash_entry(hash, token);
1206 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1213 value_copy = getStringCopy(value);
1215 /* change value; if it does not exist, insert it as new */
1216 if (!change_hash_entry(hash, token, value_copy))
1217 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1218 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1223 static void printSetupFileHash(SetupFileHash *hash)
1225 BEGIN_HASH_ITERATION(hash, itr)
1227 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1228 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1230 END_HASH_ITERATION(hash, itr)
1235 static void *loadSetupFileData(char *filename, boolean use_hash)
1238 char line[MAX_LINE_LEN];
1239 char *token, *value, *line_ptr;
1240 void *setup_file_data;
1244 setup_file_data = newSetupFileHash();
1246 setup_file_data = newSetupFileList("", "");
1248 if (!(file = fopen(filename, MODE_READ)))
1250 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1256 /* read next line of input file */
1257 if (!fgets(line, MAX_LINE_LEN, file))
1260 /* cut trailing comment or whitespace from input line */
1261 for (line_ptr = line; *line_ptr; line_ptr++)
1263 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1270 /* cut trailing whitespaces from input line */
1271 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1272 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1275 /* ignore empty lines */
1279 line_len = strlen(line);
1281 /* cut leading whitespaces from token */
1282 for (token = line; *token; token++)
1283 if (*token != ' ' && *token != '\t')
1286 /* find end of token */
1287 for (line_ptr = token; *line_ptr; line_ptr++)
1289 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1296 if (line_ptr < line + line_len)
1297 value = line_ptr + 1;
1301 /* cut leading whitespaces from value */
1302 for (; *value; value++)
1303 if (*value != ' ' && *value != '\t')
1306 if (*token && *value)
1309 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1311 setListEntry((SetupFileList *)setup_file_data, token, value);
1319 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1320 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1324 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1325 SetupFileList *first_valid_list_entry = setup_file_list->next;
1327 /* free empty list header */
1328 setup_file_list->next = NULL;
1329 freeSetupFileList(setup_file_list);
1330 setup_file_data = first_valid_list_entry;
1332 if (first_valid_list_entry == NULL)
1333 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1336 return setup_file_data;
1339 SetupFileList *loadSetupFileList(char *filename)
1341 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1344 SetupFileHash *loadSetupFileHash(char *filename)
1346 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1349 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1352 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1355 Error(ERR_WARN, "configuration file has no file identifier");
1356 else if (!checkCookieString(value, identifier))
1357 Error(ERR_WARN, "configuration file has wrong file identifier");
1361 /* ========================================================================= */
1362 /* setup file stuff */
1363 /* ========================================================================= */
1365 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1366 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1367 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1369 /* level directory info */
1370 #define LEVELINFO_TOKEN_IDENTIFIER 0
1371 #define LEVELINFO_TOKEN_NAME 1
1372 #define LEVELINFO_TOKEN_NAME_SORTING 2
1373 #define LEVELINFO_TOKEN_AUTHOR 3
1374 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1375 #define LEVELINFO_TOKEN_LEVELS 5
1376 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1377 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1378 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1379 #define LEVELINFO_TOKEN_READONLY 9
1380 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1381 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1382 #define LEVELINFO_TOKEN_MUSIC_SET 12
1384 #define NUM_LEVELINFO_TOKENS 13
1386 static LevelDirTree ldi;
1388 static struct TokenInfo levelinfo_tokens[] =
1390 /* level directory info */
1391 { TYPE_STRING, &ldi.identifier, "identifier" },
1392 { TYPE_STRING, &ldi.name, "name" },
1393 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1394 { TYPE_STRING, &ldi.author, "author" },
1395 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1396 { TYPE_INTEGER, &ldi.levels, "levels" },
1397 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1398 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1399 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1400 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1401 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1402 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1403 { TYPE_STRING, &ldi.music_set, "music_set" }
1406 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1410 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1411 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1412 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1413 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1416 ldi->node_parent = NULL;
1417 ldi->node_group = NULL;
1421 ldi->cl_cursor = -1;
1423 ldi->filename = NULL;
1424 ldi->fullpath = NULL;
1425 ldi->basepath = NULL;
1426 ldi->identifier = NULL;
1427 ldi->name = getStringCopy(ANONYMOUS_NAME);
1428 ldi->name_sorting = NULL;
1429 ldi->author = getStringCopy(ANONYMOUS_NAME);
1431 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1432 ldi->parent_link = FALSE;
1433 ldi->user_defined = FALSE;
1435 ldi->class_desc = NULL;
1437 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1439 ldi->imported_from = NULL;
1440 ldi->graphics_set = NULL;
1441 ldi->sounds_set = NULL;
1442 ldi->music_set = NULL;
1443 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1444 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1445 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1447 ldi->first_level = 0;
1448 ldi->last_level = 0;
1449 ldi->level_group = FALSE;
1450 ldi->handicap_level = 0;
1451 ldi->readonly = TRUE;
1455 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1459 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1461 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1465 /* first copy all values from the parent structure ... */
1468 /* ... then set all fields to default that cannot be inherited from parent.
1469 This is especially important for all those fields that can be set from
1470 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1471 calls 'free()' for all already set token values which requires that no
1472 other structure's pointer may point to them!
1475 ldi->filename = NULL;
1476 ldi->fullpath = NULL;
1477 ldi->basepath = NULL;
1478 ldi->identifier = NULL;
1479 ldi->name = getStringCopy(ANONYMOUS_NAME);
1480 ldi->name_sorting = NULL;
1481 ldi->author = getStringCopy(parent->author);
1482 ldi->imported_from = getStringCopy(parent->imported_from);
1484 ldi->level_group = FALSE;
1485 ldi->parent_link = FALSE;
1487 ldi->node_top = parent->node_top;
1488 ldi->node_parent = parent;
1489 ldi->node_group = NULL;
1493 void setSetupInfo(struct TokenInfo *token_info,
1494 int token_nr, char *token_value)
1496 int token_type = token_info[token_nr].type;
1497 void *setup_value = token_info[token_nr].value;
1499 if (token_value == NULL)
1502 /* set setup field to corresponding token value */
1507 *(boolean *)setup_value = get_boolean_from_string(token_value);
1511 *(Key *)setup_value = getKeyFromKeyName(token_value);
1515 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1519 *(int *)setup_value = get_integer_from_string(token_value);
1523 if (*(char **)setup_value != NULL)
1524 free(*(char **)setup_value);
1525 *(char **)setup_value = getStringCopy(token_value);
1533 static int compareTreeInfoEntries(const void *object1, const void *object2)
1535 const TreeInfo *entry1 = *((TreeInfo **)object1);
1536 const TreeInfo *entry2 = *((TreeInfo **)object2);
1537 int class_sorting1, class_sorting2;
1540 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1542 class_sorting1 = LEVELSORTING(entry1);
1543 class_sorting2 = LEVELSORTING(entry2);
1547 class_sorting1 = ARTWORKSORTING(entry1);
1548 class_sorting2 = ARTWORKSORTING(entry2);
1551 if (entry1->parent_link || entry2->parent_link)
1552 compare_result = (entry1->parent_link ? -1 : +1);
1553 else if (entry1->sort_priority == entry2->sort_priority)
1555 char *name1 = getStringToLower(entry1->name_sorting);
1556 char *name2 = getStringToLower(entry2->name_sorting);
1558 compare_result = strcmp(name1, name2);
1563 else if (class_sorting1 == class_sorting2)
1564 compare_result = entry1->sort_priority - entry2->sort_priority;
1566 compare_result = class_sorting1 - class_sorting2;
1568 return compare_result;
1571 static void createParentTreeInfoNode(TreeInfo *node_parent)
1575 if (node_parent == NULL)
1578 ti_new = newTreeInfo();
1579 setTreeInfoToDefaults(ti_new, node_parent->type);
1581 ti_new->node_parent = node_parent;
1582 ti_new->parent_link = TRUE;
1584 ti_new->identifier = getStringCopy(node_parent->identifier);
1585 ti_new->name = ".. (parent directory)";
1586 ti_new->name_sorting = getStringCopy(ti_new->name);
1588 ti_new->filename = "..";
1589 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1591 ti_new->sort_priority = node_parent->sort_priority;
1592 ti_new->class_desc = getLevelClassDescription(ti_new);
1594 pushTreeInfo(&node_parent->node_group, ti_new);
1597 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1598 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1600 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1601 TreeInfo *node_parent,
1602 char *level_directory,
1603 char *directory_name)
1605 char *directory_path = getPath2(level_directory, directory_name);
1606 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1607 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1608 LevelDirTree *leveldir_new = NULL;
1611 if (setup_file_hash == NULL)
1613 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1615 free(directory_path);
1621 leveldir_new = newTreeInfo();
1624 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1626 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1628 leveldir_new->filename = getStringCopy(directory_name);
1630 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1632 /* set all structure fields according to the token/value pairs */
1633 ldi = *leveldir_new;
1634 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1635 setSetupInfo(levelinfo_tokens, i,
1636 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1637 *leveldir_new = ldi;
1639 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1641 free(leveldir_new->name);
1642 leveldir_new->name = getStringCopy(leveldir_new->filename);
1645 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1647 if (leveldir_new->identifier == NULL)
1648 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1650 if (leveldir_new->name_sorting == NULL)
1651 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1653 if (node_parent == NULL) /* top level group */
1655 leveldir_new->basepath = level_directory;
1656 leveldir_new->fullpath = leveldir_new->filename;
1658 else /* sub level group */
1660 leveldir_new->basepath = node_parent->basepath;
1661 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1664 if (leveldir_new->levels < 1)
1665 leveldir_new->levels = 1;
1667 leveldir_new->last_level =
1668 leveldir_new->first_level + leveldir_new->levels - 1;
1670 leveldir_new->user_defined =
1671 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1673 leveldir_new->color = LEVELCOLOR(leveldir_new);
1674 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1676 leveldir_new->handicap_level = /* set handicap to default value */
1677 (leveldir_new->user_defined ?
1678 leveldir_new->last_level :
1679 leveldir_new->first_level);
1681 pushTreeInfo(node_first, leveldir_new);
1683 freeSetupFileHash(setup_file_hash);
1685 if (leveldir_new->level_group)
1687 /* create node to link back to current level directory */
1688 createParentTreeInfoNode(leveldir_new);
1690 /* step into sub-directory and look for more level series */
1691 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1692 leveldir_new, directory_path);
1695 free(directory_path);
1701 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1702 TreeInfo *node_parent,
1703 char *level_directory)
1706 struct dirent *dir_entry;
1707 boolean valid_entry_found = FALSE;
1709 if ((dir = opendir(level_directory)) == NULL)
1711 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1715 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1717 struct stat file_status;
1718 char *directory_name = dir_entry->d_name;
1719 char *directory_path = getPath2(level_directory, directory_name);
1721 /* skip entries for current and parent directory */
1722 if (strcmp(directory_name, ".") == 0 ||
1723 strcmp(directory_name, "..") == 0)
1725 free(directory_path);
1729 /* find out if directory entry is itself a directory */
1730 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1731 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1733 free(directory_path);
1737 free(directory_path);
1739 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1740 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1741 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1744 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1751 if (!valid_entry_found)
1753 /* check if this directory directly contains a file "levelinfo.conf" */
1754 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1755 level_directory, ".");
1758 if (!valid_entry_found)
1759 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1763 void LoadLevelInfo()
1765 InitUserLevelDirectory(getLoginName());
1767 DrawInitText("Loading level series:", 120, FC_GREEN);
1769 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1770 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1772 /* before sorting, the first entries will be from the user directory */
1773 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1775 if (leveldir_first == NULL)
1776 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1778 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1781 dumpTreeInfo(leveldir_first, 0);
1785 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1786 TreeInfo *node_parent,
1787 char *base_directory,
1788 char *directory_name, int type)
1790 char *directory_path = getPath2(base_directory, directory_name);
1791 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1792 SetupFileHash *setup_file_hash = NULL;
1793 TreeInfo *artwork_new = NULL;
1796 if (access(filename, F_OK) == 0) /* file exists */
1797 setup_file_hash = loadSetupFileHash(filename);
1799 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1802 struct dirent *dir_entry;
1803 boolean valid_file_found = FALSE;
1805 if ((dir = opendir(directory_path)) != NULL)
1807 while ((dir_entry = readdir(dir)) != NULL)
1809 char *entry_name = dir_entry->d_name;
1811 if (FileIsArtworkType(entry_name, type))
1813 valid_file_found = TRUE;
1821 if (!valid_file_found)
1823 if (strcmp(directory_name, ".") != 0)
1824 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1826 free(directory_path);
1833 artwork_new = newTreeInfo();
1836 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1838 setTreeInfoToDefaults(artwork_new, type);
1840 artwork_new->filename = getStringCopy(directory_name);
1842 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1845 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1848 /* set all structure fields according to the token/value pairs */
1850 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1851 setSetupInfo(levelinfo_tokens, i,
1852 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1855 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1857 free(artwork_new->name);
1858 artwork_new->name = getStringCopy(artwork_new->filename);
1862 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1865 if (artwork_new->identifier == NULL)
1866 artwork_new->identifier = getStringCopy(artwork_new->filename);
1868 if (artwork_new->name_sorting == NULL)
1869 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1872 if (node_parent == NULL) /* top level group */
1874 artwork_new->basepath = getStringCopy(base_directory);
1875 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1877 else /* sub level group */
1879 artwork_new->basepath = getStringCopy(node_parent->basepath);
1880 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1883 artwork_new->user_defined =
1884 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1886 /* (may use ".sort_priority" from "setup_file_hash" above) */
1887 artwork_new->color = ARTWORKCOLOR(artwork_new);
1888 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1890 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1892 if (artwork_new->name != NULL)
1893 free(artwork_new->name);
1895 if (strcmp(artwork_new->filename, ".") == 0)
1897 if (artwork_new->user_defined)
1899 artwork_new->identifier = getStringCopy("private");
1900 artwork_new->sort_priority = ARTWORKCLASS_USER;
1904 artwork_new->identifier = getStringCopy("classic");
1905 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1908 /* set to new values after changing ".sort_priority" */
1909 artwork_new->color = ARTWORKCOLOR(artwork_new);
1910 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1914 artwork_new->identifier = getStringCopy(artwork_new->filename);
1917 artwork_new->name = getStringCopy(artwork_new->identifier);
1918 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1921 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1923 pushTreeInfo(node_first, artwork_new);
1925 freeSetupFileHash(setup_file_hash);
1927 free(directory_path);
1933 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1934 TreeInfo *node_parent,
1935 char *base_directory, int type)
1938 struct dirent *dir_entry;
1939 boolean valid_entry_found = FALSE;
1941 if ((dir = opendir(base_directory)) == NULL)
1943 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1944 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1948 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1950 struct stat file_status;
1951 char *directory_name = dir_entry->d_name;
1952 char *directory_path = getPath2(base_directory, directory_name);
1954 /* skip entries for current and parent directory */
1955 if (strcmp(directory_name, ".") == 0 ||
1956 strcmp(directory_name, "..") == 0)
1958 free(directory_path);
1962 /* find out if directory entry is itself a directory */
1963 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1964 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1966 free(directory_path);
1970 free(directory_path);
1972 /* check if this directory contains artwork with or without config file */
1973 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1975 directory_name, type);
1980 /* check if this directory directly contains artwork itself */
1981 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1982 base_directory, ".",
1984 if (!valid_entry_found)
1985 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1989 static TreeInfo *getDummyArtworkInfo(int type)
1991 /* this is only needed when there is completely no artwork available */
1992 TreeInfo *artwork_new = newTreeInfo();
1994 setTreeInfoToDefaults(artwork_new, type);
1996 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1997 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1998 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2000 if (artwork_new->name != NULL)
2001 free(artwork_new->name);
2003 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2004 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2005 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2010 void LoadArtworkInfo()
2012 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2014 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2015 options.graphics_directory,
2016 TREE_TYPE_GRAPHICS_DIR);
2017 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2018 getUserGraphicsDir(),
2019 TREE_TYPE_GRAPHICS_DIR);
2021 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2022 options.sounds_directory,
2023 TREE_TYPE_SOUNDS_DIR);
2024 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2026 TREE_TYPE_SOUNDS_DIR);
2028 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2029 options.music_directory,
2030 TREE_TYPE_MUSIC_DIR);
2031 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2033 TREE_TYPE_MUSIC_DIR);
2035 if (artwork.gfx_first == NULL)
2036 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2037 if (artwork.snd_first == NULL)
2038 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2039 if (artwork.mus_first == NULL)
2040 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2042 /* before sorting, the first entries will be from the user directory */
2043 artwork.gfx_current =
2044 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2045 if (artwork.gfx_current == NULL)
2046 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2048 artwork.snd_current =
2049 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2050 if (artwork.snd_current == NULL)
2051 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2053 artwork.mus_current =
2054 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2055 if (artwork.mus_current == NULL)
2056 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2058 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2059 artwork.snd_current_identifier = artwork.snd_current->identifier;
2060 artwork.mus_current_identifier = artwork.mus_current->identifier;
2063 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2064 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2065 printf("music set == %s\n\n", artwork.mus_current_identifier);
2068 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2069 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2070 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2073 dumpTreeInfo(artwork.gfx_first, 0);
2074 dumpTreeInfo(artwork.snd_first, 0);
2075 dumpTreeInfo(artwork.mus_first, 0);
2079 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2080 LevelDirTree *level_node)
2082 /* recursively check all level directories for artwork sub-directories */
2086 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2087 ARTWORK_DIRECTORY((*artwork_node)->type));
2090 if (!level_node->parent_link)
2091 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2092 level_node->filename, level_node->name);
2095 if (!level_node->parent_link)
2097 TreeInfo *topnode_last = *artwork_node;
2099 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2100 (*artwork_node)->type);
2102 if (topnode_last != *artwork_node)
2104 free((*artwork_node)->identifier);
2105 free((*artwork_node)->name);
2106 free((*artwork_node)->name_sorting);
2108 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2109 (*artwork_node)->name = getStringCopy(level_node->name);
2110 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2112 (*artwork_node)->sort_priority = level_node->sort_priority;
2113 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2119 if (level_node->node_group != NULL)
2120 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2122 level_node = level_node->next;
2126 void LoadLevelArtworkInfo()
2128 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2130 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2131 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2132 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2134 /* needed for reloading level artwork not known at ealier stage */
2135 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2137 artwork.gfx_current =
2138 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2139 if (artwork.gfx_current == NULL)
2140 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2143 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2145 artwork.snd_current =
2146 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2147 if (artwork.snd_current == NULL)
2148 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2151 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2153 artwork.mus_current =
2154 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2155 if (artwork.mus_current == NULL)
2156 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2159 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2160 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2161 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2164 dumpTreeInfo(artwork.gfx_first, 0);
2165 dumpTreeInfo(artwork.snd_first, 0);
2166 dumpTreeInfo(artwork.mus_first, 0);
2170 static void SaveUserLevelInfo()
2176 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2178 if (!(file = fopen(filename, MODE_WRITE)))
2180 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2185 /* always start with reliable default values */
2186 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2188 ldi.name = getStringCopy(getLoginName());
2189 ldi.author = getStringCopy(getRealName());
2191 ldi.first_level = 1;
2192 ldi.sort_priority = LEVELCLASS_USER_START;
2193 ldi.readonly = FALSE;
2194 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2195 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2196 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2198 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2199 getCookie("LEVELINFO")));
2201 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2202 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2203 i != LEVELINFO_TOKEN_NAME_SORTING &&
2204 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2205 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2210 SetFilePermissions(filename, PERMS_PRIVATE);
2213 char *getSetupValue(int type, void *value)
2215 static char value_string[MAX_LINE_LEN];
2223 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2227 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2231 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2235 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2239 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2243 sprintf(value_string, "%d", *(int *)value);
2247 strcpy(value_string, *(char **)value);
2251 value_string[0] = '\0';
2255 return value_string;
2258 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2262 static char token_string[MAX_LINE_LEN];
2263 int token_type = token_info[token_nr].type;
2264 void *setup_value = token_info[token_nr].value;
2265 char *token_text = token_info[token_nr].text;
2266 char *value_string = getSetupValue(token_type, setup_value);
2268 /* build complete token string */
2269 sprintf(token_string, "%s%s", prefix, token_text);
2271 /* build setup entry line */
2272 line = getFormattedSetupEntry(token_string, value_string);
2274 if (token_type == TYPE_KEY_X11)
2276 Key key = *(Key *)setup_value;
2277 char *keyname = getKeyNameFromKey(key);
2279 /* add comment, if useful */
2280 if (strcmp(keyname, "(undefined)") != 0 &&
2281 strcmp(keyname, "(unknown)") != 0)
2283 /* add at least one whitespace */
2285 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2289 strcat(line, keyname);
2296 void LoadLevelSetup_LastSeries()
2299 SetupFileHash *level_setup_hash = NULL;
2301 /* always start with reliable default values */
2302 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2304 /* ----------------------------------------------------------------------- */
2305 /* ~/.<program>/levelsetup.conf */
2306 /* ----------------------------------------------------------------------- */
2308 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2310 if ((level_setup_hash = loadSetupFileHash(filename)))
2312 char *last_level_series =
2313 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2315 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2317 if (leveldir_current == NULL)
2318 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2320 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2322 freeSetupFileHash(level_setup_hash);
2325 Error(ERR_WARN, "using default setup values");
2330 void SaveLevelSetup_LastSeries()
2333 char *level_subdir = leveldir_current->filename;
2336 /* ----------------------------------------------------------------------- */
2337 /* ~/.<program>/levelsetup.conf */
2338 /* ----------------------------------------------------------------------- */
2340 InitUserDataDirectory();
2342 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2344 if (!(file = fopen(filename, MODE_WRITE)))
2346 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2351 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2352 getCookie("LEVELSETUP")));
2353 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2359 SetFilePermissions(filename, PERMS_PRIVATE);
2362 static void checkSeriesInfo()
2364 static char *level_directory = NULL;
2366 struct dirent *dir_entry;
2368 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2370 level_directory = getPath2((leveldir_current->user_defined ?
2371 getUserLevelDir(NULL) :
2372 options.level_directory),
2373 leveldir_current->fullpath);
2375 if ((dir = opendir(level_directory)) == NULL)
2377 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2381 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2383 if (strlen(dir_entry->d_name) > 4 &&
2384 dir_entry->d_name[3] == '.' &&
2385 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2387 char levelnum_str[4];
2390 strncpy(levelnum_str, dir_entry->d_name, 3);
2391 levelnum_str[3] = '\0';
2393 levelnum_value = atoi(levelnum_str);
2396 if (levelnum_value < leveldir_current->first_level)
2398 Error(ERR_WARN, "additional level %d found", levelnum_value);
2399 leveldir_current->first_level = levelnum_value;
2401 else if (levelnum_value > leveldir_current->last_level)
2403 Error(ERR_WARN, "additional level %d found", levelnum_value);
2404 leveldir_current->last_level = levelnum_value;
2413 void LoadLevelSetup_SeriesInfo()
2416 SetupFileHash *level_setup_hash = NULL;
2417 char *level_subdir = leveldir_current->filename;
2419 /* always start with reliable default values */
2420 level_nr = leveldir_current->first_level;
2422 checkSeriesInfo(leveldir_current);
2424 /* ----------------------------------------------------------------------- */
2425 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2426 /* ----------------------------------------------------------------------- */
2428 level_subdir = leveldir_current->filename;
2430 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2432 if ((level_setup_hash = loadSetupFileHash(filename)))
2436 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2440 level_nr = atoi(token_value);
2442 if (level_nr < leveldir_current->first_level)
2443 level_nr = leveldir_current->first_level;
2444 if (level_nr > leveldir_current->last_level)
2445 level_nr = leveldir_current->last_level;
2448 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2452 int level_nr = atoi(token_value);
2454 if (level_nr < leveldir_current->first_level)
2455 level_nr = leveldir_current->first_level;
2456 if (level_nr > leveldir_current->last_level + 1)
2457 level_nr = leveldir_current->last_level;
2459 if (leveldir_current->user_defined)
2460 level_nr = leveldir_current->last_level;
2462 leveldir_current->handicap_level = level_nr;
2465 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2467 freeSetupFileHash(level_setup_hash);
2470 Error(ERR_WARN, "using default setup values");
2475 void SaveLevelSetup_SeriesInfo()
2478 char *level_subdir = leveldir_current->filename;
2479 char *level_nr_str = int2str(level_nr, 0);
2480 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2483 /* ----------------------------------------------------------------------- */
2484 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2485 /* ----------------------------------------------------------------------- */
2487 InitLevelSetupDirectory(level_subdir);
2489 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2491 if (!(file = fopen(filename, MODE_WRITE)))
2493 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2498 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2499 getCookie("LEVELSETUP")));
2500 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2502 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2503 handicap_level_str));
2508 SetFilePermissions(filename, PERMS_PRIVATE);