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 30
84 #define MAX_COOKIE_LEN 256
87 /* ------------------------------------------------------------------------- */
89 /* ------------------------------------------------------------------------- */
91 static char *getLevelClassDescription(TreeInfo *ldi)
93 int position = ldi->sort_priority / 100;
95 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
96 return levelclass_desc[position];
98 return "Unknown Level Class";
101 static char *getUserLevelDir(char *level_subdir)
103 static char *userlevel_dir = NULL;
104 char *data_dir = getUserDataDir();
105 char *userlevel_subdir = LEVELS_DIRECTORY;
110 if (level_subdir != NULL)
111 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
113 userlevel_dir = getPath2(data_dir, userlevel_subdir);
115 return userlevel_dir;
118 static char *getTapeDir(char *level_subdir)
120 static char *tape_dir = NULL;
121 char *data_dir = getUserDataDir();
122 char *tape_subdir = TAPES_DIRECTORY;
127 if (level_subdir != NULL)
128 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
130 tape_dir = getPath2(data_dir, tape_subdir);
135 static char *getScoreDir(char *level_subdir)
137 static char *score_dir = NULL;
138 char *data_dir = options.rw_base_directory;
139 char *score_subdir = SCORES_DIRECTORY;
144 if (level_subdir != NULL)
145 score_dir = getPath3(data_dir, score_subdir, level_subdir);
147 score_dir = getPath2(data_dir, score_subdir);
152 static char *getLevelSetupDir(char *level_subdir)
154 static char *levelsetup_dir = NULL;
155 char *data_dir = getUserDataDir();
156 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
159 free(levelsetup_dir);
161 if (level_subdir != NULL)
162 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
164 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
166 return levelsetup_dir;
169 static char *getCurrentLevelDir()
171 static char *level_dir = NULL;
176 if (leveldir_current == NULL)
177 return options.level_directory;
179 level_dir = getPath2((leveldir_current->user_defined ?
180 getUserLevelDir(NULL) : options.level_directory),
181 leveldir_current->fullpath);
186 static char *getDefaultGraphicsDir(char *graphics_subdir)
188 static char *graphics_dir = NULL;
190 if (graphics_subdir == NULL)
191 return options.graphics_directory;
196 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
201 static char *getDefaultSoundsDir(char *sounds_subdir)
203 static char *sounds_dir = NULL;
205 if (sounds_subdir == NULL)
206 return options.sounds_directory;
211 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
216 static char *getDefaultMusicDir(char *music_subdir)
218 static char *music_dir = NULL;
220 if (music_subdir == NULL)
221 return options.music_directory;
226 music_dir = getPath2(options.music_directory, music_subdir);
231 static char *getUserGraphicsDir()
233 static char *usergraphics_dir = NULL;
235 if (usergraphics_dir == NULL)
236 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
238 return usergraphics_dir;
241 static char *getUserSoundsDir()
243 static char *usersounds_dir = NULL;
245 if (usersounds_dir == NULL)
246 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
248 return usersounds_dir;
251 static char *getUserMusicDir()
253 static char *usermusic_dir = NULL;
255 if (usermusic_dir == NULL)
256 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
258 return usermusic_dir;
261 char *getLevelFilename(int nr)
263 static char *filename = NULL;
264 char basename[MAX_FILENAME_LEN];
266 if (filename != NULL)
269 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
270 filename = getPath2(getCurrentLevelDir(), basename);
275 char *getTapeFilename(int nr)
277 static char *filename = NULL;
278 char basename[MAX_FILENAME_LEN];
280 if (filename != NULL)
283 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
284 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
289 char *getScoreFilename(int nr)
291 static char *filename = NULL;
292 char basename[MAX_FILENAME_LEN];
294 if (filename != NULL)
297 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
298 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
303 char *getSetupFilename()
305 static char *filename = NULL;
307 if (filename != NULL)
310 filename = getPath2(getSetupDir(), SETUP_FILENAME);
315 static char *getSetupArtworkDir(TreeInfo *ti)
317 static char *artwork_dir = NULL;
319 if (artwork_dir != NULL)
322 artwork_dir = getPath2(ti->basepath, ti->fullpath);
327 static char *getCorrectedImageBasename(char *basename)
329 char *result = basename;
331 #if defined(PLATFORM_MSDOS)
332 if (program.filename_prefix != NULL)
334 int prefix_len = strlen(program.filename_prefix);
336 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
337 result = &basename[prefix_len];
344 static boolean fileExists(char *filename)
347 printf("checking file '%s'\n", filename);
350 return (access(filename, F_OK) == 0);
353 char *getCustomImageFilename(char *basename)
355 static char *filename = NULL;
357 if (filename != NULL)
360 basename = getCorrectedImageBasename(basename);
362 /* 1st try: look for special artwork in current level series directory */
363 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
364 if (fileExists(filename))
367 /* 2nd try: look for special artwork in private artwork directory */
368 filename = getPath2(getUserGraphicsDir(), basename);
369 if (fileExists(filename))
372 /* 3rd try: look for special artwork in configured artwork directory */
373 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
374 if (fileExists(filename))
377 /* 4th try: look for default artwork in new default artwork directory */
378 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
379 if (fileExists(filename))
382 /* 5th try: look for default artwork in old default artwork directory */
383 filename = getPath2(options.graphics_directory, basename);
384 if (fileExists(filename))
387 return NULL; /* cannot find image file */
390 char *getCustomSoundFilename(char *basename)
392 static char *filename = NULL;
394 if (filename != NULL)
397 /* 1st try: look for special artwork in current level series directory */
398 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
399 if (fileExists(filename))
402 /* 2nd try: look for special artwork in private artwork directory */
403 filename = getPath2(getUserSoundsDir(), basename);
404 if (fileExists(filename))
407 /* 3rd try: look for special artwork in configured artwork directory */
408 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
409 if (fileExists(filename))
412 /* 4th try: look for default artwork in new default artwork directory */
413 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
414 if (fileExists(filename))
417 /* 5th try: look for default artwork in old default artwork directory */
418 filename = getPath2(options.sounds_directory, basename);
419 if (fileExists(filename))
422 return NULL; /* cannot find image file */
425 void InitTapeDirectory(char *level_subdir)
427 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
428 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
429 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
432 void InitScoreDirectory(char *level_subdir)
434 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
435 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
438 static void SaveUserLevelInfo();
440 void InitUserLevelDirectory(char *level_subdir)
442 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
444 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
445 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
446 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
452 void InitLevelSetupDirectory(char *level_subdir)
454 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
455 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
456 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
459 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
461 int file_version_major, file_version_minor, file_version_patch;
462 int game_version_major, game_version_minor, game_version_patch;
464 file_version_major = fgetc(file);
465 file_version_minor = fgetc(file);
466 file_version_patch = fgetc(file);
467 fgetc(file); /* not used */
469 game_version_major = fgetc(file);
470 game_version_minor = fgetc(file);
471 game_version_patch = fgetc(file);
472 fgetc(file); /* not used */
474 *file_version = VERSION_IDENT(file_version_major,
478 *game_version = VERSION_IDENT(game_version_major,
483 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
485 int file_version_major = VERSION_MAJOR(file_version);
486 int file_version_minor = VERSION_MINOR(file_version);
487 int file_version_patch = VERSION_PATCH(file_version);
488 int game_version_major = VERSION_MAJOR(game_version);
489 int game_version_minor = VERSION_MINOR(game_version);
490 int game_version_patch = VERSION_PATCH(game_version);
492 fputc(file_version_major, file);
493 fputc(file_version_minor, file);
494 fputc(file_version_patch, file);
495 fputc(0, file); /* not used */
497 fputc(game_version_major, file);
498 fputc(game_version_minor, file);
499 fputc(game_version_patch, file);
500 fputc(0, file); /* not used */
504 /* ------------------------------------------------------------------------- */
505 /* some functions to handle lists of level directories */
506 /* ------------------------------------------------------------------------- */
508 TreeInfo *newTreeInfo()
510 return checked_calloc(sizeof(TreeInfo));
513 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
515 node_new->next = *node_first;
516 *node_first = node_new;
519 int numTreeInfo(TreeInfo *node)
532 boolean validLevelSeries(TreeInfo *node)
534 return (node != NULL && !node->node_group && !node->parent_link);
537 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
541 if (node->node_top) /* start with first tree entry */
542 return getFirstValidTreeInfoEntry(*node->node_top);
546 else if (node->node_group) /* enter level group (step down into tree) */
547 return getFirstValidTreeInfoEntry(node->node_group);
548 else if (node->parent_link) /* skip start entry of level group */
550 if (node->next) /* get first real level series entry */
551 return getFirstValidTreeInfoEntry(node->next);
552 else /* leave empty level group and go on */
553 return getFirstValidTreeInfoEntry(node->node_parent->next);
555 else /* this seems to be a regular level series */
559 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
564 if (node->node_parent == NULL) /* top level group */
565 return *node->node_top;
566 else /* sub level group */
567 return node->node_parent->node_group;
570 int numTreeInfoInGroup(TreeInfo *node)
572 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
575 int posTreeInfo(TreeInfo *node)
577 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
582 if (node_cmp == node)
586 node_cmp = node_cmp->next;
592 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
594 TreeInfo *node_default = node;
609 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
611 if (filename == NULL)
616 if (node->node_group)
618 TreeInfo *node_group;
620 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
625 else if (!node->parent_link)
627 if (strcmp(filename, node->filename) == 0)
637 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
639 return getTreeInfoFromFilenameExt(ti, filename);
642 void dumpTreeInfo(TreeInfo *node, int depth)
646 printf("Dumping TreeInfo:\n");
650 for (i=0; i<(depth + 1) * 3; i++)
653 printf("filename == '%s' [%s]\n", node->filename, node->name);
655 if (node->node_group != NULL)
656 dumpTreeInfo(node->node_group, depth + 1);
662 void sortTreeInfo(TreeInfo **node_first,
663 int (*compare_function)(const void *, const void *))
665 int num_nodes = numTreeInfo(*node_first);
666 TreeInfo **sort_array;
667 TreeInfo *node = *node_first;
673 /* allocate array for sorting structure pointers */
674 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
676 /* writing structure pointers to sorting array */
677 while (i < num_nodes && node) /* double boundary check... */
679 sort_array[i] = node;
685 /* sorting the structure pointers in the sorting array */
686 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
689 /* update the linkage of list elements with the sorted node array */
690 for (i=0; i<num_nodes - 1; i++)
691 sort_array[i]->next = sort_array[i + 1];
692 sort_array[num_nodes - 1]->next = NULL;
694 /* update the linkage of the main list anchor pointer */
695 *node_first = sort_array[0];
699 /* now recursively sort the level group structures */
703 if (node->node_group != NULL)
704 sortTreeInfo(&node->node_group, compare_function);
711 /* ========================================================================= */
712 /* some stuff from "files.c" */
713 /* ========================================================================= */
715 #if defined(PLATFORM_WIN32)
717 #define S_IRGRP S_IRUSR
720 #define S_IROTH S_IRUSR
723 #define S_IWGRP S_IWUSR
726 #define S_IWOTH S_IWUSR
729 #define S_IXGRP S_IXUSR
732 #define S_IXOTH S_IXUSR
735 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
740 #endif /* PLATFORM_WIN32 */
742 /* file permissions for newly written files */
743 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
744 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
745 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
747 #define MODE_W_PRIVATE (S_IWUSR)
748 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
749 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
751 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
752 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
754 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
755 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
757 char *getUserDataDir(void)
759 static char *userdata_dir = NULL;
763 char *home_dir = getHomeDir();
764 char *data_dir = program.userdata_directory;
766 userdata_dir = getPath2(home_dir, data_dir);
774 return getUserDataDir();
777 static mode_t posix_umask(mode_t mask)
779 #if defined(PLATFORM_UNIX)
786 static int posix_mkdir(const char *pathname, mode_t mode)
788 #if defined(PLATFORM_WIN32)
789 return mkdir(pathname);
791 return mkdir(pathname, mode);
795 void createDirectory(char *dir, char *text, int permission_class)
797 /* leave "other" permissions in umask untouched, but ensure group parts
798 of USERDATA_DIR_MODE are not masked */
799 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
800 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
801 mode_t normal_umask = posix_umask(0);
802 mode_t group_umask = ~(dir_mode & S_IRWXG);
803 posix_umask(normal_umask & group_umask);
805 if (access(dir, F_OK) != 0)
806 if (posix_mkdir(dir, dir_mode) != 0)
807 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
809 posix_umask(normal_umask); /* reset normal umask */
812 void InitUserDataDirectory()
814 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
817 void SetFilePermissions(char *filename, int permission_class)
819 chmod(filename, (permission_class == PERMS_PRIVATE ?
820 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
823 char *getCookie(char *file_type)
825 static char cookie[MAX_COOKIE_LEN + 1];
827 if (strlen(program.cookie_prefix) + 1 +
828 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
829 return "[COOKIE ERROR]"; /* should never happen */
831 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
832 program.cookie_prefix, file_type,
833 program.version_major, program.version_minor);
838 int getFileVersionFromCookieString(const char *cookie)
840 const char *ptr_cookie1, *ptr_cookie2;
841 const char *pattern1 = "_FILE_VERSION_";
842 const char *pattern2 = "?.?";
843 const int len_cookie = strlen(cookie);
844 const int len_pattern1 = strlen(pattern1);
845 const int len_pattern2 = strlen(pattern2);
846 const int len_pattern = len_pattern1 + len_pattern2;
847 int version_major, version_minor;
849 if (len_cookie <= len_pattern)
852 ptr_cookie1 = &cookie[len_cookie - len_pattern];
853 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
855 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
858 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
859 ptr_cookie2[1] != '.' ||
860 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
863 version_major = ptr_cookie2[0] - '0';
864 version_minor = ptr_cookie2[2] - '0';
866 return VERSION_IDENT(version_major, version_minor, 0);
869 boolean checkCookieString(const char *cookie, const char *template)
871 const char *pattern = "_FILE_VERSION_?.?";
872 const int len_cookie = strlen(cookie);
873 const int len_template = strlen(template);
874 const int len_pattern = strlen(pattern);
876 if (len_cookie != len_template)
879 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
885 /* ------------------------------------------------------------------------- */
886 /* setup file list handling functions */
887 /* ------------------------------------------------------------------------- */
889 int get_string_integer_value(char *s)
891 static char *number_text[][3] =
893 { "0", "zero", "null", },
894 { "1", "one", "first" },
895 { "2", "two", "second" },
896 { "3", "three", "third" },
897 { "4", "four", "fourth" },
898 { "5", "five", "fifth" },
899 { "6", "six", "sixth" },
900 { "7", "seven", "seventh" },
901 { "8", "eight", "eighth" },
902 { "9", "nine", "ninth" },
903 { "10", "ten", "tenth" },
904 { "11", "eleven", "eleventh" },
905 { "12", "twelve", "twelfth" },
909 char *s_lower = getStringToLower(s);
914 if (strcmp(s_lower, number_text[i][j]) == 0)
925 boolean get_string_boolean_value(char *s)
927 char *s_lower = getStringToLower(s);
928 boolean result = FALSE;
930 if (strcmp(s_lower, "true") == 0 ||
931 strcmp(s_lower, "yes") == 0 ||
932 strcmp(s_lower, "on") == 0 ||
933 get_string_integer_value(s) == 1)
941 char *getFormattedSetupEntry(char *token, char *value)
944 static char entry[MAX_LINE_LEN];
946 sprintf(entry, "%s:", token);
947 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
951 strcat(entry, value);
956 void freeSetupFileList(struct SetupFileList *setup_file_list)
958 if (!setup_file_list)
961 if (setup_file_list->token)
962 free(setup_file_list->token);
963 if (setup_file_list->value)
964 free(setup_file_list->value);
965 if (setup_file_list->next)
966 freeSetupFileList(setup_file_list->next);
967 free(setup_file_list);
970 static struct SetupFileList *newSetupFileList(char *token, char *value)
972 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
974 new->token = checked_malloc(strlen(token) + 1);
975 strcpy(new->token, token);
977 new->value = checked_malloc(strlen(value) + 1);
978 strcpy(new->value, value);
985 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
987 if (!setup_file_list)
990 if (strcmp(setup_file_list->token, token) == 0)
991 return setup_file_list->value;
993 return getTokenValue(setup_file_list->next, token);
996 static void setTokenValue(struct SetupFileList *setup_file_list,
997 char *token, char *value)
999 if (!setup_file_list)
1002 if (strcmp(setup_file_list->token, token) == 0)
1004 free(setup_file_list->value);
1005 setup_file_list->value = checked_malloc(strlen(value) + 1);
1006 strcpy(setup_file_list->value, value);
1008 else if (setup_file_list->next == NULL)
1009 setup_file_list->next = newSetupFileList(token, value);
1011 setTokenValue(setup_file_list->next, token, value);
1015 static void printSetupFileList(struct SetupFileList *setup_file_list)
1017 if (!setup_file_list)
1020 printf("token: '%s'\n", setup_file_list->token);
1021 printf("value: '%s'\n", setup_file_list->value);
1023 printSetupFileList(setup_file_list->next);
1027 struct SetupFileList *loadSetupFileList(char *filename)
1030 char line[MAX_LINE_LEN];
1031 char *token, *value, *line_ptr;
1032 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1033 struct SetupFileList *first_valid_list_entry;
1037 if (!(file = fopen(filename, MODE_READ)))
1039 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1045 /* read next line of input file */
1046 if (!fgets(line, MAX_LINE_LEN, file))
1049 /* cut trailing comment or whitespace from input line */
1050 for (line_ptr = line; *line_ptr; line_ptr++)
1052 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1059 /* cut trailing whitespaces from input line */
1060 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1061 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1064 /* ignore empty lines */
1068 line_len = strlen(line);
1070 /* cut leading whitespaces from token */
1071 for (token = line; *token; token++)
1072 if (*token != ' ' && *token != '\t')
1075 /* find end of token */
1076 for (line_ptr = token; *line_ptr; line_ptr++)
1078 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1085 if (line_ptr < line + line_len)
1086 value = line_ptr + 1;
1090 /* cut leading whitespaces from value */
1091 for (; *value; value++)
1092 if (*value != ' ' && *value != '\t')
1095 if (*token && *value)
1096 setTokenValue(setup_file_list, token, value);
1101 first_valid_list_entry = setup_file_list->next;
1103 /* free empty list header */
1104 setup_file_list->next = NULL;
1105 freeSetupFileList(setup_file_list);
1107 if (first_valid_list_entry == NULL)
1108 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1110 return first_valid_list_entry;
1113 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1116 if (!setup_file_list)
1119 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1121 if (!checkCookieString(setup_file_list->value, identifier))
1123 Error(ERR_WARN, "configuration file has wrong file identifier");
1130 if (setup_file_list->next)
1131 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1134 Error(ERR_WARN, "configuration file has no file identifier");
1140 /* ========================================================================= */
1141 /* setup file stuff */
1142 /* ========================================================================= */
1144 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1145 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1146 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1148 /* level directory info */
1149 #define LEVELINFO_TOKEN_NAME 0
1150 #define LEVELINFO_TOKEN_NAME_SHORT 1
1151 #define LEVELINFO_TOKEN_NAME_SORTING 2
1152 #define LEVELINFO_TOKEN_AUTHOR 3
1153 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1154 #define LEVELINFO_TOKEN_LEVELS 5
1155 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1156 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1157 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1158 #define LEVELINFO_TOKEN_READONLY 9
1160 #define NUM_LEVELINFO_TOKENS 10
1162 static LevelDirTree ldi;
1164 static struct TokenInfo levelinfo_tokens[] =
1166 /* level directory info */
1167 { TYPE_STRING, &ldi.name, "name" },
1168 { TYPE_STRING, &ldi.name_short, "name_short" },
1169 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1170 { TYPE_STRING, &ldi.author, "author" },
1171 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1172 { TYPE_INTEGER, &ldi.levels, "levels" },
1173 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1174 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1175 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1176 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1179 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1183 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1184 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1185 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1186 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1189 ldi->node_parent = NULL;
1190 ldi->node_group = NULL;
1194 ldi->cl_cursor = -1;
1196 ldi->filename = NULL;
1197 ldi->fullpath = NULL;
1198 ldi->basepath = NULL;
1199 ldi->name = getStringCopy(ANONYMOUS_NAME);
1200 ldi->name_short = NULL;
1201 ldi->name_sorting = NULL;
1202 ldi->author = getStringCopy(ANONYMOUS_NAME);
1204 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1205 ldi->parent_link = FALSE;
1206 ldi->user_defined = FALSE;
1208 ldi->class_desc = NULL;
1210 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1212 ldi->imported_from = NULL;
1214 ldi->first_level = 0;
1215 ldi->last_level = 0;
1216 ldi->level_group = FALSE;
1217 ldi->handicap_level = 0;
1218 ldi->readonly = TRUE;
1222 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1226 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1228 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1232 /* first copy all values from the parent structure ... */
1235 /* ... then set all fields to default that cannot be inherited from parent.
1236 This is especially important for all those fields that can be set from
1237 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1238 calls 'free()' for all already set token values which requires that no
1239 other structure's pointer may point to them!
1242 ldi->filename = NULL;
1243 ldi->fullpath = NULL;
1244 ldi->basepath = NULL;
1245 ldi->name = getStringCopy(ANONYMOUS_NAME);
1246 ldi->name_short = NULL;
1247 ldi->name_sorting = NULL;
1248 ldi->author = getStringCopy(parent->author);
1249 ldi->imported_from = getStringCopy(parent->imported_from);
1251 ldi->level_group = FALSE;
1252 ldi->parent_link = FALSE;
1254 ldi->node_top = parent->node_top;
1255 ldi->node_parent = parent;
1256 ldi->node_group = NULL;
1260 void setSetupInfo(struct TokenInfo *token_info,
1261 int token_nr, char *token_value)
1263 int token_type = token_info[token_nr].type;
1264 void *setup_value = token_info[token_nr].value;
1266 if (token_value == NULL)
1269 /* set setup field to corresponding token value */
1274 *(boolean *)setup_value = get_string_boolean_value(token_value);
1278 *(Key *)setup_value = getKeyFromKeyName(token_value);
1282 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1286 *(int *)setup_value = get_string_integer_value(token_value);
1290 if (*(char **)setup_value != NULL)
1291 free(*(char **)setup_value);
1292 *(char **)setup_value = getStringCopy(token_value);
1300 static int compareTreeInfoEntries(const void *object1, const void *object2)
1302 const TreeInfo *entry1 = *((TreeInfo **)object1);
1303 const TreeInfo *entry2 = *((TreeInfo **)object2);
1306 if (entry1->parent_link || entry2->parent_link)
1307 compare_result = (entry1->parent_link ? -1 : +1);
1308 else if (entry1->sort_priority == entry2->sort_priority)
1310 char *name1 = getStringToLower(entry1->name_sorting);
1311 char *name2 = getStringToLower(entry2->name_sorting);
1313 compare_result = strcmp(name1, name2);
1318 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1319 compare_result = entry1->sort_priority - entry2->sort_priority;
1321 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1323 return compare_result;
1326 static void createParentTreeInfoNode(TreeInfo *node_parent)
1330 if (node_parent == NULL)
1333 ti_new = newTreeInfo();
1334 setTreeInfoToDefaults(ti_new, node_parent->type);
1336 ti_new->node_parent = node_parent;
1337 ti_new->parent_link = TRUE;
1339 ti_new->name = ".. (parent directory)";
1340 ti_new->name_short = getStringCopy(ti_new->name);
1341 ti_new->name_sorting = getStringCopy(ti_new->name);
1343 ti_new->filename = "..";
1344 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1346 ti_new->sort_priority = node_parent->sort_priority;
1347 ti_new->class_desc = getLevelClassDescription(ti_new);
1349 pushTreeInfo(&node_parent->node_group, ti_new);
1352 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1353 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1355 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1356 TreeInfo *node_parent,
1357 char *level_directory,
1358 char *directory_name)
1360 char *directory_path = getPath2(level_directory, directory_name);
1361 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1362 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1363 LevelDirTree *leveldir_new = NULL;
1366 if (setup_file_list == NULL)
1368 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1370 free(directory_path);
1376 leveldir_new = newTreeInfo();
1379 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1381 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1383 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1385 /* set all structure fields according to the token/value pairs */
1386 ldi = *leveldir_new;
1387 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1388 setSetupInfo(levelinfo_tokens, i,
1389 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1390 *leveldir_new = ldi;
1392 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1394 if (leveldir_new->name_short == NULL)
1395 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1397 if (leveldir_new->name_sorting == NULL)
1398 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1400 leveldir_new->filename = getStringCopy(directory_name);
1402 if (node_parent == NULL) /* top level group */
1404 leveldir_new->basepath = level_directory;
1405 leveldir_new->fullpath = leveldir_new->filename;
1407 else /* sub level group */
1409 leveldir_new->basepath = node_parent->basepath;
1410 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1413 if (leveldir_new->levels < 1)
1414 leveldir_new->levels = 1;
1416 leveldir_new->last_level =
1417 leveldir_new->first_level + leveldir_new->levels - 1;
1419 leveldir_new->user_defined =
1420 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1422 leveldir_new->color = LEVELCOLOR(leveldir_new);
1423 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1425 leveldir_new->handicap_level = /* set handicap to default value */
1426 (leveldir_new->user_defined ?
1427 leveldir_new->last_level :
1428 leveldir_new->first_level);
1430 pushTreeInfo(node_first, leveldir_new);
1432 freeSetupFileList(setup_file_list);
1434 if (leveldir_new->level_group)
1436 /* create node to link back to current level directory */
1437 createParentTreeInfoNode(leveldir_new);
1439 /* step into sub-directory and look for more level series */
1440 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1441 leveldir_new, directory_path);
1444 free(directory_path);
1450 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1451 TreeInfo *node_parent,
1452 char *level_directory)
1455 struct dirent *dir_entry;
1456 boolean valid_entry_found = FALSE;
1458 if ((dir = opendir(level_directory)) == NULL)
1460 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1464 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1466 struct stat file_status;
1467 char *directory_name = dir_entry->d_name;
1468 char *directory_path = getPath2(level_directory, directory_name);
1470 /* skip entries for current and parent directory */
1471 if (strcmp(directory_name, ".") == 0 ||
1472 strcmp(directory_name, "..") == 0)
1474 free(directory_path);
1478 /* find out if directory entry is itself a directory */
1479 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1480 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1482 free(directory_path);
1486 free(directory_path);
1488 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1495 if (!valid_entry_found)
1497 /* check if this directory directly contains a file "levelinfo.conf" */
1498 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1499 level_directory, ".");
1502 if (!valid_entry_found)
1503 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1507 void LoadLevelInfo()
1509 InitUserLevelDirectory(getLoginName());
1511 DrawInitText("Loading level series:", 120, FC_GREEN);
1513 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1514 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1516 /* before sorting, the first entries will be from the user directory */
1517 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1519 if (leveldir_first == NULL)
1520 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1522 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1525 dumpTreeInfo(leveldir_first, 0);
1529 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1530 TreeInfo *node_parent,
1531 char *base_directory,
1532 char *directory_name, int type)
1534 char *directory_path = getPath2(base_directory, directory_name);
1536 getPath2(directory_path,
1537 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1538 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1539 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1540 struct SetupFileList *setup_file_list = NULL;
1541 TreeInfo *artwork_new = NULL;
1542 char *check_dir = NULL;
1545 if (access(filename, F_OK) == 0) /* file exists */
1546 loadSetupFileList(filename);
1548 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1551 struct dirent *dir_entry;
1552 boolean valid_file_found = FALSE;
1554 if ((dir = opendir(directory_path)) != NULL)
1556 while ((dir_entry = readdir(dir)) != NULL)
1558 char *entry_name = dir_entry->d_name;
1560 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1561 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1562 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1564 valid_file_found = TRUE;
1572 if (!valid_file_found)
1575 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1577 free(directory_path);
1584 artwork_new = newTreeInfo();
1587 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1589 setTreeInfoToDefaults(artwork_new, type);
1591 artwork_new->filename = getStringCopy(directory_name);
1593 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1596 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1599 /* set all structure fields according to the token/value pairs */
1601 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1602 setSetupInfo(levelinfo_tokens, i,
1603 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1606 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1608 if (artwork_new->name_short == NULL)
1609 artwork_new->name_short = getStringCopy(artwork_new->name);
1611 if (artwork_new->name_sorting == NULL)
1612 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1615 if (node_parent == NULL) /* top level group */
1617 artwork_new->basepath = base_directory;
1618 artwork_new->fullpath = artwork_new->filename;
1620 else /* sub level group */
1622 artwork_new->basepath = node_parent->basepath;
1623 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1626 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1627 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1628 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1629 artwork_new->user_defined =
1630 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1632 /* (may use ".sort_priority" from "setup_file_list" above) */
1633 artwork_new->color = LEVELCOLOR(artwork_new);
1634 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1636 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1638 if (artwork_new->name != NULL)
1639 free(artwork_new->name);
1641 if (strcmp(artwork_new->filename, ".") == 0)
1643 if (artwork_new->user_defined)
1644 artwork_new->name = getStringCopy("private");
1646 artwork_new->name = getStringCopy("default");
1649 artwork_new->name = getStringCopy(artwork_new->filename);
1651 artwork_new->name_short = getStringCopy(artwork_new->name);
1652 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1655 pushTreeInfo(node_first, artwork_new);
1657 freeSetupFileList(setup_file_list);
1659 free(directory_path);
1665 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1666 TreeInfo *node_parent,
1667 char *base_directory, int type)
1670 struct dirent *dir_entry;
1671 boolean valid_entry_found = FALSE;
1673 if ((dir = opendir(base_directory)) == NULL)
1675 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1676 base_directory == options.graphics_directory) ||
1677 (type == TREE_TYPE_SOUNDS_DIR &&
1678 base_directory == options.sounds_directory) ||
1679 (type == TREE_TYPE_MUSIC_DIR &&
1680 base_directory == options.music_directory))
1681 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1685 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1687 struct stat file_status;
1688 char *directory_name = dir_entry->d_name;
1689 char *directory_path = getPath2(base_directory, directory_name);
1691 /* skip entries for current and parent directory */
1692 if (strcmp(directory_name, ".") == 0 ||
1693 strcmp(directory_name, "..") == 0)
1695 free(directory_path);
1699 /* find out if directory entry is itself a directory */
1700 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1701 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1703 free(directory_path);
1707 free(directory_path);
1709 /* check if this directory contains artwork with or without config file */
1710 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1712 directory_name, type);
1717 /* check if this directory directly contains artwork itself */
1718 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1719 base_directory, ".",
1721 if (!valid_entry_found)
1722 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1726 void LoadArtworkInfo()
1728 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1730 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1731 options.graphics_directory,
1732 TREE_TYPE_GRAPHICS_DIR);
1733 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1734 getUserGraphicsDir(),
1735 TREE_TYPE_GRAPHICS_DIR);
1737 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1738 options.sounds_directory,
1739 TREE_TYPE_SOUNDS_DIR);
1740 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1742 TREE_TYPE_SOUNDS_DIR);
1744 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1745 options.music_directory,
1746 TREE_TYPE_MUSIC_DIR);
1747 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1749 TREE_TYPE_MUSIC_DIR);
1751 /* before sorting, the first entries will be from the user directory */
1752 artwork.gfx_current =
1753 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1754 if (artwork.gfx_current == NULL)
1755 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1757 artwork.snd_current =
1758 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1759 if (artwork.snd_current == NULL)
1760 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1762 artwork.mus_current =
1763 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1764 if (artwork.mus_current == NULL)
1765 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1767 artwork.graphics_set_current = artwork.gfx_current->name;
1768 artwork.sounds_set_current = artwork.snd_current->name;
1769 artwork.music_set_current = artwork.mus_current->name;
1771 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1772 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1773 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1776 dumpTreeInfo(artwork.gfx_first, 0);
1777 dumpTreeInfo(artwork.snd_first, 0);
1778 dumpTreeInfo(artwork.mus_first, 0);
1782 static void SaveUserLevelInfo()
1788 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1790 if (!(file = fopen(filename, MODE_WRITE)))
1792 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1797 /* always start with reliable default values */
1798 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1800 ldi.name = getLoginName();
1801 ldi.author = getRealName();
1803 ldi.first_level = 1;
1804 ldi.sort_priority = LEVELCLASS_USER_START;
1805 ldi.readonly = FALSE;
1807 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1808 getCookie("LEVELINFO")));
1810 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1811 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1812 i != LEVELINFO_TOKEN_NAME_SORTING &&
1813 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1814 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1819 SetFilePermissions(filename, PERMS_PRIVATE);
1822 char *getSetupValue(int type, void *value)
1824 static char value_string[MAX_LINE_LEN];
1829 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1833 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1837 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1841 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1845 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1849 sprintf(value_string, "%d", *(int *)value);
1853 strcpy(value_string, *(char **)value);
1857 value_string[0] = '\0';
1861 return value_string;
1864 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1867 static char entry[MAX_LINE_LEN];
1868 int token_type = token_info[token_nr].type;
1869 void *setup_value = token_info[token_nr].value;
1870 char *token_text = token_info[token_nr].text;
1871 char *value_string = getSetupValue(token_type, setup_value);
1873 /* start with the prefix, token and some spaces to format output line */
1874 sprintf(entry, "%s%s:", prefix, token_text);
1875 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1878 /* continue with the token's value (which can have different types) */
1879 strcat(entry, value_string);
1881 if (token_type == TYPE_KEY_X11)
1883 Key key = *(Key *)setup_value;
1884 char *keyname = getKeyNameFromKey(key);
1886 /* add comment, if useful */
1887 if (strcmp(keyname, "(undefined)") != 0 &&
1888 strcmp(keyname, "(unknown)") != 0)
1890 for (i=strlen(entry); i<50; i++)
1893 strcat(entry, "# ");
1894 strcat(entry, keyname);
1901 void LoadLevelSetup_LastSeries()
1904 struct SetupFileList *level_setup_list = NULL;
1906 /* always start with reliable default values */
1907 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1909 /* ----------------------------------------------------------------------- */
1910 /* ~/.<program>/levelsetup.conf */
1911 /* ----------------------------------------------------------------------- */
1913 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1915 if ((level_setup_list = loadSetupFileList(filename)))
1917 char *last_level_series =
1918 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1920 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1922 if (leveldir_current == NULL)
1923 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1925 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1927 freeSetupFileList(level_setup_list);
1930 Error(ERR_WARN, "using default setup values");
1935 void SaveLevelSetup_LastSeries()
1938 char *level_subdir = leveldir_current->filename;
1941 /* ----------------------------------------------------------------------- */
1942 /* ~/.<program>/levelsetup.conf */
1943 /* ----------------------------------------------------------------------- */
1945 InitUserDataDirectory();
1947 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1949 if (!(file = fopen(filename, MODE_WRITE)))
1951 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1956 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1957 getCookie("LEVELSETUP")));
1958 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1964 SetFilePermissions(filename, PERMS_PRIVATE);
1967 static void checkSeriesInfo()
1969 static char *level_directory = NULL;
1971 struct dirent *dir_entry;
1973 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
1975 level_directory = getPath2((leveldir_current->user_defined ?
1976 getUserLevelDir(NULL) :
1977 options.level_directory),
1978 leveldir_current->fullpath);
1980 if ((dir = opendir(level_directory)) == NULL)
1982 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1986 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
1988 if (strlen(dir_entry->d_name) > 4 &&
1989 dir_entry->d_name[3] == '.' &&
1990 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
1992 char levelnum_str[4];
1995 strncpy(levelnum_str, dir_entry->d_name, 3);
1996 levelnum_str[3] = '\0';
1998 levelnum_value = atoi(levelnum_str);
2000 if (levelnum_value < leveldir_current->first_level)
2002 Error(ERR_WARN, "additional level %d found", levelnum_value);
2003 leveldir_current->first_level = levelnum_value;
2005 else if (levelnum_value > leveldir_current->last_level)
2007 Error(ERR_WARN, "additional level %d found", levelnum_value);
2008 leveldir_current->last_level = levelnum_value;
2016 void LoadLevelSetup_SeriesInfo()
2019 struct SetupFileList *level_setup_list = NULL;
2020 char *level_subdir = leveldir_current->filename;
2022 /* always start with reliable default values */
2023 level_nr = leveldir_current->first_level;
2025 checkSeriesInfo(leveldir_current);
2027 /* ----------------------------------------------------------------------- */
2028 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2029 /* ----------------------------------------------------------------------- */
2031 level_subdir = leveldir_current->filename;
2033 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2035 if ((level_setup_list = loadSetupFileList(filename)))
2039 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2043 level_nr = atoi(token_value);
2045 if (level_nr < leveldir_current->first_level)
2046 level_nr = leveldir_current->first_level;
2047 if (level_nr > leveldir_current->last_level)
2048 level_nr = leveldir_current->last_level;
2051 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2055 int level_nr = atoi(token_value);
2057 if (level_nr < leveldir_current->first_level)
2058 level_nr = leveldir_current->first_level;
2059 if (level_nr > leveldir_current->last_level + 1)
2060 level_nr = leveldir_current->last_level;
2062 if (leveldir_current->user_defined)
2063 level_nr = leveldir_current->last_level;
2065 leveldir_current->handicap_level = level_nr;
2068 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2070 freeSetupFileList(level_setup_list);
2073 Error(ERR_WARN, "using default setup values");
2078 void SaveLevelSetup_SeriesInfo()
2081 char *level_subdir = leveldir_current->filename;
2082 char *level_nr_str = int2str(level_nr, 0);
2083 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2086 /* ----------------------------------------------------------------------- */
2087 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2088 /* ----------------------------------------------------------------------- */
2090 InitLevelSetupDirectory(level_subdir);
2092 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2094 if (!(file = fopen(filename, MODE_WRITE)))
2096 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2101 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2102 getCookie("LEVELSETUP")));
2103 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2105 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2106 handicap_level_str));
2111 SetFilePermissions(filename, PERMS_PRIVATE);