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 ***********************************************************/
24 /* file names and filename extensions */
25 #if !defined(PLATFORM_MSDOS)
26 #define LEVELSETUP_DIRECTORY "levelsetup"
27 #define SETUP_FILENAME "setup.conf"
28 #define LEVELSETUP_FILENAME "levelsetup.conf"
29 #define LEVELINFO_FILENAME "levelinfo.conf"
30 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
31 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
32 #define MUSICINFO_FILENAME "musicinfo.conf"
33 #define LEVELFILE_EXTENSION "level"
34 #define TAPEFILE_EXTENSION "tape"
35 #define SCOREFILE_EXTENSION "score"
37 #define LEVELSETUP_DIRECTORY "lvlsetup"
38 #define SETUP_FILENAME "setup.cnf"
39 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
40 #define LEVELINFO_FILENAME "lvlinfo.cnf"
41 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
42 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
43 #define MUSICINFO_FILENAME "musinfo.cnf"
44 #define LEVELFILE_EXTENSION "lvl"
45 #define TAPEFILE_EXTENSION "tap"
46 #define SCOREFILE_EXTENSION "sco"
49 #define NUM_LEVELCLASS_DESC 8
50 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
62 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
63 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
64 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
65 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
66 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
67 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
68 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
69 IS_LEVELCLASS_USER(n) ? FC_RED : \
72 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
73 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
74 IS_LEVELCLASS_BD(n) ? 2 : \
75 IS_LEVELCLASS_EM(n) ? 3 : \
76 IS_LEVELCLASS_SP(n) ? 4 : \
77 IS_LEVELCLASS_DX(n) ? 5 : \
78 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
79 IS_LEVELCLASS_USER(n) ? 7 : \
82 #define TOKEN_VALUE_POSITION 40
83 #define TOKEN_COMMENT_POSITION 60
85 #define MAX_COOKIE_LEN 256
87 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
88 GRAPHICSINFO_FILENAME : \
89 (type) == TREE_TYPE_SOUNDS_DIR ? \
90 SOUNDSINFO_FILENAME : \
91 (type) == TREE_TYPE_MUSIC_DIR ? \
92 MUSICINFO_FILENAME : "")
94 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
95 GRAPHICS_DIRECTORY : \
96 (type) == TREE_TYPE_SOUNDS_DIR ? \
98 (type) == TREE_TYPE_MUSIC_DIR ? \
101 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
102 options.graphics_directory : \
103 (type) == TREE_TYPE_SOUNDS_DIR ? \
104 options.sounds_directory : \
105 (type) == TREE_TYPE_MUSIC_DIR ? \
106 options.music_directory : "")
109 /* ------------------------------------------------------------------------- */
111 /* ------------------------------------------------------------------------- */
113 static char *getLevelClassDescription(TreeInfo *ldi)
115 int position = ldi->sort_priority / 100;
117 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
118 return levelclass_desc[position];
120 return "Unknown Level Class";
123 static char *getUserLevelDir(char *level_subdir)
125 static char *userlevel_dir = NULL;
126 char *data_dir = getUserDataDir();
127 char *userlevel_subdir = LEVELS_DIRECTORY;
132 if (level_subdir != NULL)
133 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
135 userlevel_dir = getPath2(data_dir, userlevel_subdir);
137 return userlevel_dir;
140 static char *getTapeDir(char *level_subdir)
142 static char *tape_dir = NULL;
143 char *data_dir = getUserDataDir();
144 char *tape_subdir = TAPES_DIRECTORY;
149 if (level_subdir != NULL)
150 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
152 tape_dir = getPath2(data_dir, tape_subdir);
157 static char *getScoreDir(char *level_subdir)
159 static char *score_dir = NULL;
160 char *data_dir = options.rw_base_directory;
161 char *score_subdir = SCORES_DIRECTORY;
166 if (level_subdir != NULL)
167 score_dir = getPath3(data_dir, score_subdir, level_subdir);
169 score_dir = getPath2(data_dir, score_subdir);
174 static char *getLevelSetupDir(char *level_subdir)
176 static char *levelsetup_dir = NULL;
177 char *data_dir = getUserDataDir();
178 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
181 free(levelsetup_dir);
183 if (level_subdir != NULL)
184 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
186 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
188 return levelsetup_dir;
191 static char *getLevelDirFromTreeInfo(TreeInfo *node)
193 static char *level_dir = NULL;
196 return options.level_directory;
201 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
202 options.level_directory), node->fullpath);
207 static char *getCurrentLevelDir()
209 return getLevelDirFromTreeInfo(leveldir_current);
212 static char *getDefaultGraphicsDir(char *graphics_subdir)
214 static char *graphics_dir = NULL;
216 if (graphics_subdir == NULL)
217 return options.graphics_directory;
222 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
227 static char *getDefaultSoundsDir(char *sounds_subdir)
229 static char *sounds_dir = NULL;
231 if (sounds_subdir == NULL)
232 return options.sounds_directory;
237 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
242 static char *getDefaultMusicDir(char *music_subdir)
244 static char *music_dir = NULL;
246 if (music_subdir == NULL)
247 return options.music_directory;
252 music_dir = getPath2(options.music_directory, music_subdir);
257 static char *getUserGraphicsDir()
259 static char *usergraphics_dir = NULL;
261 if (usergraphics_dir == NULL)
262 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
264 return usergraphics_dir;
267 static char *getUserSoundsDir()
269 static char *usersounds_dir = NULL;
271 if (usersounds_dir == NULL)
272 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
274 return usersounds_dir;
277 static char *getUserMusicDir()
279 static char *usermusic_dir = NULL;
281 if (usermusic_dir == NULL)
282 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
284 return usermusic_dir;
287 char *getLevelFilename(int nr)
289 static char *filename = NULL;
290 char basename[MAX_FILENAME_LEN];
292 if (filename != NULL)
295 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
296 filename = getPath2(getCurrentLevelDir(), basename);
301 char *getTapeFilename(int nr)
303 static char *filename = NULL;
304 char basename[MAX_FILENAME_LEN];
306 if (filename != NULL)
309 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
310 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
315 char *getScoreFilename(int nr)
317 static char *filename = NULL;
318 char basename[MAX_FILENAME_LEN];
320 if (filename != NULL)
323 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
324 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
329 char *getSetupFilename()
331 static char *filename = NULL;
333 if (filename != NULL)
336 filename = getPath2(getSetupDir(), SETUP_FILENAME);
341 static char *getSetupArtworkDir(TreeInfo *ti)
343 static char *artwork_dir = NULL;
345 if (artwork_dir != NULL)
348 artwork_dir = getPath2(ti->basepath, ti->fullpath);
353 static char *getCorrectedImageBasename(char *basename)
355 char *result = basename;
357 #if defined(PLATFORM_MSDOS)
358 if (program.filename_prefix != NULL)
360 int prefix_len = strlen(program.filename_prefix);
362 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
363 result = &basename[prefix_len];
370 static boolean fileExists(char *filename)
373 printf("checking file '%s'\n", filename);
376 return (access(filename, F_OK) == 0);
379 char *getCustomImageFilename(char *basename)
381 static char *filename = NULL;
383 if (filename != NULL)
386 basename = getCorrectedImageBasename(basename);
388 if (!setup.override_level_graphics)
390 /* 1st try: look for special artwork in current level series directory */
391 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
392 if (fileExists(filename))
396 /* 2nd try: look for special artwork in configured artwork directory */
397 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
398 if (fileExists(filename))
401 /* 3rd try: look for default artwork in new default artwork directory */
402 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
403 if (fileExists(filename))
406 /* 4th try: look for default artwork in old default artwork directory */
407 filename = getPath2(options.graphics_directory, basename);
408 if (fileExists(filename))
411 return NULL; /* cannot find specified artwork file anywhere */
414 char *getCustomSoundFilename(char *basename)
416 static char *filename = NULL;
418 if (filename != NULL)
421 if (!setup.override_level_sounds)
423 /* 1st try: look for special artwork in current level series directory */
424 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
425 if (fileExists(filename))
429 /* 2nd try: look for special artwork in configured artwork directory */
430 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
431 if (fileExists(filename))
434 /* 3rd try: look for default artwork in new default artwork directory */
435 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
436 if (fileExists(filename))
439 /* 4th try: look for default artwork in old default artwork directory */
440 filename = getPath2(options.sounds_directory, basename);
441 if (fileExists(filename))
444 return NULL; /* cannot find specified artwork file anywhere */
447 char *getCustomSoundConfigFilename()
449 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
452 char *getCustomMusicDirectory(void)
454 static char *directory = NULL;
456 if (directory != NULL)
459 if (!setup.override_level_music)
461 /* 1st try: look for special artwork in current level series directory */
462 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
463 if (fileExists(directory))
467 /* 2nd try: look for special artwork in configured artwork directory */
468 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
469 if (fileExists(directory))
472 /* 3rd try: look for default artwork in new default artwork directory */
473 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
474 if (fileExists(directory))
477 /* 4th try: look for default artwork in old default artwork directory */
478 directory = getStringCopy(options.music_directory);
479 if (fileExists(directory))
482 return NULL; /* cannot find specified artwork file anywhere */
485 void InitTapeDirectory(char *level_subdir)
487 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
488 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
489 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
492 void InitScoreDirectory(char *level_subdir)
494 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
495 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
498 static void SaveUserLevelInfo();
500 void InitUserLevelDirectory(char *level_subdir)
502 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
504 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
505 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
506 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
512 void InitLevelSetupDirectory(char *level_subdir)
514 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
515 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
516 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
519 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
521 int file_version_major, file_version_minor, file_version_patch;
522 int game_version_major, game_version_minor, game_version_patch;
524 file_version_major = fgetc(file);
525 file_version_minor = fgetc(file);
526 file_version_patch = fgetc(file);
527 fgetc(file); /* not used */
529 game_version_major = fgetc(file);
530 game_version_minor = fgetc(file);
531 game_version_patch = fgetc(file);
532 fgetc(file); /* not used */
534 *file_version = VERSION_IDENT(file_version_major,
538 *game_version = VERSION_IDENT(game_version_major,
543 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
545 int file_version_major = VERSION_MAJOR(file_version);
546 int file_version_minor = VERSION_MINOR(file_version);
547 int file_version_patch = VERSION_PATCH(file_version);
548 int game_version_major = VERSION_MAJOR(game_version);
549 int game_version_minor = VERSION_MINOR(game_version);
550 int game_version_patch = VERSION_PATCH(game_version);
552 fputc(file_version_major, file);
553 fputc(file_version_minor, file);
554 fputc(file_version_patch, file);
555 fputc(0, file); /* not used */
557 fputc(game_version_major, file);
558 fputc(game_version_minor, file);
559 fputc(game_version_patch, file);
560 fputc(0, file); /* not used */
564 /* ------------------------------------------------------------------------- */
565 /* some functions to handle lists of level directories */
566 /* ------------------------------------------------------------------------- */
568 TreeInfo *newTreeInfo()
570 return checked_calloc(sizeof(TreeInfo));
573 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
575 node_new->next = *node_first;
576 *node_first = node_new;
579 int numTreeInfo(TreeInfo *node)
592 boolean validLevelSeries(TreeInfo *node)
594 return (node != NULL && !node->node_group && !node->parent_link);
597 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
601 if (node->node_top) /* start with first tree entry */
602 return getFirstValidTreeInfoEntry(*node->node_top);
606 else if (node->node_group) /* enter level group (step down into tree) */
607 return getFirstValidTreeInfoEntry(node->node_group);
608 else if (node->parent_link) /* skip start entry of level group */
610 if (node->next) /* get first real level series entry */
611 return getFirstValidTreeInfoEntry(node->next);
612 else /* leave empty level group and go on */
613 return getFirstValidTreeInfoEntry(node->node_parent->next);
615 else /* this seems to be a regular level series */
619 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
624 if (node->node_parent == NULL) /* top level group */
625 return *node->node_top;
626 else /* sub level group */
627 return node->node_parent->node_group;
630 int numTreeInfoInGroup(TreeInfo *node)
632 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
635 int posTreeInfo(TreeInfo *node)
637 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
642 if (node_cmp == node)
646 node_cmp = node_cmp->next;
652 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
654 TreeInfo *node_default = node;
669 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
671 if (filename == NULL)
676 if (node->node_group)
678 TreeInfo *node_group;
680 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
685 else if (!node->parent_link)
687 if (strcmp(filename, node->filename) == 0)
697 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
699 return getTreeInfoFromFilenameExt(ti, filename);
702 void dumpTreeInfo(TreeInfo *node, int depth)
706 printf("Dumping TreeInfo:\n");
710 for (i=0; i<(depth + 1) * 3; i++)
713 printf("filename == '%s' (%s) [%s]\n",
714 node->filename, node->name, node->name_short);
716 if (node->node_group != NULL)
717 dumpTreeInfo(node->node_group, depth + 1);
723 void sortTreeInfo(TreeInfo **node_first,
724 int (*compare_function)(const void *, const void *))
726 int num_nodes = numTreeInfo(*node_first);
727 TreeInfo **sort_array;
728 TreeInfo *node = *node_first;
734 /* allocate array for sorting structure pointers */
735 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
737 /* writing structure pointers to sorting array */
738 while (i < num_nodes && node) /* double boundary check... */
740 sort_array[i] = node;
746 /* sorting the structure pointers in the sorting array */
747 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
750 /* update the linkage of list elements with the sorted node array */
751 for (i=0; i<num_nodes - 1; i++)
752 sort_array[i]->next = sort_array[i + 1];
753 sort_array[num_nodes - 1]->next = NULL;
755 /* update the linkage of the main list anchor pointer */
756 *node_first = sort_array[0];
760 /* now recursively sort the level group structures */
764 if (node->node_group != NULL)
765 sortTreeInfo(&node->node_group, compare_function);
772 /* ========================================================================= */
773 /* some stuff from "files.c" */
774 /* ========================================================================= */
776 #if defined(PLATFORM_WIN32)
778 #define S_IRGRP S_IRUSR
781 #define S_IROTH S_IRUSR
784 #define S_IWGRP S_IWUSR
787 #define S_IWOTH S_IWUSR
790 #define S_IXGRP S_IXUSR
793 #define S_IXOTH S_IXUSR
796 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
801 #endif /* PLATFORM_WIN32 */
803 /* file permissions for newly written files */
804 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
805 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
806 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
808 #define MODE_W_PRIVATE (S_IWUSR)
809 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
810 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
812 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
813 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
815 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
816 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
818 char *getUserDataDir(void)
820 static char *userdata_dir = NULL;
824 char *home_dir = getHomeDir();
825 char *data_dir = program.userdata_directory;
827 userdata_dir = getPath2(home_dir, data_dir);
835 return getUserDataDir();
838 static mode_t posix_umask(mode_t mask)
840 #if defined(PLATFORM_UNIX)
847 static int posix_mkdir(const char *pathname, mode_t mode)
849 #if defined(PLATFORM_WIN32)
850 return mkdir(pathname);
852 return mkdir(pathname, mode);
856 void createDirectory(char *dir, char *text, int permission_class)
858 /* leave "other" permissions in umask untouched, but ensure group parts
859 of USERDATA_DIR_MODE are not masked */
860 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
861 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
862 mode_t normal_umask = posix_umask(0);
863 mode_t group_umask = ~(dir_mode & S_IRWXG);
864 posix_umask(normal_umask & group_umask);
866 if (access(dir, F_OK) != 0)
867 if (posix_mkdir(dir, dir_mode) != 0)
868 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
870 posix_umask(normal_umask); /* reset normal umask */
873 void InitUserDataDirectory()
875 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
878 void SetFilePermissions(char *filename, int permission_class)
880 chmod(filename, (permission_class == PERMS_PRIVATE ?
881 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
884 char *getCookie(char *file_type)
886 static char cookie[MAX_COOKIE_LEN + 1];
888 if (strlen(program.cookie_prefix) + 1 +
889 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
890 return "[COOKIE ERROR]"; /* should never happen */
892 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
893 program.cookie_prefix, file_type,
894 program.version_major, program.version_minor);
899 int getFileVersionFromCookieString(const char *cookie)
901 const char *ptr_cookie1, *ptr_cookie2;
902 const char *pattern1 = "_FILE_VERSION_";
903 const char *pattern2 = "?.?";
904 const int len_cookie = strlen(cookie);
905 const int len_pattern1 = strlen(pattern1);
906 const int len_pattern2 = strlen(pattern2);
907 const int len_pattern = len_pattern1 + len_pattern2;
908 int version_major, version_minor;
910 if (len_cookie <= len_pattern)
913 ptr_cookie1 = &cookie[len_cookie - len_pattern];
914 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
916 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
919 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
920 ptr_cookie2[1] != '.' ||
921 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
924 version_major = ptr_cookie2[0] - '0';
925 version_minor = ptr_cookie2[2] - '0';
927 return VERSION_IDENT(version_major, version_minor, 0);
930 boolean checkCookieString(const char *cookie, const char *template)
932 const char *pattern = "_FILE_VERSION_?.?";
933 const int len_cookie = strlen(cookie);
934 const int len_template = strlen(template);
935 const int len_pattern = strlen(pattern);
937 if (len_cookie != len_template)
940 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
946 /* ------------------------------------------------------------------------- */
947 /* setup file list handling functions */
948 /* ------------------------------------------------------------------------- */
950 int get_string_integer_value(char *s)
952 static char *number_text[][3] =
954 { "0", "zero", "null", },
955 { "1", "one", "first" },
956 { "2", "two", "second" },
957 { "3", "three", "third" },
958 { "4", "four", "fourth" },
959 { "5", "five", "fifth" },
960 { "6", "six", "sixth" },
961 { "7", "seven", "seventh" },
962 { "8", "eight", "eighth" },
963 { "9", "nine", "ninth" },
964 { "10", "ten", "tenth" },
965 { "11", "eleven", "eleventh" },
966 { "12", "twelve", "twelfth" },
970 char *s_lower = getStringToLower(s);
975 if (strcmp(s_lower, number_text[i][j]) == 0)
986 boolean get_string_boolean_value(char *s)
988 char *s_lower = getStringToLower(s);
989 boolean result = FALSE;
991 if (strcmp(s_lower, "true") == 0 ||
992 strcmp(s_lower, "yes") == 0 ||
993 strcmp(s_lower, "on") == 0 ||
994 get_string_integer_value(s) == 1)
1002 char *getFormattedSetupEntry(char *token, char *value)
1005 static char entry[MAX_LINE_LEN];
1007 /* start with the token and some spaces to format output line */
1008 sprintf(entry, "%s:", token);
1009 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1012 /* continue with the token's value */
1013 strcat(entry, value);
1018 void freeSetupFileList(struct SetupFileList *setup_file_list)
1020 if (!setup_file_list)
1023 if (setup_file_list->token)
1024 free(setup_file_list->token);
1025 if (setup_file_list->value)
1026 free(setup_file_list->value);
1027 if (setup_file_list->next)
1028 freeSetupFileList(setup_file_list->next);
1029 free(setup_file_list);
1032 static struct SetupFileList *newSetupFileList(char *token, char *value)
1034 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1036 new->token = checked_malloc(strlen(token) + 1);
1037 strcpy(new->token, token);
1039 new->value = checked_malloc(strlen(value) + 1);
1040 strcpy(new->value, value);
1047 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1049 if (!setup_file_list)
1052 if (strcmp(setup_file_list->token, token) == 0)
1053 return setup_file_list->value;
1055 return getTokenValue(setup_file_list->next, token);
1058 static void setTokenValue(struct SetupFileList *setup_file_list,
1059 char *token, char *value)
1061 if (!setup_file_list)
1064 if (strcmp(setup_file_list->token, token) == 0)
1066 free(setup_file_list->value);
1067 setup_file_list->value = checked_malloc(strlen(value) + 1);
1068 strcpy(setup_file_list->value, value);
1070 else if (setup_file_list->next == NULL)
1071 setup_file_list->next = newSetupFileList(token, value);
1073 setTokenValue(setup_file_list->next, token, value);
1077 static void printSetupFileList(struct SetupFileList *setup_file_list)
1079 if (!setup_file_list)
1082 printf("token: '%s'\n", setup_file_list->token);
1083 printf("value: '%s'\n", setup_file_list->value);
1085 printSetupFileList(setup_file_list->next);
1089 struct SetupFileList *loadSetupFileList(char *filename)
1092 char line[MAX_LINE_LEN];
1093 char *token, *value, *line_ptr;
1094 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1095 struct SetupFileList *first_valid_list_entry;
1099 if (!(file = fopen(filename, MODE_READ)))
1101 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1107 /* read next line of input file */
1108 if (!fgets(line, MAX_LINE_LEN, file))
1111 /* cut trailing comment or whitespace from input line */
1112 for (line_ptr = line; *line_ptr; line_ptr++)
1114 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1121 /* cut trailing whitespaces from input line */
1122 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1123 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1126 /* ignore empty lines */
1130 line_len = strlen(line);
1132 /* cut leading whitespaces from token */
1133 for (token = line; *token; token++)
1134 if (*token != ' ' && *token != '\t')
1137 /* find end of token */
1138 for (line_ptr = token; *line_ptr; line_ptr++)
1140 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1147 if (line_ptr < line + line_len)
1148 value = line_ptr + 1;
1152 /* cut leading whitespaces from value */
1153 for (; *value; value++)
1154 if (*value != ' ' && *value != '\t')
1157 if (*token && *value)
1158 setTokenValue(setup_file_list, token, value);
1163 first_valid_list_entry = setup_file_list->next;
1165 /* free empty list header */
1166 setup_file_list->next = NULL;
1167 freeSetupFileList(setup_file_list);
1169 if (first_valid_list_entry == NULL)
1170 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1172 return first_valid_list_entry;
1175 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1178 if (!setup_file_list)
1181 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1183 if (!checkCookieString(setup_file_list->value, identifier))
1185 Error(ERR_WARN, "configuration file has wrong file identifier");
1192 if (setup_file_list->next)
1193 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1196 Error(ERR_WARN, "configuration file has no file identifier");
1202 /* ========================================================================= */
1203 /* setup file stuff */
1204 /* ========================================================================= */
1206 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1207 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1208 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1210 /* level directory info */
1211 #define LEVELINFO_TOKEN_NAME 0
1212 #define LEVELINFO_TOKEN_NAME_SHORT 1
1213 #define LEVELINFO_TOKEN_NAME_SORTING 2
1214 #define LEVELINFO_TOKEN_AUTHOR 3
1215 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1216 #define LEVELINFO_TOKEN_LEVELS 5
1217 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1218 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1219 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1220 #define LEVELINFO_TOKEN_READONLY 9
1222 #define NUM_LEVELINFO_TOKENS 10
1224 static LevelDirTree ldi;
1226 static struct TokenInfo levelinfo_tokens[] =
1228 /* level directory info */
1229 { TYPE_STRING, &ldi.name, "name" },
1230 { TYPE_STRING, &ldi.name_short, "name_short" },
1231 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1232 { TYPE_STRING, &ldi.author, "author" },
1233 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1234 { TYPE_INTEGER, &ldi.levels, "levels" },
1235 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1236 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1237 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1238 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1241 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1245 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1246 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1247 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1248 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1251 ldi->node_parent = NULL;
1252 ldi->node_group = NULL;
1256 ldi->cl_cursor = -1;
1258 ldi->filename = NULL;
1259 ldi->fullpath = NULL;
1260 ldi->basepath = NULL;
1261 ldi->name = getStringCopy(ANONYMOUS_NAME);
1262 ldi->name_short = NULL;
1263 ldi->name_sorting = NULL;
1264 ldi->author = getStringCopy(ANONYMOUS_NAME);
1266 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1267 ldi->parent_link = FALSE;
1268 ldi->user_defined = FALSE;
1270 ldi->class_desc = NULL;
1272 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1274 ldi->imported_from = NULL;
1276 ldi->first_level = 0;
1277 ldi->last_level = 0;
1278 ldi->level_group = FALSE;
1279 ldi->handicap_level = 0;
1280 ldi->readonly = TRUE;
1284 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1288 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1290 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1294 /* first copy all values from the parent structure ... */
1297 /* ... then set all fields to default that cannot be inherited from parent.
1298 This is especially important for all those fields that can be set from
1299 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1300 calls 'free()' for all already set token values which requires that no
1301 other structure's pointer may point to them!
1304 ldi->filename = NULL;
1305 ldi->fullpath = NULL;
1306 ldi->basepath = NULL;
1307 ldi->name = getStringCopy(ANONYMOUS_NAME);
1308 ldi->name_short = NULL;
1309 ldi->name_sorting = NULL;
1310 ldi->author = getStringCopy(parent->author);
1311 ldi->imported_from = getStringCopy(parent->imported_from);
1313 ldi->level_group = FALSE;
1314 ldi->parent_link = FALSE;
1316 ldi->node_top = parent->node_top;
1317 ldi->node_parent = parent;
1318 ldi->node_group = NULL;
1322 void setSetupInfo(struct TokenInfo *token_info,
1323 int token_nr, char *token_value)
1325 int token_type = token_info[token_nr].type;
1326 void *setup_value = token_info[token_nr].value;
1328 if (token_value == NULL)
1331 /* set setup field to corresponding token value */
1336 *(boolean *)setup_value = get_string_boolean_value(token_value);
1340 *(Key *)setup_value = getKeyFromKeyName(token_value);
1344 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1348 *(int *)setup_value = get_string_integer_value(token_value);
1352 if (*(char **)setup_value != NULL)
1353 free(*(char **)setup_value);
1354 *(char **)setup_value = getStringCopy(token_value);
1362 static int compareTreeInfoEntries(const void *object1, const void *object2)
1364 const TreeInfo *entry1 = *((TreeInfo **)object1);
1365 const TreeInfo *entry2 = *((TreeInfo **)object2);
1368 if (entry1->parent_link || entry2->parent_link)
1369 compare_result = (entry1->parent_link ? -1 : +1);
1370 else if (entry1->sort_priority == entry2->sort_priority)
1372 char *name1 = getStringToLower(entry1->name_sorting);
1373 char *name2 = getStringToLower(entry2->name_sorting);
1375 compare_result = strcmp(name1, name2);
1380 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1381 compare_result = entry1->sort_priority - entry2->sort_priority;
1383 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1385 return compare_result;
1388 static void createParentTreeInfoNode(TreeInfo *node_parent)
1392 if (node_parent == NULL)
1395 ti_new = newTreeInfo();
1396 setTreeInfoToDefaults(ti_new, node_parent->type);
1398 ti_new->node_parent = node_parent;
1399 ti_new->parent_link = TRUE;
1401 ti_new->name = ".. (parent directory)";
1402 ti_new->name_short = getStringCopy(ti_new->name);
1403 ti_new->name_sorting = getStringCopy(ti_new->name);
1405 ti_new->filename = "..";
1406 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1408 ti_new->sort_priority = node_parent->sort_priority;
1409 ti_new->class_desc = getLevelClassDescription(ti_new);
1411 pushTreeInfo(&node_parent->node_group, ti_new);
1414 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1415 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1417 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1418 TreeInfo *node_parent,
1419 char *level_directory,
1420 char *directory_name)
1422 char *directory_path = getPath2(level_directory, directory_name);
1423 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1424 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1425 LevelDirTree *leveldir_new = NULL;
1428 if (setup_file_list == NULL)
1430 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1432 free(directory_path);
1438 leveldir_new = newTreeInfo();
1441 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1443 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1445 leveldir_new->filename = getStringCopy(directory_name);
1447 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1449 /* set all structure fields according to the token/value pairs */
1450 ldi = *leveldir_new;
1451 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1452 setSetupInfo(levelinfo_tokens, i,
1453 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1454 *leveldir_new = ldi;
1456 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1458 free(leveldir_new->name);
1459 leveldir_new->name = getStringCopy(leveldir_new->filename);
1462 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1464 if (leveldir_new->name_short == NULL)
1465 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1467 if (leveldir_new->name_sorting == NULL)
1468 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1470 if (node_parent == NULL) /* top level group */
1472 leveldir_new->basepath = level_directory;
1473 leveldir_new->fullpath = leveldir_new->filename;
1475 else /* sub level group */
1477 leveldir_new->basepath = node_parent->basepath;
1478 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1481 if (leveldir_new->levels < 1)
1482 leveldir_new->levels = 1;
1484 leveldir_new->last_level =
1485 leveldir_new->first_level + leveldir_new->levels - 1;
1487 leveldir_new->user_defined =
1488 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1490 leveldir_new->color = LEVELCOLOR(leveldir_new);
1491 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1493 leveldir_new->handicap_level = /* set handicap to default value */
1494 (leveldir_new->user_defined ?
1495 leveldir_new->last_level :
1496 leveldir_new->first_level);
1498 pushTreeInfo(node_first, leveldir_new);
1500 freeSetupFileList(setup_file_list);
1502 if (leveldir_new->level_group)
1504 /* create node to link back to current level directory */
1505 createParentTreeInfoNode(leveldir_new);
1507 /* step into sub-directory and look for more level series */
1508 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1509 leveldir_new, directory_path);
1512 free(directory_path);
1518 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1519 TreeInfo *node_parent,
1520 char *level_directory)
1523 struct dirent *dir_entry;
1524 boolean valid_entry_found = FALSE;
1526 if ((dir = opendir(level_directory)) == NULL)
1528 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1532 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1534 struct stat file_status;
1535 char *directory_name = dir_entry->d_name;
1536 char *directory_path = getPath2(level_directory, directory_name);
1538 /* skip entries for current and parent directory */
1539 if (strcmp(directory_name, ".") == 0 ||
1540 strcmp(directory_name, "..") == 0)
1542 free(directory_path);
1546 /* find out if directory entry is itself a directory */
1547 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1548 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1550 free(directory_path);
1554 free(directory_path);
1556 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1557 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1558 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1561 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1568 if (!valid_entry_found)
1570 /* check if this directory directly contains a file "levelinfo.conf" */
1571 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1572 level_directory, ".");
1575 if (!valid_entry_found)
1576 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1580 void LoadLevelInfo()
1582 InitUserLevelDirectory(getLoginName());
1584 DrawInitText("Loading level series:", 120, FC_GREEN);
1586 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1587 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1589 /* before sorting, the first entries will be from the user directory */
1590 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1592 if (leveldir_first == NULL)
1593 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1595 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1598 dumpTreeInfo(leveldir_first, 0);
1602 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1603 TreeInfo *node_parent,
1604 char *base_directory,
1605 char *directory_name, int type)
1607 char *directory_path = getPath2(base_directory, directory_name);
1608 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1609 struct SetupFileList *setup_file_list = NULL;
1610 TreeInfo *artwork_new = NULL;
1613 if (access(filename, F_OK) == 0) /* file exists */
1614 setup_file_list = loadSetupFileList(filename);
1616 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1619 struct dirent *dir_entry;
1620 boolean valid_file_found = FALSE;
1622 if ((dir = opendir(directory_path)) != NULL)
1624 while ((dir_entry = readdir(dir)) != NULL)
1626 char *entry_name = dir_entry->d_name;
1628 if (FileIsArtworkType(entry_name, type))
1630 valid_file_found = TRUE;
1638 if (!valid_file_found)
1640 if (strcmp(directory_name, ".") != 0)
1641 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1643 free(directory_path);
1650 artwork_new = newTreeInfo();
1653 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1655 setTreeInfoToDefaults(artwork_new, type);
1657 artwork_new->filename = getStringCopy(directory_name);
1659 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1662 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1665 /* set all structure fields according to the token/value pairs */
1667 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1668 setSetupInfo(levelinfo_tokens, i,
1669 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1672 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1674 free(artwork_new->name);
1675 artwork_new->name = getStringCopy(artwork_new->filename);
1678 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1680 if (artwork_new->name_short == NULL)
1681 artwork_new->name_short = getStringCopy(artwork_new->name);
1683 if (artwork_new->name_sorting == NULL)
1684 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1687 if (node_parent == NULL) /* top level group */
1689 artwork_new->basepath = getStringCopy(base_directory);
1690 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1692 else /* sub level group */
1694 artwork_new->basepath = getStringCopy(node_parent->basepath);
1695 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1698 artwork_new->user_defined =
1699 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1701 /* (may use ".sort_priority" from "setup_file_list" above) */
1702 artwork_new->color = LEVELCOLOR(artwork_new);
1703 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1705 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1707 if (artwork_new->name != NULL)
1708 free(artwork_new->name);
1710 if (strcmp(artwork_new->filename, ".") == 0)
1712 if (artwork_new->user_defined)
1714 artwork_new->name = getStringCopy("private");
1715 artwork_new->sort_priority = LEVELCLASS_USER;
1719 artwork_new->name = getStringCopy("classic");
1720 artwork_new->sort_priority = LEVELCLASS_CLASSICS;
1723 artwork_new->color = LEVELCOLOR(artwork_new);
1724 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1727 artwork_new->name = getStringCopy(artwork_new->filename);
1729 artwork_new->name_short = getStringCopy(artwork_new->name);
1730 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1733 pushTreeInfo(node_first, artwork_new);
1735 freeSetupFileList(setup_file_list);
1737 free(directory_path);
1743 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1744 TreeInfo *node_parent,
1745 char *base_directory, int type)
1748 struct dirent *dir_entry;
1749 boolean valid_entry_found = FALSE;
1751 if ((dir = opendir(base_directory)) == NULL)
1753 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1754 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1758 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1760 struct stat file_status;
1761 char *directory_name = dir_entry->d_name;
1762 char *directory_path = getPath2(base_directory, directory_name);
1764 /* skip entries for current and parent directory */
1765 if (strcmp(directory_name, ".") == 0 ||
1766 strcmp(directory_name, "..") == 0)
1768 free(directory_path);
1772 /* find out if directory entry is itself a directory */
1773 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1774 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1776 free(directory_path);
1780 free(directory_path);
1782 /* check if this directory contains artwork with or without config file */
1783 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1785 directory_name, type);
1790 /* check if this directory directly contains artwork itself */
1791 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1792 base_directory, ".",
1794 if (!valid_entry_found)
1795 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1799 void LoadArtworkInfo()
1801 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1803 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1804 options.graphics_directory,
1805 TREE_TYPE_GRAPHICS_DIR);
1806 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1807 getUserGraphicsDir(),
1808 TREE_TYPE_GRAPHICS_DIR);
1810 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1811 options.sounds_directory,
1812 TREE_TYPE_SOUNDS_DIR);
1813 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1815 TREE_TYPE_SOUNDS_DIR);
1817 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1818 options.music_directory,
1819 TREE_TYPE_MUSIC_DIR);
1820 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1822 TREE_TYPE_MUSIC_DIR);
1824 /* before sorting, the first entries will be from the user directory */
1825 artwork.gfx_current =
1826 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1827 if (artwork.gfx_current == NULL)
1828 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1830 artwork.snd_current =
1831 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1832 if (artwork.snd_current == NULL)
1833 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1835 artwork.mus_current =
1836 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1837 if (artwork.mus_current == NULL)
1838 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1840 artwork.graphics_set_current_name = artwork.gfx_current->name;
1841 artwork.sounds_set_current_name = artwork.snd_current->name;
1842 artwork.music_set_current_name = artwork.mus_current->name;
1845 printf("graphics set == %s\n\n", artwork.graphics_set_current_name);
1846 printf("sounds set == %s\n\n", artwork.sounds_set_current_name);
1847 printf("music set == %s\n\n", artwork.music_set_current_name);
1850 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1851 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1852 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1855 dumpTreeInfo(artwork.gfx_first, 0);
1856 dumpTreeInfo(artwork.snd_first, 0);
1857 dumpTreeInfo(artwork.mus_first, 0);
1861 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1862 LevelDirTree *level_node)
1864 /* recursively check all level directories for artwork sub-directories */
1868 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1869 ARTWORK_DIRECTORY((*artwork_node)->type));
1872 if (!level_node->parent_link)
1873 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1874 level_node->filename, level_node->name);
1877 if (!level_node->parent_link)
1879 TreeInfo *topnode_last = *artwork_node;
1881 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1882 (*artwork_node)->type);
1884 if (topnode_last != *artwork_node)
1886 free((*artwork_node)->name);
1887 free((*artwork_node)->name_sorting);
1888 free((*artwork_node)->name_short);
1890 (*artwork_node)->name = getStringCopy(level_node->name);
1891 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1892 (*artwork_node)->name_short = getStringCopy(level_node->filename);
1894 (*artwork_node)->sort_priority = level_node->sort_priority;
1895 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1901 if (level_node->node_group != NULL)
1902 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1904 level_node = level_node->next;
1908 void LoadLevelArtworkInfo()
1910 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1912 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1913 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1914 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1916 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1917 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1918 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1921 dumpTreeInfo(artwork.gfx_first, 0);
1922 dumpTreeInfo(artwork.snd_first, 0);
1923 dumpTreeInfo(artwork.mus_first, 0);
1927 static void SaveUserLevelInfo()
1933 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1935 if (!(file = fopen(filename, MODE_WRITE)))
1937 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1942 /* always start with reliable default values */
1943 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1945 ldi.name = getLoginName();
1946 ldi.author = getRealName();
1948 ldi.first_level = 1;
1949 ldi.sort_priority = LEVELCLASS_USER_START;
1950 ldi.readonly = FALSE;
1952 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1953 getCookie("LEVELINFO")));
1955 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1956 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1957 i != LEVELINFO_TOKEN_NAME_SORTING &&
1958 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1959 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1964 SetFilePermissions(filename, PERMS_PRIVATE);
1967 char *getSetupValue(int type, void *value)
1969 static char value_string[MAX_LINE_LEN];
1977 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1981 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1985 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1989 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1993 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1997 sprintf(value_string, "%d", *(int *)value);
2001 strcpy(value_string, *(char **)value);
2005 value_string[0] = '\0';
2009 return value_string;
2012 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2016 static char token_string[MAX_LINE_LEN];
2017 int token_type = token_info[token_nr].type;
2018 void *setup_value = token_info[token_nr].value;
2019 char *token_text = token_info[token_nr].text;
2020 char *value_string = getSetupValue(token_type, setup_value);
2022 /* build complete token string */
2023 sprintf(token_string, "%s%s", prefix, token_text);
2025 /* build setup entry line */
2026 line = getFormattedSetupEntry(token_string, value_string);
2028 if (token_type == TYPE_KEY_X11)
2030 Key key = *(Key *)setup_value;
2031 char *keyname = getKeyNameFromKey(key);
2033 /* add comment, if useful */
2034 if (strcmp(keyname, "(undefined)") != 0 &&
2035 strcmp(keyname, "(unknown)") != 0)
2037 /* add at least one whitespace */
2039 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2043 strcat(line, keyname);
2050 void LoadLevelSetup_LastSeries()
2053 struct SetupFileList *level_setup_list = NULL;
2055 /* always start with reliable default values */
2056 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2058 /* ----------------------------------------------------------------------- */
2059 /* ~/.<program>/levelsetup.conf */
2060 /* ----------------------------------------------------------------------- */
2062 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2064 if ((level_setup_list = loadSetupFileList(filename)))
2066 char *last_level_series =
2067 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2069 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2071 if (leveldir_current == NULL)
2072 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2074 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2076 freeSetupFileList(level_setup_list);
2079 Error(ERR_WARN, "using default setup values");
2084 void SaveLevelSetup_LastSeries()
2087 char *level_subdir = leveldir_current->filename;
2090 /* ----------------------------------------------------------------------- */
2091 /* ~/.<program>/levelsetup.conf */
2092 /* ----------------------------------------------------------------------- */
2094 InitUserDataDirectory();
2096 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2098 if (!(file = fopen(filename, MODE_WRITE)))
2100 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2105 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2106 getCookie("LEVELSETUP")));
2107 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2113 SetFilePermissions(filename, PERMS_PRIVATE);
2116 static void checkSeriesInfo()
2118 static char *level_directory = NULL;
2120 struct dirent *dir_entry;
2122 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2124 level_directory = getPath2((leveldir_current->user_defined ?
2125 getUserLevelDir(NULL) :
2126 options.level_directory),
2127 leveldir_current->fullpath);
2129 if ((dir = opendir(level_directory)) == NULL)
2131 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2135 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2137 if (strlen(dir_entry->d_name) > 4 &&
2138 dir_entry->d_name[3] == '.' &&
2139 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2141 char levelnum_str[4];
2144 strncpy(levelnum_str, dir_entry->d_name, 3);
2145 levelnum_str[3] = '\0';
2147 levelnum_value = atoi(levelnum_str);
2149 if (levelnum_value < leveldir_current->first_level)
2151 Error(ERR_WARN, "additional level %d found", levelnum_value);
2152 leveldir_current->first_level = levelnum_value;
2154 else if (levelnum_value > leveldir_current->last_level)
2156 Error(ERR_WARN, "additional level %d found", levelnum_value);
2157 leveldir_current->last_level = levelnum_value;
2165 void LoadLevelSetup_SeriesInfo()
2168 struct SetupFileList *level_setup_list = NULL;
2169 char *level_subdir = leveldir_current->filename;
2171 /* always start with reliable default values */
2172 level_nr = leveldir_current->first_level;
2174 checkSeriesInfo(leveldir_current);
2176 /* ----------------------------------------------------------------------- */
2177 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2178 /* ----------------------------------------------------------------------- */
2180 level_subdir = leveldir_current->filename;
2182 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2184 if ((level_setup_list = loadSetupFileList(filename)))
2188 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2192 level_nr = atoi(token_value);
2194 if (level_nr < leveldir_current->first_level)
2195 level_nr = leveldir_current->first_level;
2196 if (level_nr > leveldir_current->last_level)
2197 level_nr = leveldir_current->last_level;
2200 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2204 int level_nr = atoi(token_value);
2206 if (level_nr < leveldir_current->first_level)
2207 level_nr = leveldir_current->first_level;
2208 if (level_nr > leveldir_current->last_level + 1)
2209 level_nr = leveldir_current->last_level;
2211 if (leveldir_current->user_defined)
2212 level_nr = leveldir_current->last_level;
2214 leveldir_current->handicap_level = level_nr;
2217 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2219 freeSetupFileList(level_setup_list);
2222 Error(ERR_WARN, "using default setup values");
2227 void SaveLevelSetup_SeriesInfo()
2230 char *level_subdir = leveldir_current->filename;
2231 char *level_nr_str = int2str(level_nr, 0);
2232 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2235 /* ----------------------------------------------------------------------- */
2236 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2237 /* ----------------------------------------------------------------------- */
2239 InitLevelSetupDirectory(level_subdir);
2241 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2243 if (!(file = fopen(filename, MODE_WRITE)))
2245 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2250 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2251 getCookie("LEVELSETUP")));
2252 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2254 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2255 handicap_level_str));
2260 SetFilePermissions(filename, PERMS_PRIVATE);