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
88 /* ------------------------------------------------------------------------- */
90 /* ------------------------------------------------------------------------- */
92 static char *getLevelClassDescription(TreeInfo *ldi)
94 int position = ldi->sort_priority / 100;
96 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
97 return levelclass_desc[position];
99 return "Unknown Level Class";
102 static char *getUserLevelDir(char *level_subdir)
104 static char *userlevel_dir = NULL;
105 char *data_dir = getUserDataDir();
106 char *userlevel_subdir = LEVELS_DIRECTORY;
111 if (level_subdir != NULL)
112 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
114 userlevel_dir = getPath2(data_dir, userlevel_subdir);
116 return userlevel_dir;
119 static char *getTapeDir(char *level_subdir)
121 static char *tape_dir = NULL;
122 char *data_dir = getUserDataDir();
123 char *tape_subdir = TAPES_DIRECTORY;
128 if (level_subdir != NULL)
129 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
131 tape_dir = getPath2(data_dir, tape_subdir);
136 static char *getScoreDir(char *level_subdir)
138 static char *score_dir = NULL;
139 char *data_dir = options.rw_base_directory;
140 char *score_subdir = SCORES_DIRECTORY;
145 if (level_subdir != NULL)
146 score_dir = getPath3(data_dir, score_subdir, level_subdir);
148 score_dir = getPath2(data_dir, score_subdir);
153 static char *getLevelSetupDir(char *level_subdir)
155 static char *levelsetup_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
160 free(levelsetup_dir);
162 if (level_subdir != NULL)
163 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
165 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
167 return levelsetup_dir;
170 static char *getCurrentLevelDir()
172 static char *level_dir = NULL;
177 if (leveldir_current == NULL)
178 return options.level_directory;
180 level_dir = getPath2((leveldir_current->user_defined ?
181 getUserLevelDir(NULL) : options.level_directory),
182 leveldir_current->fullpath);
187 static char *getDefaultGraphicsDir(char *graphics_subdir)
189 static char *graphics_dir = NULL;
191 if (graphics_subdir == NULL)
192 return options.graphics_directory;
197 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
202 static char *getDefaultSoundsDir(char *sounds_subdir)
204 static char *sounds_dir = NULL;
206 if (sounds_subdir == NULL)
207 return options.sounds_directory;
212 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
217 static char *getDefaultMusicDir(char *music_subdir)
219 static char *music_dir = NULL;
221 if (music_subdir == NULL)
222 return options.music_directory;
227 music_dir = getPath2(options.music_directory, music_subdir);
232 static char *getUserGraphicsDir()
234 static char *usergraphics_dir = NULL;
236 if (usergraphics_dir == NULL)
237 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
239 return usergraphics_dir;
242 static char *getUserSoundsDir()
244 static char *usersounds_dir = NULL;
246 if (usersounds_dir == NULL)
247 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
249 return usersounds_dir;
252 static char *getUserMusicDir()
254 static char *usermusic_dir = NULL;
256 if (usermusic_dir == NULL)
257 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
259 return usermusic_dir;
262 char *getLevelFilename(int nr)
264 static char *filename = NULL;
265 char basename[MAX_FILENAME_LEN];
267 if (filename != NULL)
270 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
271 filename = getPath2(getCurrentLevelDir(), basename);
276 char *getTapeFilename(int nr)
278 static char *filename = NULL;
279 char basename[MAX_FILENAME_LEN];
281 if (filename != NULL)
284 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
285 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
290 char *getScoreFilename(int nr)
292 static char *filename = NULL;
293 char basename[MAX_FILENAME_LEN];
295 if (filename != NULL)
298 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
299 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
304 char *getSetupFilename()
306 static char *filename = NULL;
308 if (filename != NULL)
311 filename = getPath2(getSetupDir(), SETUP_FILENAME);
316 static char *getSetupArtworkDir(TreeInfo *ti)
318 static char *artwork_dir = NULL;
320 if (artwork_dir != NULL)
323 artwork_dir = getPath2(ti->basepath, ti->fullpath);
328 static char *getCorrectedImageBasename(char *basename)
330 char *result = basename;
332 #if defined(PLATFORM_MSDOS)
333 if (program.filename_prefix != NULL)
335 int prefix_len = strlen(program.filename_prefix);
337 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
338 result = &basename[prefix_len];
345 static boolean fileExists(char *filename)
348 printf("checking file '%s'\n", filename);
351 return (access(filename, F_OK) == 0);
354 char *getCustomImageFilename(char *basename)
356 static char *filename = NULL;
358 if (filename != NULL)
361 basename = getCorrectedImageBasename(basename);
363 /* 1st try: look for special artwork in current level series directory */
364 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
365 if (fileExists(filename))
368 /* 2nd try: look for special artwork in private artwork directory */
369 filename = getPath2(getUserGraphicsDir(), basename);
370 if (fileExists(filename))
373 /* 3rd try: look for special artwork in configured artwork directory */
374 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
375 if (fileExists(filename))
378 /* 4th try: look for default artwork in new default artwork directory */
379 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
380 if (fileExists(filename))
383 /* 5th try: look for default artwork in old default artwork directory */
384 filename = getPath2(options.graphics_directory, basename);
385 if (fileExists(filename))
388 return NULL; /* cannot find image file */
391 char *getCustomSoundFilename(char *basename)
393 static char *filename = NULL;
395 if (filename != NULL)
398 /* 1st try: look for special artwork in current level series directory */
399 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
400 if (fileExists(filename))
403 /* 2nd try: look for special artwork in private artwork directory */
404 filename = getPath2(getUserSoundsDir(), basename);
405 if (fileExists(filename))
408 /* 3rd try: look for special artwork in configured artwork directory */
409 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
410 if (fileExists(filename))
413 /* 4th try: look for default artwork in new default artwork directory */
414 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
415 if (fileExists(filename))
418 /* 5th try: look for default artwork in old default artwork directory */
419 filename = getPath2(options.sounds_directory, basename);
420 if (fileExists(filename))
423 return NULL; /* cannot find image file */
426 char *getCustomSoundConfigFilename()
428 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
431 char *getCustomMusicDirectory(void)
433 static char *directory = NULL;
435 if (directory != NULL)
438 /* 1st try: look for special artwork in current level series directory */
439 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
440 if (fileExists(directory))
443 /* 2nd try: look for special artwork in private artwork directory */
444 directory = getStringCopy(getUserMusicDir());
445 if (fileExists(directory))
448 /* 3rd try: look for special artwork in configured artwork directory */
449 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
450 if (fileExists(directory))
453 /* 4th try: look for default artwork in new default artwork directory */
454 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
455 if (fileExists(directory))
458 /* 5th try: look for default artwork in old default artwork directory */
459 directory = getStringCopy(options.music_directory);
460 if (fileExists(directory))
463 return NULL; /* cannot find image file */
466 void InitTapeDirectory(char *level_subdir)
468 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
469 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
470 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
473 void InitScoreDirectory(char *level_subdir)
475 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
476 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
479 static void SaveUserLevelInfo();
481 void InitUserLevelDirectory(char *level_subdir)
483 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
485 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
486 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
487 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
493 void InitLevelSetupDirectory(char *level_subdir)
495 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
496 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
497 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
500 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
502 int file_version_major, file_version_minor, file_version_patch;
503 int game_version_major, game_version_minor, game_version_patch;
505 file_version_major = fgetc(file);
506 file_version_minor = fgetc(file);
507 file_version_patch = fgetc(file);
508 fgetc(file); /* not used */
510 game_version_major = fgetc(file);
511 game_version_minor = fgetc(file);
512 game_version_patch = fgetc(file);
513 fgetc(file); /* not used */
515 *file_version = VERSION_IDENT(file_version_major,
519 *game_version = VERSION_IDENT(game_version_major,
524 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
526 int file_version_major = VERSION_MAJOR(file_version);
527 int file_version_minor = VERSION_MINOR(file_version);
528 int file_version_patch = VERSION_PATCH(file_version);
529 int game_version_major = VERSION_MAJOR(game_version);
530 int game_version_minor = VERSION_MINOR(game_version);
531 int game_version_patch = VERSION_PATCH(game_version);
533 fputc(file_version_major, file);
534 fputc(file_version_minor, file);
535 fputc(file_version_patch, file);
536 fputc(0, file); /* not used */
538 fputc(game_version_major, file);
539 fputc(game_version_minor, file);
540 fputc(game_version_patch, file);
541 fputc(0, file); /* not used */
545 /* ------------------------------------------------------------------------- */
546 /* some functions to handle lists of level directories */
547 /* ------------------------------------------------------------------------- */
549 TreeInfo *newTreeInfo()
551 return checked_calloc(sizeof(TreeInfo));
554 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
556 node_new->next = *node_first;
557 *node_first = node_new;
560 int numTreeInfo(TreeInfo *node)
573 boolean validLevelSeries(TreeInfo *node)
575 return (node != NULL && !node->node_group && !node->parent_link);
578 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
582 if (node->node_top) /* start with first tree entry */
583 return getFirstValidTreeInfoEntry(*node->node_top);
587 else if (node->node_group) /* enter level group (step down into tree) */
588 return getFirstValidTreeInfoEntry(node->node_group);
589 else if (node->parent_link) /* skip start entry of level group */
591 if (node->next) /* get first real level series entry */
592 return getFirstValidTreeInfoEntry(node->next);
593 else /* leave empty level group and go on */
594 return getFirstValidTreeInfoEntry(node->node_parent->next);
596 else /* this seems to be a regular level series */
600 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
605 if (node->node_parent == NULL) /* top level group */
606 return *node->node_top;
607 else /* sub level group */
608 return node->node_parent->node_group;
611 int numTreeInfoInGroup(TreeInfo *node)
613 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
616 int posTreeInfo(TreeInfo *node)
618 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
623 if (node_cmp == node)
627 node_cmp = node_cmp->next;
633 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
635 TreeInfo *node_default = node;
650 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
652 if (filename == NULL)
657 if (node->node_group)
659 TreeInfo *node_group;
661 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
666 else if (!node->parent_link)
668 if (strcmp(filename, node->filename) == 0)
678 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
680 return getTreeInfoFromFilenameExt(ti, filename);
683 void dumpTreeInfo(TreeInfo *node, int depth)
687 printf("Dumping TreeInfo:\n");
691 for (i=0; i<(depth + 1) * 3; i++)
694 printf("filename == '%s' [%s]\n", node->filename, node->name);
696 if (node->node_group != NULL)
697 dumpTreeInfo(node->node_group, depth + 1);
703 void sortTreeInfo(TreeInfo **node_first,
704 int (*compare_function)(const void *, const void *))
706 int num_nodes = numTreeInfo(*node_first);
707 TreeInfo **sort_array;
708 TreeInfo *node = *node_first;
714 /* allocate array for sorting structure pointers */
715 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
717 /* writing structure pointers to sorting array */
718 while (i < num_nodes && node) /* double boundary check... */
720 sort_array[i] = node;
726 /* sorting the structure pointers in the sorting array */
727 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
730 /* update the linkage of list elements with the sorted node array */
731 for (i=0; i<num_nodes - 1; i++)
732 sort_array[i]->next = sort_array[i + 1];
733 sort_array[num_nodes - 1]->next = NULL;
735 /* update the linkage of the main list anchor pointer */
736 *node_first = sort_array[0];
740 /* now recursively sort the level group structures */
744 if (node->node_group != NULL)
745 sortTreeInfo(&node->node_group, compare_function);
752 /* ========================================================================= */
753 /* some stuff from "files.c" */
754 /* ========================================================================= */
756 #if defined(PLATFORM_WIN32)
758 #define S_IRGRP S_IRUSR
761 #define S_IROTH S_IRUSR
764 #define S_IWGRP S_IWUSR
767 #define S_IWOTH S_IWUSR
770 #define S_IXGRP S_IXUSR
773 #define S_IXOTH S_IXUSR
776 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
781 #endif /* PLATFORM_WIN32 */
783 /* file permissions for newly written files */
784 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
785 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
786 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
788 #define MODE_W_PRIVATE (S_IWUSR)
789 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
790 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
792 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
793 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
795 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
796 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
798 char *getUserDataDir(void)
800 static char *userdata_dir = NULL;
804 char *home_dir = getHomeDir();
805 char *data_dir = program.userdata_directory;
807 userdata_dir = getPath2(home_dir, data_dir);
815 return getUserDataDir();
818 static mode_t posix_umask(mode_t mask)
820 #if defined(PLATFORM_UNIX)
827 static int posix_mkdir(const char *pathname, mode_t mode)
829 #if defined(PLATFORM_WIN32)
830 return mkdir(pathname);
832 return mkdir(pathname, mode);
836 void createDirectory(char *dir, char *text, int permission_class)
838 /* leave "other" permissions in umask untouched, but ensure group parts
839 of USERDATA_DIR_MODE are not masked */
840 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
841 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
842 mode_t normal_umask = posix_umask(0);
843 mode_t group_umask = ~(dir_mode & S_IRWXG);
844 posix_umask(normal_umask & group_umask);
846 if (access(dir, F_OK) != 0)
847 if (posix_mkdir(dir, dir_mode) != 0)
848 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
850 posix_umask(normal_umask); /* reset normal umask */
853 void InitUserDataDirectory()
855 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
858 void SetFilePermissions(char *filename, int permission_class)
860 chmod(filename, (permission_class == PERMS_PRIVATE ?
861 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
864 char *getCookie(char *file_type)
866 static char cookie[MAX_COOKIE_LEN + 1];
868 if (strlen(program.cookie_prefix) + 1 +
869 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
870 return "[COOKIE ERROR]"; /* should never happen */
872 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
873 program.cookie_prefix, file_type,
874 program.version_major, program.version_minor);
879 int getFileVersionFromCookieString(const char *cookie)
881 const char *ptr_cookie1, *ptr_cookie2;
882 const char *pattern1 = "_FILE_VERSION_";
883 const char *pattern2 = "?.?";
884 const int len_cookie = strlen(cookie);
885 const int len_pattern1 = strlen(pattern1);
886 const int len_pattern2 = strlen(pattern2);
887 const int len_pattern = len_pattern1 + len_pattern2;
888 int version_major, version_minor;
890 if (len_cookie <= len_pattern)
893 ptr_cookie1 = &cookie[len_cookie - len_pattern];
894 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
896 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
899 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
900 ptr_cookie2[1] != '.' ||
901 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
904 version_major = ptr_cookie2[0] - '0';
905 version_minor = ptr_cookie2[2] - '0';
907 return VERSION_IDENT(version_major, version_minor, 0);
910 boolean checkCookieString(const char *cookie, const char *template)
912 const char *pattern = "_FILE_VERSION_?.?";
913 const int len_cookie = strlen(cookie);
914 const int len_template = strlen(template);
915 const int len_pattern = strlen(pattern);
917 if (len_cookie != len_template)
920 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
926 /* ------------------------------------------------------------------------- */
927 /* setup file list handling functions */
928 /* ------------------------------------------------------------------------- */
930 int get_string_integer_value(char *s)
932 static char *number_text[][3] =
934 { "0", "zero", "null", },
935 { "1", "one", "first" },
936 { "2", "two", "second" },
937 { "3", "three", "third" },
938 { "4", "four", "fourth" },
939 { "5", "five", "fifth" },
940 { "6", "six", "sixth" },
941 { "7", "seven", "seventh" },
942 { "8", "eight", "eighth" },
943 { "9", "nine", "ninth" },
944 { "10", "ten", "tenth" },
945 { "11", "eleven", "eleventh" },
946 { "12", "twelve", "twelfth" },
950 char *s_lower = getStringToLower(s);
955 if (strcmp(s_lower, number_text[i][j]) == 0)
966 boolean get_string_boolean_value(char *s)
968 char *s_lower = getStringToLower(s);
969 boolean result = FALSE;
971 if (strcmp(s_lower, "true") == 0 ||
972 strcmp(s_lower, "yes") == 0 ||
973 strcmp(s_lower, "on") == 0 ||
974 get_string_integer_value(s) == 1)
982 char *getFormattedSetupEntry(char *token, char *value)
985 static char entry[MAX_LINE_LEN];
987 /* start with the token and some spaces to format output line */
988 sprintf(entry, "%s:", token);
989 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
992 /* continue with the token's value */
993 strcat(entry, value);
998 void freeSetupFileList(struct SetupFileList *setup_file_list)
1000 if (!setup_file_list)
1003 if (setup_file_list->token)
1004 free(setup_file_list->token);
1005 if (setup_file_list->value)
1006 free(setup_file_list->value);
1007 if (setup_file_list->next)
1008 freeSetupFileList(setup_file_list->next);
1009 free(setup_file_list);
1012 static struct SetupFileList *newSetupFileList(char *token, char *value)
1014 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1016 new->token = checked_malloc(strlen(token) + 1);
1017 strcpy(new->token, token);
1019 new->value = checked_malloc(strlen(value) + 1);
1020 strcpy(new->value, value);
1027 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1029 if (!setup_file_list)
1032 if (strcmp(setup_file_list->token, token) == 0)
1033 return setup_file_list->value;
1035 return getTokenValue(setup_file_list->next, token);
1038 static void setTokenValue(struct SetupFileList *setup_file_list,
1039 char *token, char *value)
1041 if (!setup_file_list)
1044 if (strcmp(setup_file_list->token, token) == 0)
1046 free(setup_file_list->value);
1047 setup_file_list->value = checked_malloc(strlen(value) + 1);
1048 strcpy(setup_file_list->value, value);
1050 else if (setup_file_list->next == NULL)
1051 setup_file_list->next = newSetupFileList(token, value);
1053 setTokenValue(setup_file_list->next, token, value);
1057 static void printSetupFileList(struct SetupFileList *setup_file_list)
1059 if (!setup_file_list)
1062 printf("token: '%s'\n", setup_file_list->token);
1063 printf("value: '%s'\n", setup_file_list->value);
1065 printSetupFileList(setup_file_list->next);
1069 struct SetupFileList *loadSetupFileList(char *filename)
1072 char line[MAX_LINE_LEN];
1073 char *token, *value, *line_ptr;
1074 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1075 struct SetupFileList *first_valid_list_entry;
1079 if (!(file = fopen(filename, MODE_READ)))
1081 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1087 /* read next line of input file */
1088 if (!fgets(line, MAX_LINE_LEN, file))
1091 /* cut trailing comment or whitespace from input line */
1092 for (line_ptr = line; *line_ptr; line_ptr++)
1094 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1101 /* cut trailing whitespaces from input line */
1102 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1103 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1106 /* ignore empty lines */
1110 line_len = strlen(line);
1112 /* cut leading whitespaces from token */
1113 for (token = line; *token; token++)
1114 if (*token != ' ' && *token != '\t')
1117 /* find end of token */
1118 for (line_ptr = token; *line_ptr; line_ptr++)
1120 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1127 if (line_ptr < line + line_len)
1128 value = line_ptr + 1;
1132 /* cut leading whitespaces from value */
1133 for (; *value; value++)
1134 if (*value != ' ' && *value != '\t')
1137 if (*token && *value)
1138 setTokenValue(setup_file_list, token, value);
1143 first_valid_list_entry = setup_file_list->next;
1145 /* free empty list header */
1146 setup_file_list->next = NULL;
1147 freeSetupFileList(setup_file_list);
1149 if (first_valid_list_entry == NULL)
1150 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1152 return first_valid_list_entry;
1155 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1158 if (!setup_file_list)
1161 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1163 if (!checkCookieString(setup_file_list->value, identifier))
1165 Error(ERR_WARN, "configuration file has wrong file identifier");
1172 if (setup_file_list->next)
1173 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1176 Error(ERR_WARN, "configuration file has no file identifier");
1182 /* ========================================================================= */
1183 /* setup file stuff */
1184 /* ========================================================================= */
1186 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1187 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1188 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1190 /* level directory info */
1191 #define LEVELINFO_TOKEN_NAME 0
1192 #define LEVELINFO_TOKEN_NAME_SHORT 1
1193 #define LEVELINFO_TOKEN_NAME_SORTING 2
1194 #define LEVELINFO_TOKEN_AUTHOR 3
1195 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1196 #define LEVELINFO_TOKEN_LEVELS 5
1197 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1198 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1199 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1200 #define LEVELINFO_TOKEN_READONLY 9
1202 #define NUM_LEVELINFO_TOKENS 10
1204 static LevelDirTree ldi;
1206 static struct TokenInfo levelinfo_tokens[] =
1208 /* level directory info */
1209 { TYPE_STRING, &ldi.name, "name" },
1210 { TYPE_STRING, &ldi.name_short, "name_short" },
1211 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1212 { TYPE_STRING, &ldi.author, "author" },
1213 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1214 { TYPE_INTEGER, &ldi.levels, "levels" },
1215 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1216 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1217 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1218 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1221 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1225 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1226 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1227 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1228 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1231 ldi->node_parent = NULL;
1232 ldi->node_group = NULL;
1236 ldi->cl_cursor = -1;
1238 ldi->filename = NULL;
1239 ldi->fullpath = NULL;
1240 ldi->basepath = NULL;
1241 ldi->name = getStringCopy(ANONYMOUS_NAME);
1242 ldi->name_short = NULL;
1243 ldi->name_sorting = NULL;
1244 ldi->author = getStringCopy(ANONYMOUS_NAME);
1246 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1247 ldi->parent_link = FALSE;
1248 ldi->user_defined = FALSE;
1250 ldi->class_desc = NULL;
1252 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1254 ldi->imported_from = NULL;
1256 ldi->first_level = 0;
1257 ldi->last_level = 0;
1258 ldi->level_group = FALSE;
1259 ldi->handicap_level = 0;
1260 ldi->readonly = TRUE;
1264 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1268 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1270 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1274 /* first copy all values from the parent structure ... */
1277 /* ... then set all fields to default that cannot be inherited from parent.
1278 This is especially important for all those fields that can be set from
1279 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1280 calls 'free()' for all already set token values which requires that no
1281 other structure's pointer may point to them!
1284 ldi->filename = NULL;
1285 ldi->fullpath = NULL;
1286 ldi->basepath = NULL;
1287 ldi->name = getStringCopy(ANONYMOUS_NAME);
1288 ldi->name_short = NULL;
1289 ldi->name_sorting = NULL;
1290 ldi->author = getStringCopy(parent->author);
1291 ldi->imported_from = getStringCopy(parent->imported_from);
1293 ldi->level_group = FALSE;
1294 ldi->parent_link = FALSE;
1296 ldi->node_top = parent->node_top;
1297 ldi->node_parent = parent;
1298 ldi->node_group = NULL;
1302 void setSetupInfo(struct TokenInfo *token_info,
1303 int token_nr, char *token_value)
1305 int token_type = token_info[token_nr].type;
1306 void *setup_value = token_info[token_nr].value;
1308 if (token_value == NULL)
1311 /* set setup field to corresponding token value */
1316 *(boolean *)setup_value = get_string_boolean_value(token_value);
1320 *(Key *)setup_value = getKeyFromKeyName(token_value);
1324 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1328 *(int *)setup_value = get_string_integer_value(token_value);
1332 if (*(char **)setup_value != NULL)
1333 free(*(char **)setup_value);
1334 *(char **)setup_value = getStringCopy(token_value);
1342 static int compareTreeInfoEntries(const void *object1, const void *object2)
1344 const TreeInfo *entry1 = *((TreeInfo **)object1);
1345 const TreeInfo *entry2 = *((TreeInfo **)object2);
1348 if (entry1->parent_link || entry2->parent_link)
1349 compare_result = (entry1->parent_link ? -1 : +1);
1350 else if (entry1->sort_priority == entry2->sort_priority)
1352 char *name1 = getStringToLower(entry1->name_sorting);
1353 char *name2 = getStringToLower(entry2->name_sorting);
1355 compare_result = strcmp(name1, name2);
1360 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1361 compare_result = entry1->sort_priority - entry2->sort_priority;
1363 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1365 return compare_result;
1368 static void createParentTreeInfoNode(TreeInfo *node_parent)
1372 if (node_parent == NULL)
1375 ti_new = newTreeInfo();
1376 setTreeInfoToDefaults(ti_new, node_parent->type);
1378 ti_new->node_parent = node_parent;
1379 ti_new->parent_link = TRUE;
1381 ti_new->name = ".. (parent directory)";
1382 ti_new->name_short = getStringCopy(ti_new->name);
1383 ti_new->name_sorting = getStringCopy(ti_new->name);
1385 ti_new->filename = "..";
1386 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1388 ti_new->sort_priority = node_parent->sort_priority;
1389 ti_new->class_desc = getLevelClassDescription(ti_new);
1391 pushTreeInfo(&node_parent->node_group, ti_new);
1394 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1395 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1397 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1398 TreeInfo *node_parent,
1399 char *level_directory,
1400 char *directory_name)
1402 char *directory_path = getPath2(level_directory, directory_name);
1403 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1404 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1405 LevelDirTree *leveldir_new = NULL;
1408 if (setup_file_list == NULL)
1410 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1412 free(directory_path);
1418 leveldir_new = newTreeInfo();
1421 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1423 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1425 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1427 /* set all structure fields according to the token/value pairs */
1428 ldi = *leveldir_new;
1429 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1430 setSetupInfo(levelinfo_tokens, i,
1431 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1432 *leveldir_new = ldi;
1434 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1436 if (leveldir_new->name_short == NULL)
1437 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1439 if (leveldir_new->name_sorting == NULL)
1440 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1442 leveldir_new->filename = getStringCopy(directory_name);
1444 if (node_parent == NULL) /* top level group */
1446 leveldir_new->basepath = level_directory;
1447 leveldir_new->fullpath = leveldir_new->filename;
1449 else /* sub level group */
1451 leveldir_new->basepath = node_parent->basepath;
1452 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1455 if (leveldir_new->levels < 1)
1456 leveldir_new->levels = 1;
1458 leveldir_new->last_level =
1459 leveldir_new->first_level + leveldir_new->levels - 1;
1461 leveldir_new->user_defined =
1462 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1464 leveldir_new->color = LEVELCOLOR(leveldir_new);
1465 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1467 leveldir_new->handicap_level = /* set handicap to default value */
1468 (leveldir_new->user_defined ?
1469 leveldir_new->last_level :
1470 leveldir_new->first_level);
1472 pushTreeInfo(node_first, leveldir_new);
1474 freeSetupFileList(setup_file_list);
1476 if (leveldir_new->level_group)
1478 /* create node to link back to current level directory */
1479 createParentTreeInfoNode(leveldir_new);
1481 /* step into sub-directory and look for more level series */
1482 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1483 leveldir_new, directory_path);
1486 free(directory_path);
1492 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1493 TreeInfo *node_parent,
1494 char *level_directory)
1497 struct dirent *dir_entry;
1498 boolean valid_entry_found = FALSE;
1500 if ((dir = opendir(level_directory)) == NULL)
1502 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1506 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1508 struct stat file_status;
1509 char *directory_name = dir_entry->d_name;
1510 char *directory_path = getPath2(level_directory, directory_name);
1512 /* skip entries for current and parent directory */
1513 if (strcmp(directory_name, ".") == 0 ||
1514 strcmp(directory_name, "..") == 0)
1516 free(directory_path);
1520 /* find out if directory entry is itself a directory */
1521 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1522 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1524 free(directory_path);
1528 free(directory_path);
1530 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1537 if (!valid_entry_found)
1539 /* check if this directory directly contains a file "levelinfo.conf" */
1540 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1541 level_directory, ".");
1544 if (!valid_entry_found)
1545 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1549 void LoadLevelInfo()
1551 InitUserLevelDirectory(getLoginName());
1553 DrawInitText("Loading level series:", 120, FC_GREEN);
1555 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1556 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1558 /* before sorting, the first entries will be from the user directory */
1559 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1561 if (leveldir_first == NULL)
1562 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1564 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1567 dumpTreeInfo(leveldir_first, 0);
1571 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1572 TreeInfo *node_parent,
1573 char *base_directory,
1574 char *directory_name, int type)
1576 char *directory_path = getPath2(base_directory, directory_name);
1578 getPath2(directory_path,
1579 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1580 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1581 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1582 struct SetupFileList *setup_file_list = NULL;
1583 TreeInfo *artwork_new = NULL;
1584 char *check_dir = NULL;
1587 if (access(filename, F_OK) == 0) /* file exists */
1588 loadSetupFileList(filename);
1590 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1593 struct dirent *dir_entry;
1594 boolean valid_file_found = FALSE;
1596 if ((dir = opendir(directory_path)) != NULL)
1598 while ((dir_entry = readdir(dir)) != NULL)
1600 char *entry_name = dir_entry->d_name;
1602 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1603 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1604 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1606 valid_file_found = TRUE;
1614 if (!valid_file_found)
1617 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1619 free(directory_path);
1626 artwork_new = newTreeInfo();
1629 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1631 setTreeInfoToDefaults(artwork_new, type);
1633 artwork_new->filename = getStringCopy(directory_name);
1635 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1638 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1641 /* set all structure fields according to the token/value pairs */
1643 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1644 setSetupInfo(levelinfo_tokens, i,
1645 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1648 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1650 if (artwork_new->name_short == NULL)
1651 artwork_new->name_short = getStringCopy(artwork_new->name);
1653 if (artwork_new->name_sorting == NULL)
1654 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1657 if (node_parent == NULL) /* top level group */
1659 artwork_new->basepath = getStringCopy(base_directory);
1660 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1662 else /* sub level group */
1664 artwork_new->basepath = getStringCopy(node_parent->basepath);
1665 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1668 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1669 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1670 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1671 artwork_new->user_defined =
1672 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1674 /* (may use ".sort_priority" from "setup_file_list" above) */
1675 artwork_new->color = LEVELCOLOR(artwork_new);
1676 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1678 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1680 if (artwork_new->name != NULL)
1681 free(artwork_new->name);
1683 if (strcmp(artwork_new->filename, ".") == 0)
1685 if (artwork_new->user_defined)
1686 artwork_new->name = getStringCopy("private");
1688 artwork_new->name = getStringCopy("default");
1691 artwork_new->name = getStringCopy(artwork_new->filename);
1693 artwork_new->name_short = getStringCopy(artwork_new->name);
1694 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1697 pushTreeInfo(node_first, artwork_new);
1699 freeSetupFileList(setup_file_list);
1701 free(directory_path);
1707 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1708 TreeInfo *node_parent,
1709 char *base_directory, int type)
1712 struct dirent *dir_entry;
1713 boolean valid_entry_found = FALSE;
1715 if ((dir = opendir(base_directory)) == NULL)
1717 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1718 base_directory == options.graphics_directory) ||
1719 (type == TREE_TYPE_SOUNDS_DIR &&
1720 base_directory == options.sounds_directory) ||
1721 (type == TREE_TYPE_MUSIC_DIR &&
1722 base_directory == options.music_directory))
1723 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1727 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1729 struct stat file_status;
1730 char *directory_name = dir_entry->d_name;
1731 char *directory_path = getPath2(base_directory, directory_name);
1733 /* skip entries for current and parent directory */
1734 if (strcmp(directory_name, ".") == 0 ||
1735 strcmp(directory_name, "..") == 0)
1737 free(directory_path);
1741 /* find out if directory entry is itself a directory */
1742 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1743 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1745 free(directory_path);
1749 free(directory_path);
1751 /* check if this directory contains artwork with or without config file */
1752 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1754 directory_name, type);
1759 /* check if this directory directly contains artwork itself */
1760 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1761 base_directory, ".",
1763 if (!valid_entry_found)
1764 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1768 void LoadArtworkInfo()
1770 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1772 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1773 options.graphics_directory,
1774 TREE_TYPE_GRAPHICS_DIR);
1775 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1776 getUserGraphicsDir(),
1777 TREE_TYPE_GRAPHICS_DIR);
1779 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1780 options.sounds_directory,
1781 TREE_TYPE_SOUNDS_DIR);
1782 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1784 TREE_TYPE_SOUNDS_DIR);
1786 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1787 options.music_directory,
1788 TREE_TYPE_MUSIC_DIR);
1789 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1791 TREE_TYPE_MUSIC_DIR);
1793 /* before sorting, the first entries will be from the user directory */
1794 artwork.gfx_current =
1795 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1796 if (artwork.gfx_current == NULL)
1797 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1799 artwork.snd_current =
1800 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1801 if (artwork.snd_current == NULL)
1802 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1804 artwork.mus_current =
1805 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1806 if (artwork.mus_current == NULL)
1807 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1809 artwork.graphics_set_current = artwork.gfx_current->name;
1810 artwork.sounds_set_current = artwork.snd_current->name;
1811 artwork.music_set_current = artwork.mus_current->name;
1814 printf("graphics set == %s\n\n", artwork.graphics_set_current);
1815 printf("sounds set == %s\n\n", artwork.sounds_set_current);
1816 printf("music set == %s\n\n", artwork.music_set_current);
1819 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1820 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1821 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1824 dumpTreeInfo(artwork.gfx_first, 0);
1825 dumpTreeInfo(artwork.snd_first, 0);
1826 dumpTreeInfo(artwork.mus_first, 0);
1830 static void SaveUserLevelInfo()
1836 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1838 if (!(file = fopen(filename, MODE_WRITE)))
1840 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1845 /* always start with reliable default values */
1846 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1848 ldi.name = getLoginName();
1849 ldi.author = getRealName();
1851 ldi.first_level = 1;
1852 ldi.sort_priority = LEVELCLASS_USER_START;
1853 ldi.readonly = FALSE;
1855 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1856 getCookie("LEVELINFO")));
1858 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1859 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1860 i != LEVELINFO_TOKEN_NAME_SORTING &&
1861 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1862 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1867 SetFilePermissions(filename, PERMS_PRIVATE);
1870 char *getSetupValue(int type, void *value)
1872 static char value_string[MAX_LINE_LEN];
1877 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1881 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1885 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1889 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1893 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1897 sprintf(value_string, "%d", *(int *)value);
1901 strcpy(value_string, *(char **)value);
1905 value_string[0] = '\0';
1909 return value_string;
1912 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1916 static char token_string[MAX_LINE_LEN];
1917 int token_type = token_info[token_nr].type;
1918 void *setup_value = token_info[token_nr].value;
1919 char *token_text = token_info[token_nr].text;
1920 char *value_string = getSetupValue(token_type, setup_value);
1922 /* build complete token string */
1923 sprintf(token_string, "%s%s", prefix, token_text);
1925 /* build setup entry line */
1926 line = getFormattedSetupEntry(token_string, value_string);
1928 if (token_type == TYPE_KEY_X11)
1930 Key key = *(Key *)setup_value;
1931 char *keyname = getKeyNameFromKey(key);
1933 /* add comment, if useful */
1934 if (strcmp(keyname, "(undefined)") != 0 &&
1935 strcmp(keyname, "(unknown)") != 0)
1937 /* add at least one whitespace */
1939 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
1943 strcat(line, keyname);
1950 void LoadLevelSetup_LastSeries()
1953 struct SetupFileList *level_setup_list = NULL;
1955 /* always start with reliable default values */
1956 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1958 /* ----------------------------------------------------------------------- */
1959 /* ~/.<program>/levelsetup.conf */
1960 /* ----------------------------------------------------------------------- */
1962 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1964 if ((level_setup_list = loadSetupFileList(filename)))
1966 char *last_level_series =
1967 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1969 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1971 if (leveldir_current == NULL)
1972 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1974 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1976 freeSetupFileList(level_setup_list);
1979 Error(ERR_WARN, "using default setup values");
1984 void SaveLevelSetup_LastSeries()
1987 char *level_subdir = leveldir_current->filename;
1990 /* ----------------------------------------------------------------------- */
1991 /* ~/.<program>/levelsetup.conf */
1992 /* ----------------------------------------------------------------------- */
1994 InitUserDataDirectory();
1996 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1998 if (!(file = fopen(filename, MODE_WRITE)))
2000 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2005 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2006 getCookie("LEVELSETUP")));
2007 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2013 SetFilePermissions(filename, PERMS_PRIVATE);
2016 static void checkSeriesInfo()
2018 static char *level_directory = NULL;
2020 struct dirent *dir_entry;
2022 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2024 level_directory = getPath2((leveldir_current->user_defined ?
2025 getUserLevelDir(NULL) :
2026 options.level_directory),
2027 leveldir_current->fullpath);
2029 if ((dir = opendir(level_directory)) == NULL)
2031 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2035 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2037 if (strlen(dir_entry->d_name) > 4 &&
2038 dir_entry->d_name[3] == '.' &&
2039 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2041 char levelnum_str[4];
2044 strncpy(levelnum_str, dir_entry->d_name, 3);
2045 levelnum_str[3] = '\0';
2047 levelnum_value = atoi(levelnum_str);
2049 if (levelnum_value < leveldir_current->first_level)
2051 Error(ERR_WARN, "additional level %d found", levelnum_value);
2052 leveldir_current->first_level = levelnum_value;
2054 else if (levelnum_value > leveldir_current->last_level)
2056 Error(ERR_WARN, "additional level %d found", levelnum_value);
2057 leveldir_current->last_level = levelnum_value;
2065 void LoadLevelSetup_SeriesInfo()
2068 struct SetupFileList *level_setup_list = NULL;
2069 char *level_subdir = leveldir_current->filename;
2071 /* always start with reliable default values */
2072 level_nr = leveldir_current->first_level;
2074 checkSeriesInfo(leveldir_current);
2076 /* ----------------------------------------------------------------------- */
2077 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2078 /* ----------------------------------------------------------------------- */
2080 level_subdir = leveldir_current->filename;
2082 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2084 if ((level_setup_list = loadSetupFileList(filename)))
2088 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2092 level_nr = atoi(token_value);
2094 if (level_nr < leveldir_current->first_level)
2095 level_nr = leveldir_current->first_level;
2096 if (level_nr > leveldir_current->last_level)
2097 level_nr = leveldir_current->last_level;
2100 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2104 int level_nr = atoi(token_value);
2106 if (level_nr < leveldir_current->first_level)
2107 level_nr = leveldir_current->first_level;
2108 if (level_nr > leveldir_current->last_level + 1)
2109 level_nr = leveldir_current->last_level;
2111 if (leveldir_current->user_defined)
2112 level_nr = leveldir_current->last_level;
2114 leveldir_current->handicap_level = level_nr;
2117 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2119 freeSetupFileList(level_setup_list);
2122 Error(ERR_WARN, "using default setup values");
2127 void SaveLevelSetup_SeriesInfo()
2130 char *level_subdir = leveldir_current->filename;
2131 char *level_nr_str = int2str(level_nr, 0);
2132 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2135 /* ----------------------------------------------------------------------- */
2136 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2137 /* ----------------------------------------------------------------------- */
2139 InitLevelSetupDirectory(level_subdir);
2141 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2143 if (!(file = fopen(filename, MODE_WRITE)))
2145 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2150 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2151 getCookie("LEVELSETUP")));
2152 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2154 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2155 handicap_level_str));
2160 SetFilePermissions(filename, PERMS_PRIVATE);