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 sprintf(entry, "%s:", token);
988 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
992 strcat(entry, value);
997 void freeSetupFileList(struct SetupFileList *setup_file_list)
999 if (!setup_file_list)
1002 if (setup_file_list->token)
1003 free(setup_file_list->token);
1004 if (setup_file_list->value)
1005 free(setup_file_list->value);
1006 if (setup_file_list->next)
1007 freeSetupFileList(setup_file_list->next);
1008 free(setup_file_list);
1011 static struct SetupFileList *newSetupFileList(char *token, char *value)
1013 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1015 new->token = checked_malloc(strlen(token) + 1);
1016 strcpy(new->token, token);
1018 new->value = checked_malloc(strlen(value) + 1);
1019 strcpy(new->value, value);
1026 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1028 if (!setup_file_list)
1031 if (strcmp(setup_file_list->token, token) == 0)
1032 return setup_file_list->value;
1034 return getTokenValue(setup_file_list->next, token);
1037 static void setTokenValue(struct SetupFileList *setup_file_list,
1038 char *token, char *value)
1040 if (!setup_file_list)
1043 if (strcmp(setup_file_list->token, token) == 0)
1045 free(setup_file_list->value);
1046 setup_file_list->value = checked_malloc(strlen(value) + 1);
1047 strcpy(setup_file_list->value, value);
1049 else if (setup_file_list->next == NULL)
1050 setup_file_list->next = newSetupFileList(token, value);
1052 setTokenValue(setup_file_list->next, token, value);
1056 static void printSetupFileList(struct SetupFileList *setup_file_list)
1058 if (!setup_file_list)
1061 printf("token: '%s'\n", setup_file_list->token);
1062 printf("value: '%s'\n", setup_file_list->value);
1064 printSetupFileList(setup_file_list->next);
1068 struct SetupFileList *loadSetupFileList(char *filename)
1071 char line[MAX_LINE_LEN];
1072 char *token, *value, *line_ptr;
1073 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1074 struct SetupFileList *first_valid_list_entry;
1078 if (!(file = fopen(filename, MODE_READ)))
1080 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1086 /* read next line of input file */
1087 if (!fgets(line, MAX_LINE_LEN, file))
1090 /* cut trailing comment or whitespace from input line */
1091 for (line_ptr = line; *line_ptr; line_ptr++)
1093 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1100 /* cut trailing whitespaces from input line */
1101 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1102 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1105 /* ignore empty lines */
1109 line_len = strlen(line);
1111 /* cut leading whitespaces from token */
1112 for (token = line; *token; token++)
1113 if (*token != ' ' && *token != '\t')
1116 /* find end of token */
1117 for (line_ptr = token; *line_ptr; line_ptr++)
1119 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1126 if (line_ptr < line + line_len)
1127 value = line_ptr + 1;
1131 /* cut leading whitespaces from value */
1132 for (; *value; value++)
1133 if (*value != ' ' && *value != '\t')
1136 if (*token && *value)
1137 setTokenValue(setup_file_list, token, value);
1142 first_valid_list_entry = setup_file_list->next;
1144 /* free empty list header */
1145 setup_file_list->next = NULL;
1146 freeSetupFileList(setup_file_list);
1148 if (first_valid_list_entry == NULL)
1149 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1151 return first_valid_list_entry;
1154 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1157 if (!setup_file_list)
1160 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1162 if (!checkCookieString(setup_file_list->value, identifier))
1164 Error(ERR_WARN, "configuration file has wrong file identifier");
1171 if (setup_file_list->next)
1172 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1175 Error(ERR_WARN, "configuration file has no file identifier");
1181 /* ========================================================================= */
1182 /* setup file stuff */
1183 /* ========================================================================= */
1185 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1186 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1187 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1189 /* level directory info */
1190 #define LEVELINFO_TOKEN_NAME 0
1191 #define LEVELINFO_TOKEN_NAME_SHORT 1
1192 #define LEVELINFO_TOKEN_NAME_SORTING 2
1193 #define LEVELINFO_TOKEN_AUTHOR 3
1194 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1195 #define LEVELINFO_TOKEN_LEVELS 5
1196 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1197 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1198 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1199 #define LEVELINFO_TOKEN_READONLY 9
1201 #define NUM_LEVELINFO_TOKENS 10
1203 static LevelDirTree ldi;
1205 static struct TokenInfo levelinfo_tokens[] =
1207 /* level directory info */
1208 { TYPE_STRING, &ldi.name, "name" },
1209 { TYPE_STRING, &ldi.name_short, "name_short" },
1210 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1211 { TYPE_STRING, &ldi.author, "author" },
1212 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1213 { TYPE_INTEGER, &ldi.levels, "levels" },
1214 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1215 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1216 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1217 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1220 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1224 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1225 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1226 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1227 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1230 ldi->node_parent = NULL;
1231 ldi->node_group = NULL;
1235 ldi->cl_cursor = -1;
1237 ldi->filename = NULL;
1238 ldi->fullpath = NULL;
1239 ldi->basepath = NULL;
1240 ldi->name = getStringCopy(ANONYMOUS_NAME);
1241 ldi->name_short = NULL;
1242 ldi->name_sorting = NULL;
1243 ldi->author = getStringCopy(ANONYMOUS_NAME);
1245 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1246 ldi->parent_link = FALSE;
1247 ldi->user_defined = FALSE;
1249 ldi->class_desc = NULL;
1251 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1253 ldi->imported_from = NULL;
1255 ldi->first_level = 0;
1256 ldi->last_level = 0;
1257 ldi->level_group = FALSE;
1258 ldi->handicap_level = 0;
1259 ldi->readonly = TRUE;
1263 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1267 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1269 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1273 /* first copy all values from the parent structure ... */
1276 /* ... then set all fields to default that cannot be inherited from parent.
1277 This is especially important for all those fields that can be set from
1278 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1279 calls 'free()' for all already set token values which requires that no
1280 other structure's pointer may point to them!
1283 ldi->filename = NULL;
1284 ldi->fullpath = NULL;
1285 ldi->basepath = NULL;
1286 ldi->name = getStringCopy(ANONYMOUS_NAME);
1287 ldi->name_short = NULL;
1288 ldi->name_sorting = NULL;
1289 ldi->author = getStringCopy(parent->author);
1290 ldi->imported_from = getStringCopy(parent->imported_from);
1292 ldi->level_group = FALSE;
1293 ldi->parent_link = FALSE;
1295 ldi->node_top = parent->node_top;
1296 ldi->node_parent = parent;
1297 ldi->node_group = NULL;
1301 void setSetupInfo(struct TokenInfo *token_info,
1302 int token_nr, char *token_value)
1304 int token_type = token_info[token_nr].type;
1305 void *setup_value = token_info[token_nr].value;
1307 if (token_value == NULL)
1310 /* set setup field to corresponding token value */
1315 *(boolean *)setup_value = get_string_boolean_value(token_value);
1319 *(Key *)setup_value = getKeyFromKeyName(token_value);
1323 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1327 *(int *)setup_value = get_string_integer_value(token_value);
1331 if (*(char **)setup_value != NULL)
1332 free(*(char **)setup_value);
1333 *(char **)setup_value = getStringCopy(token_value);
1341 static int compareTreeInfoEntries(const void *object1, const void *object2)
1343 const TreeInfo *entry1 = *((TreeInfo **)object1);
1344 const TreeInfo *entry2 = *((TreeInfo **)object2);
1347 if (entry1->parent_link || entry2->parent_link)
1348 compare_result = (entry1->parent_link ? -1 : +1);
1349 else if (entry1->sort_priority == entry2->sort_priority)
1351 char *name1 = getStringToLower(entry1->name_sorting);
1352 char *name2 = getStringToLower(entry2->name_sorting);
1354 compare_result = strcmp(name1, name2);
1359 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1360 compare_result = entry1->sort_priority - entry2->sort_priority;
1362 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1364 return compare_result;
1367 static void createParentTreeInfoNode(TreeInfo *node_parent)
1371 if (node_parent == NULL)
1374 ti_new = newTreeInfo();
1375 setTreeInfoToDefaults(ti_new, node_parent->type);
1377 ti_new->node_parent = node_parent;
1378 ti_new->parent_link = TRUE;
1380 ti_new->name = ".. (parent directory)";
1381 ti_new->name_short = getStringCopy(ti_new->name);
1382 ti_new->name_sorting = getStringCopy(ti_new->name);
1384 ti_new->filename = "..";
1385 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1387 ti_new->sort_priority = node_parent->sort_priority;
1388 ti_new->class_desc = getLevelClassDescription(ti_new);
1390 pushTreeInfo(&node_parent->node_group, ti_new);
1393 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1394 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1396 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1397 TreeInfo *node_parent,
1398 char *level_directory,
1399 char *directory_name)
1401 char *directory_path = getPath2(level_directory, directory_name);
1402 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1403 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1404 LevelDirTree *leveldir_new = NULL;
1407 if (setup_file_list == NULL)
1409 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1411 free(directory_path);
1417 leveldir_new = newTreeInfo();
1420 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1422 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1424 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1426 /* set all structure fields according to the token/value pairs */
1427 ldi = *leveldir_new;
1428 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1429 setSetupInfo(levelinfo_tokens, i,
1430 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1431 *leveldir_new = ldi;
1433 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1435 if (leveldir_new->name_short == NULL)
1436 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1438 if (leveldir_new->name_sorting == NULL)
1439 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1441 leveldir_new->filename = getStringCopy(directory_name);
1443 if (node_parent == NULL) /* top level group */
1445 leveldir_new->basepath = level_directory;
1446 leveldir_new->fullpath = leveldir_new->filename;
1448 else /* sub level group */
1450 leveldir_new->basepath = node_parent->basepath;
1451 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1454 if (leveldir_new->levels < 1)
1455 leveldir_new->levels = 1;
1457 leveldir_new->last_level =
1458 leveldir_new->first_level + leveldir_new->levels - 1;
1460 leveldir_new->user_defined =
1461 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1463 leveldir_new->color = LEVELCOLOR(leveldir_new);
1464 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1466 leveldir_new->handicap_level = /* set handicap to default value */
1467 (leveldir_new->user_defined ?
1468 leveldir_new->last_level :
1469 leveldir_new->first_level);
1471 pushTreeInfo(node_first, leveldir_new);
1473 freeSetupFileList(setup_file_list);
1475 if (leveldir_new->level_group)
1477 /* create node to link back to current level directory */
1478 createParentTreeInfoNode(leveldir_new);
1480 /* step into sub-directory and look for more level series */
1481 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1482 leveldir_new, directory_path);
1485 free(directory_path);
1491 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1492 TreeInfo *node_parent,
1493 char *level_directory)
1496 struct dirent *dir_entry;
1497 boolean valid_entry_found = FALSE;
1499 if ((dir = opendir(level_directory)) == NULL)
1501 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1505 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1507 struct stat file_status;
1508 char *directory_name = dir_entry->d_name;
1509 char *directory_path = getPath2(level_directory, directory_name);
1511 /* skip entries for current and parent directory */
1512 if (strcmp(directory_name, ".") == 0 ||
1513 strcmp(directory_name, "..") == 0)
1515 free(directory_path);
1519 /* find out if directory entry is itself a directory */
1520 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1521 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1523 free(directory_path);
1527 free(directory_path);
1529 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1536 if (!valid_entry_found)
1538 /* check if this directory directly contains a file "levelinfo.conf" */
1539 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1540 level_directory, ".");
1543 if (!valid_entry_found)
1544 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1548 void LoadLevelInfo()
1550 InitUserLevelDirectory(getLoginName());
1552 DrawInitText("Loading level series:", 120, FC_GREEN);
1554 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1555 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1557 /* before sorting, the first entries will be from the user directory */
1558 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1560 if (leveldir_first == NULL)
1561 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1563 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1566 dumpTreeInfo(leveldir_first, 0);
1570 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1571 TreeInfo *node_parent,
1572 char *base_directory,
1573 char *directory_name, int type)
1575 char *directory_path = getPath2(base_directory, directory_name);
1577 getPath2(directory_path,
1578 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1579 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1580 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1581 struct SetupFileList *setup_file_list = NULL;
1582 TreeInfo *artwork_new = NULL;
1583 char *check_dir = NULL;
1586 if (access(filename, F_OK) == 0) /* file exists */
1587 loadSetupFileList(filename);
1589 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1592 struct dirent *dir_entry;
1593 boolean valid_file_found = FALSE;
1595 if ((dir = opendir(directory_path)) != NULL)
1597 while ((dir_entry = readdir(dir)) != NULL)
1599 char *entry_name = dir_entry->d_name;
1601 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1602 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1603 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1605 valid_file_found = TRUE;
1613 if (!valid_file_found)
1616 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1618 free(directory_path);
1625 artwork_new = newTreeInfo();
1628 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1630 setTreeInfoToDefaults(artwork_new, type);
1632 artwork_new->filename = getStringCopy(directory_name);
1634 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1637 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1640 /* set all structure fields according to the token/value pairs */
1642 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1643 setSetupInfo(levelinfo_tokens, i,
1644 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1647 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1649 if (artwork_new->name_short == NULL)
1650 artwork_new->name_short = getStringCopy(artwork_new->name);
1652 if (artwork_new->name_sorting == NULL)
1653 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1656 if (node_parent == NULL) /* top level group */
1658 artwork_new->basepath = getStringCopy(base_directory);
1659 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1661 else /* sub level group */
1663 artwork_new->basepath = getStringCopy(node_parent->basepath);
1664 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1667 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1668 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1669 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1670 artwork_new->user_defined =
1671 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1673 /* (may use ".sort_priority" from "setup_file_list" above) */
1674 artwork_new->color = LEVELCOLOR(artwork_new);
1675 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1677 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1679 if (artwork_new->name != NULL)
1680 free(artwork_new->name);
1682 if (strcmp(artwork_new->filename, ".") == 0)
1684 if (artwork_new->user_defined)
1685 artwork_new->name = getStringCopy("private");
1687 artwork_new->name = getStringCopy("default");
1690 artwork_new->name = getStringCopy(artwork_new->filename);
1692 artwork_new->name_short = getStringCopy(artwork_new->name);
1693 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1696 pushTreeInfo(node_first, artwork_new);
1698 freeSetupFileList(setup_file_list);
1700 free(directory_path);
1706 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1707 TreeInfo *node_parent,
1708 char *base_directory, int type)
1711 struct dirent *dir_entry;
1712 boolean valid_entry_found = FALSE;
1714 if ((dir = opendir(base_directory)) == NULL)
1716 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1717 base_directory == options.graphics_directory) ||
1718 (type == TREE_TYPE_SOUNDS_DIR &&
1719 base_directory == options.sounds_directory) ||
1720 (type == TREE_TYPE_MUSIC_DIR &&
1721 base_directory == options.music_directory))
1722 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1726 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1728 struct stat file_status;
1729 char *directory_name = dir_entry->d_name;
1730 char *directory_path = getPath2(base_directory, directory_name);
1732 /* skip entries for current and parent directory */
1733 if (strcmp(directory_name, ".") == 0 ||
1734 strcmp(directory_name, "..") == 0)
1736 free(directory_path);
1740 /* find out if directory entry is itself a directory */
1741 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1742 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1744 free(directory_path);
1748 free(directory_path);
1750 /* check if this directory contains artwork with or without config file */
1751 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1753 directory_name, type);
1758 /* check if this directory directly contains artwork itself */
1759 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1760 base_directory, ".",
1762 if (!valid_entry_found)
1763 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1767 void LoadArtworkInfo()
1769 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1771 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1772 options.graphics_directory,
1773 TREE_TYPE_GRAPHICS_DIR);
1774 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1775 getUserGraphicsDir(),
1776 TREE_TYPE_GRAPHICS_DIR);
1778 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1779 options.sounds_directory,
1780 TREE_TYPE_SOUNDS_DIR);
1781 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1783 TREE_TYPE_SOUNDS_DIR);
1785 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1786 options.music_directory,
1787 TREE_TYPE_MUSIC_DIR);
1788 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1790 TREE_TYPE_MUSIC_DIR);
1792 /* before sorting, the first entries will be from the user directory */
1793 artwork.gfx_current =
1794 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1795 if (artwork.gfx_current == NULL)
1796 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1798 artwork.snd_current =
1799 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1800 if (artwork.snd_current == NULL)
1801 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1803 artwork.mus_current =
1804 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1805 if (artwork.mus_current == NULL)
1806 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1808 artwork.graphics_set_current = artwork.gfx_current->name;
1809 artwork.sounds_set_current = artwork.snd_current->name;
1810 artwork.music_set_current = artwork.mus_current->name;
1812 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1813 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1814 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1817 dumpTreeInfo(artwork.gfx_first, 0);
1818 dumpTreeInfo(artwork.snd_first, 0);
1819 dumpTreeInfo(artwork.mus_first, 0);
1823 static void SaveUserLevelInfo()
1829 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1831 if (!(file = fopen(filename, MODE_WRITE)))
1833 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1838 /* always start with reliable default values */
1839 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1841 ldi.name = getLoginName();
1842 ldi.author = getRealName();
1844 ldi.first_level = 1;
1845 ldi.sort_priority = LEVELCLASS_USER_START;
1846 ldi.readonly = FALSE;
1848 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1849 getCookie("LEVELINFO")));
1851 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1852 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1853 i != LEVELINFO_TOKEN_NAME_SORTING &&
1854 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1855 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1860 SetFilePermissions(filename, PERMS_PRIVATE);
1863 char *getSetupValue(int type, void *value)
1865 static char value_string[MAX_LINE_LEN];
1870 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1874 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1878 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1882 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1886 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1890 sprintf(value_string, "%d", *(int *)value);
1894 strcpy(value_string, *(char **)value);
1898 value_string[0] = '\0';
1902 return value_string;
1905 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1908 static char entry[MAX_LINE_LEN];
1909 int token_type = token_info[token_nr].type;
1910 void *setup_value = token_info[token_nr].value;
1911 char *token_text = token_info[token_nr].text;
1912 char *value_string = getSetupValue(token_type, setup_value);
1914 /* start with the prefix, token and some spaces to format output line */
1915 sprintf(entry, "%s%s:", prefix, token_text);
1916 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1919 /* continue with the token's value (which can have different types) */
1920 strcat(entry, value_string);
1922 if (token_type == TYPE_KEY_X11)
1924 Key key = *(Key *)setup_value;
1925 char *keyname = getKeyNameFromKey(key);
1927 /* add comment, if useful */
1928 if (strcmp(keyname, "(undefined)") != 0 &&
1929 strcmp(keyname, "(unknown)") != 0)
1931 /* add at least one whitespace */
1933 for (i=strlen(entry); i<TOKEN_COMMENT_POSITION; i++)
1936 strcat(entry, "# ");
1937 strcat(entry, keyname);
1944 void LoadLevelSetup_LastSeries()
1947 struct SetupFileList *level_setup_list = NULL;
1949 /* always start with reliable default values */
1950 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1952 /* ----------------------------------------------------------------------- */
1953 /* ~/.<program>/levelsetup.conf */
1954 /* ----------------------------------------------------------------------- */
1956 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1958 if ((level_setup_list = loadSetupFileList(filename)))
1960 char *last_level_series =
1961 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1963 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1965 if (leveldir_current == NULL)
1966 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1968 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1970 freeSetupFileList(level_setup_list);
1973 Error(ERR_WARN, "using default setup values");
1978 void SaveLevelSetup_LastSeries()
1981 char *level_subdir = leveldir_current->filename;
1984 /* ----------------------------------------------------------------------- */
1985 /* ~/.<program>/levelsetup.conf */
1986 /* ----------------------------------------------------------------------- */
1988 InitUserDataDirectory();
1990 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1992 if (!(file = fopen(filename, MODE_WRITE)))
1994 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1999 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2000 getCookie("LEVELSETUP")));
2001 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2007 SetFilePermissions(filename, PERMS_PRIVATE);
2010 static void checkSeriesInfo()
2012 static char *level_directory = NULL;
2014 struct dirent *dir_entry;
2016 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2018 level_directory = getPath2((leveldir_current->user_defined ?
2019 getUserLevelDir(NULL) :
2020 options.level_directory),
2021 leveldir_current->fullpath);
2023 if ((dir = opendir(level_directory)) == NULL)
2025 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2029 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2031 if (strlen(dir_entry->d_name) > 4 &&
2032 dir_entry->d_name[3] == '.' &&
2033 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2035 char levelnum_str[4];
2038 strncpy(levelnum_str, dir_entry->d_name, 3);
2039 levelnum_str[3] = '\0';
2041 levelnum_value = atoi(levelnum_str);
2043 if (levelnum_value < leveldir_current->first_level)
2045 Error(ERR_WARN, "additional level %d found", levelnum_value);
2046 leveldir_current->first_level = levelnum_value;
2048 else if (levelnum_value > leveldir_current->last_level)
2050 Error(ERR_WARN, "additional level %d found", levelnum_value);
2051 leveldir_current->last_level = levelnum_value;
2059 void LoadLevelSetup_SeriesInfo()
2062 struct SetupFileList *level_setup_list = NULL;
2063 char *level_subdir = leveldir_current->filename;
2065 /* always start with reliable default values */
2066 level_nr = leveldir_current->first_level;
2068 checkSeriesInfo(leveldir_current);
2070 /* ----------------------------------------------------------------------- */
2071 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2072 /* ----------------------------------------------------------------------- */
2074 level_subdir = leveldir_current->filename;
2076 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2078 if ((level_setup_list = loadSetupFileList(filename)))
2082 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2086 level_nr = atoi(token_value);
2088 if (level_nr < leveldir_current->first_level)
2089 level_nr = leveldir_current->first_level;
2090 if (level_nr > leveldir_current->last_level)
2091 level_nr = leveldir_current->last_level;
2094 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2098 int level_nr = atoi(token_value);
2100 if (level_nr < leveldir_current->first_level)
2101 level_nr = leveldir_current->first_level;
2102 if (level_nr > leveldir_current->last_level + 1)
2103 level_nr = leveldir_current->last_level;
2105 if (leveldir_current->user_defined)
2106 level_nr = leveldir_current->last_level;
2108 leveldir_current->handicap_level = level_nr;
2111 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2113 freeSetupFileList(level_setup_list);
2116 Error(ERR_WARN, "using default setup values");
2121 void SaveLevelSetup_SeriesInfo()
2124 char *level_subdir = leveldir_current->filename;
2125 char *level_nr_str = int2str(level_nr, 0);
2126 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2129 /* ----------------------------------------------------------------------- */
2130 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2131 /* ----------------------------------------------------------------------- */
2133 InitLevelSetupDirectory(level_subdir);
2135 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2137 if (!(file = fopen(filename, MODE_WRITE)))
2139 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2144 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2145 getCookie("LEVELSETUP")));
2146 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2148 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2149 handicap_level_str));
2154 SetFilePermissions(filename, PERMS_PRIVATE);