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 char *getCustomSoundConfigFilename()
427 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
430 char *getCustomMusicDirectory(void)
432 static char *directory = NULL;
434 if (directory != NULL)
437 /* 1st try: look for special artwork in current level series directory */
438 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
439 if (fileExists(directory))
442 /* 2nd try: look for special artwork in private artwork directory */
443 directory = getStringCopy(getUserMusicDir());
444 if (fileExists(directory))
447 /* 3rd try: look for special artwork in configured artwork directory */
448 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
449 if (fileExists(directory))
452 /* 4th try: look for default artwork in new default artwork directory */
453 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
454 if (fileExists(directory))
457 /* 5th try: look for default artwork in old default artwork directory */
458 directory = getStringCopy(options.music_directory);
459 if (fileExists(directory))
462 return NULL; /* cannot find image file */
465 void InitTapeDirectory(char *level_subdir)
467 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
468 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
469 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
472 void InitScoreDirectory(char *level_subdir)
474 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
475 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
478 static void SaveUserLevelInfo();
480 void InitUserLevelDirectory(char *level_subdir)
482 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
484 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
485 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
486 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
492 void InitLevelSetupDirectory(char *level_subdir)
494 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
495 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
496 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
499 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
501 int file_version_major, file_version_minor, file_version_patch;
502 int game_version_major, game_version_minor, game_version_patch;
504 file_version_major = fgetc(file);
505 file_version_minor = fgetc(file);
506 file_version_patch = fgetc(file);
507 fgetc(file); /* not used */
509 game_version_major = fgetc(file);
510 game_version_minor = fgetc(file);
511 game_version_patch = fgetc(file);
512 fgetc(file); /* not used */
514 *file_version = VERSION_IDENT(file_version_major,
518 *game_version = VERSION_IDENT(game_version_major,
523 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
525 int file_version_major = VERSION_MAJOR(file_version);
526 int file_version_minor = VERSION_MINOR(file_version);
527 int file_version_patch = VERSION_PATCH(file_version);
528 int game_version_major = VERSION_MAJOR(game_version);
529 int game_version_minor = VERSION_MINOR(game_version);
530 int game_version_patch = VERSION_PATCH(game_version);
532 fputc(file_version_major, file);
533 fputc(file_version_minor, file);
534 fputc(file_version_patch, file);
535 fputc(0, file); /* not used */
537 fputc(game_version_major, file);
538 fputc(game_version_minor, file);
539 fputc(game_version_patch, file);
540 fputc(0, file); /* not used */
544 /* ------------------------------------------------------------------------- */
545 /* some functions to handle lists of level directories */
546 /* ------------------------------------------------------------------------- */
548 TreeInfo *newTreeInfo()
550 return checked_calloc(sizeof(TreeInfo));
553 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
555 node_new->next = *node_first;
556 *node_first = node_new;
559 int numTreeInfo(TreeInfo *node)
572 boolean validLevelSeries(TreeInfo *node)
574 return (node != NULL && !node->node_group && !node->parent_link);
577 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
581 if (node->node_top) /* start with first tree entry */
582 return getFirstValidTreeInfoEntry(*node->node_top);
586 else if (node->node_group) /* enter level group (step down into tree) */
587 return getFirstValidTreeInfoEntry(node->node_group);
588 else if (node->parent_link) /* skip start entry of level group */
590 if (node->next) /* get first real level series entry */
591 return getFirstValidTreeInfoEntry(node->next);
592 else /* leave empty level group and go on */
593 return getFirstValidTreeInfoEntry(node->node_parent->next);
595 else /* this seems to be a regular level series */
599 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
604 if (node->node_parent == NULL) /* top level group */
605 return *node->node_top;
606 else /* sub level group */
607 return node->node_parent->node_group;
610 int numTreeInfoInGroup(TreeInfo *node)
612 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
615 int posTreeInfo(TreeInfo *node)
617 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
622 if (node_cmp == node)
626 node_cmp = node_cmp->next;
632 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
634 TreeInfo *node_default = node;
649 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
651 if (filename == NULL)
656 if (node->node_group)
658 TreeInfo *node_group;
660 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
665 else if (!node->parent_link)
667 if (strcmp(filename, node->filename) == 0)
677 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
679 return getTreeInfoFromFilenameExt(ti, filename);
682 void dumpTreeInfo(TreeInfo *node, int depth)
686 printf("Dumping TreeInfo:\n");
690 for (i=0; i<(depth + 1) * 3; i++)
693 printf("filename == '%s' [%s]\n", node->filename, node->name);
695 if (node->node_group != NULL)
696 dumpTreeInfo(node->node_group, depth + 1);
702 void sortTreeInfo(TreeInfo **node_first,
703 int (*compare_function)(const void *, const void *))
705 int num_nodes = numTreeInfo(*node_first);
706 TreeInfo **sort_array;
707 TreeInfo *node = *node_first;
713 /* allocate array for sorting structure pointers */
714 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
716 /* writing structure pointers to sorting array */
717 while (i < num_nodes && node) /* double boundary check... */
719 sort_array[i] = node;
725 /* sorting the structure pointers in the sorting array */
726 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
729 /* update the linkage of list elements with the sorted node array */
730 for (i=0; i<num_nodes - 1; i++)
731 sort_array[i]->next = sort_array[i + 1];
732 sort_array[num_nodes - 1]->next = NULL;
734 /* update the linkage of the main list anchor pointer */
735 *node_first = sort_array[0];
739 /* now recursively sort the level group structures */
743 if (node->node_group != NULL)
744 sortTreeInfo(&node->node_group, compare_function);
751 /* ========================================================================= */
752 /* some stuff from "files.c" */
753 /* ========================================================================= */
755 #if defined(PLATFORM_WIN32)
757 #define S_IRGRP S_IRUSR
760 #define S_IROTH S_IRUSR
763 #define S_IWGRP S_IWUSR
766 #define S_IWOTH S_IWUSR
769 #define S_IXGRP S_IXUSR
772 #define S_IXOTH S_IXUSR
775 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
780 #endif /* PLATFORM_WIN32 */
782 /* file permissions for newly written files */
783 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
784 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
785 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
787 #define MODE_W_PRIVATE (S_IWUSR)
788 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
789 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
791 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
792 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
794 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
795 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
797 char *getUserDataDir(void)
799 static char *userdata_dir = NULL;
803 char *home_dir = getHomeDir();
804 char *data_dir = program.userdata_directory;
806 userdata_dir = getPath2(home_dir, data_dir);
814 return getUserDataDir();
817 static mode_t posix_umask(mode_t mask)
819 #if defined(PLATFORM_UNIX)
826 static int posix_mkdir(const char *pathname, mode_t mode)
828 #if defined(PLATFORM_WIN32)
829 return mkdir(pathname);
831 return mkdir(pathname, mode);
835 void createDirectory(char *dir, char *text, int permission_class)
837 /* leave "other" permissions in umask untouched, but ensure group parts
838 of USERDATA_DIR_MODE are not masked */
839 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
840 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
841 mode_t normal_umask = posix_umask(0);
842 mode_t group_umask = ~(dir_mode & S_IRWXG);
843 posix_umask(normal_umask & group_umask);
845 if (access(dir, F_OK) != 0)
846 if (posix_mkdir(dir, dir_mode) != 0)
847 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
849 posix_umask(normal_umask); /* reset normal umask */
852 void InitUserDataDirectory()
854 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
857 void SetFilePermissions(char *filename, int permission_class)
859 chmod(filename, (permission_class == PERMS_PRIVATE ?
860 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
863 char *getCookie(char *file_type)
865 static char cookie[MAX_COOKIE_LEN + 1];
867 if (strlen(program.cookie_prefix) + 1 +
868 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
869 return "[COOKIE ERROR]"; /* should never happen */
871 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
872 program.cookie_prefix, file_type,
873 program.version_major, program.version_minor);
878 int getFileVersionFromCookieString(const char *cookie)
880 const char *ptr_cookie1, *ptr_cookie2;
881 const char *pattern1 = "_FILE_VERSION_";
882 const char *pattern2 = "?.?";
883 const int len_cookie = strlen(cookie);
884 const int len_pattern1 = strlen(pattern1);
885 const int len_pattern2 = strlen(pattern2);
886 const int len_pattern = len_pattern1 + len_pattern2;
887 int version_major, version_minor;
889 if (len_cookie <= len_pattern)
892 ptr_cookie1 = &cookie[len_cookie - len_pattern];
893 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
895 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
898 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
899 ptr_cookie2[1] != '.' ||
900 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
903 version_major = ptr_cookie2[0] - '0';
904 version_minor = ptr_cookie2[2] - '0';
906 return VERSION_IDENT(version_major, version_minor, 0);
909 boolean checkCookieString(const char *cookie, const char *template)
911 const char *pattern = "_FILE_VERSION_?.?";
912 const int len_cookie = strlen(cookie);
913 const int len_template = strlen(template);
914 const int len_pattern = strlen(pattern);
916 if (len_cookie != len_template)
919 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
925 /* ------------------------------------------------------------------------- */
926 /* setup file list handling functions */
927 /* ------------------------------------------------------------------------- */
929 int get_string_integer_value(char *s)
931 static char *number_text[][3] =
933 { "0", "zero", "null", },
934 { "1", "one", "first" },
935 { "2", "two", "second" },
936 { "3", "three", "third" },
937 { "4", "four", "fourth" },
938 { "5", "five", "fifth" },
939 { "6", "six", "sixth" },
940 { "7", "seven", "seventh" },
941 { "8", "eight", "eighth" },
942 { "9", "nine", "ninth" },
943 { "10", "ten", "tenth" },
944 { "11", "eleven", "eleventh" },
945 { "12", "twelve", "twelfth" },
949 char *s_lower = getStringToLower(s);
954 if (strcmp(s_lower, number_text[i][j]) == 0)
965 boolean get_string_boolean_value(char *s)
967 char *s_lower = getStringToLower(s);
968 boolean result = FALSE;
970 if (strcmp(s_lower, "true") == 0 ||
971 strcmp(s_lower, "yes") == 0 ||
972 strcmp(s_lower, "on") == 0 ||
973 get_string_integer_value(s) == 1)
981 char *getFormattedSetupEntry(char *token, char *value)
984 static char entry[MAX_LINE_LEN];
986 sprintf(entry, "%s:", token);
987 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
991 strcat(entry, value);
996 void freeSetupFileList(struct SetupFileList *setup_file_list)
998 if (!setup_file_list)
1001 if (setup_file_list->token)
1002 free(setup_file_list->token);
1003 if (setup_file_list->value)
1004 free(setup_file_list->value);
1005 if (setup_file_list->next)
1006 freeSetupFileList(setup_file_list->next);
1007 free(setup_file_list);
1010 static struct SetupFileList *newSetupFileList(char *token, char *value)
1012 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1014 new->token = checked_malloc(strlen(token) + 1);
1015 strcpy(new->token, token);
1017 new->value = checked_malloc(strlen(value) + 1);
1018 strcpy(new->value, value);
1025 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1027 if (!setup_file_list)
1030 if (strcmp(setup_file_list->token, token) == 0)
1031 return setup_file_list->value;
1033 return getTokenValue(setup_file_list->next, token);
1036 static void setTokenValue(struct SetupFileList *setup_file_list,
1037 char *token, char *value)
1039 if (!setup_file_list)
1042 if (strcmp(setup_file_list->token, token) == 0)
1044 free(setup_file_list->value);
1045 setup_file_list->value = checked_malloc(strlen(value) + 1);
1046 strcpy(setup_file_list->value, value);
1048 else if (setup_file_list->next == NULL)
1049 setup_file_list->next = newSetupFileList(token, value);
1051 setTokenValue(setup_file_list->next, token, value);
1055 static void printSetupFileList(struct SetupFileList *setup_file_list)
1057 if (!setup_file_list)
1060 printf("token: '%s'\n", setup_file_list->token);
1061 printf("value: '%s'\n", setup_file_list->value);
1063 printSetupFileList(setup_file_list->next);
1067 struct SetupFileList *loadSetupFileList(char *filename)
1070 char line[MAX_LINE_LEN];
1071 char *token, *value, *line_ptr;
1072 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1073 struct SetupFileList *first_valid_list_entry;
1077 if (!(file = fopen(filename, MODE_READ)))
1079 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1085 /* read next line of input file */
1086 if (!fgets(line, MAX_LINE_LEN, file))
1089 /* cut trailing comment or whitespace from input line */
1090 for (line_ptr = line; *line_ptr; line_ptr++)
1092 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1099 /* cut trailing whitespaces from input line */
1100 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1101 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1104 /* ignore empty lines */
1108 line_len = strlen(line);
1110 /* cut leading whitespaces from token */
1111 for (token = line; *token; token++)
1112 if (*token != ' ' && *token != '\t')
1115 /* find end of token */
1116 for (line_ptr = token; *line_ptr; line_ptr++)
1118 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1125 if (line_ptr < line + line_len)
1126 value = line_ptr + 1;
1130 /* cut leading whitespaces from value */
1131 for (; *value; value++)
1132 if (*value != ' ' && *value != '\t')
1135 if (*token && *value)
1136 setTokenValue(setup_file_list, token, value);
1141 first_valid_list_entry = setup_file_list->next;
1143 /* free empty list header */
1144 setup_file_list->next = NULL;
1145 freeSetupFileList(setup_file_list);
1147 if (first_valid_list_entry == NULL)
1148 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1150 return first_valid_list_entry;
1153 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1156 if (!setup_file_list)
1159 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1161 if (!checkCookieString(setup_file_list->value, identifier))
1163 Error(ERR_WARN, "configuration file has wrong file identifier");
1170 if (setup_file_list->next)
1171 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1174 Error(ERR_WARN, "configuration file has no file identifier");
1180 /* ========================================================================= */
1181 /* setup file stuff */
1182 /* ========================================================================= */
1184 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1185 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1186 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1188 /* level directory info */
1189 #define LEVELINFO_TOKEN_NAME 0
1190 #define LEVELINFO_TOKEN_NAME_SHORT 1
1191 #define LEVELINFO_TOKEN_NAME_SORTING 2
1192 #define LEVELINFO_TOKEN_AUTHOR 3
1193 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1194 #define LEVELINFO_TOKEN_LEVELS 5
1195 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1196 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1197 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1198 #define LEVELINFO_TOKEN_READONLY 9
1200 #define NUM_LEVELINFO_TOKENS 10
1202 static LevelDirTree ldi;
1204 static struct TokenInfo levelinfo_tokens[] =
1206 /* level directory info */
1207 { TYPE_STRING, &ldi.name, "name" },
1208 { TYPE_STRING, &ldi.name_short, "name_short" },
1209 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1210 { TYPE_STRING, &ldi.author, "author" },
1211 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1212 { TYPE_INTEGER, &ldi.levels, "levels" },
1213 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1214 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1215 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1216 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1219 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1223 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1224 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1225 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1226 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1229 ldi->node_parent = NULL;
1230 ldi->node_group = NULL;
1234 ldi->cl_cursor = -1;
1236 ldi->filename = NULL;
1237 ldi->fullpath = NULL;
1238 ldi->basepath = NULL;
1239 ldi->name = getStringCopy(ANONYMOUS_NAME);
1240 ldi->name_short = NULL;
1241 ldi->name_sorting = NULL;
1242 ldi->author = getStringCopy(ANONYMOUS_NAME);
1244 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1245 ldi->parent_link = FALSE;
1246 ldi->user_defined = FALSE;
1248 ldi->class_desc = NULL;
1250 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1252 ldi->imported_from = NULL;
1254 ldi->first_level = 0;
1255 ldi->last_level = 0;
1256 ldi->level_group = FALSE;
1257 ldi->handicap_level = 0;
1258 ldi->readonly = TRUE;
1262 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1266 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1268 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1272 /* first copy all values from the parent structure ... */
1275 /* ... then set all fields to default that cannot be inherited from parent.
1276 This is especially important for all those fields that can be set from
1277 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1278 calls 'free()' for all already set token values which requires that no
1279 other structure's pointer may point to them!
1282 ldi->filename = NULL;
1283 ldi->fullpath = NULL;
1284 ldi->basepath = NULL;
1285 ldi->name = getStringCopy(ANONYMOUS_NAME);
1286 ldi->name_short = NULL;
1287 ldi->name_sorting = NULL;
1288 ldi->author = getStringCopy(parent->author);
1289 ldi->imported_from = getStringCopy(parent->imported_from);
1291 ldi->level_group = FALSE;
1292 ldi->parent_link = FALSE;
1294 ldi->node_top = parent->node_top;
1295 ldi->node_parent = parent;
1296 ldi->node_group = NULL;
1300 void setSetupInfo(struct TokenInfo *token_info,
1301 int token_nr, char *token_value)
1303 int token_type = token_info[token_nr].type;
1304 void *setup_value = token_info[token_nr].value;
1306 if (token_value == NULL)
1309 /* set setup field to corresponding token value */
1314 *(boolean *)setup_value = get_string_boolean_value(token_value);
1318 *(Key *)setup_value = getKeyFromKeyName(token_value);
1322 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1326 *(int *)setup_value = get_string_integer_value(token_value);
1330 if (*(char **)setup_value != NULL)
1331 free(*(char **)setup_value);
1332 *(char **)setup_value = getStringCopy(token_value);
1340 static int compareTreeInfoEntries(const void *object1, const void *object2)
1342 const TreeInfo *entry1 = *((TreeInfo **)object1);
1343 const TreeInfo *entry2 = *((TreeInfo **)object2);
1346 if (entry1->parent_link || entry2->parent_link)
1347 compare_result = (entry1->parent_link ? -1 : +1);
1348 else if (entry1->sort_priority == entry2->sort_priority)
1350 char *name1 = getStringToLower(entry1->name_sorting);
1351 char *name2 = getStringToLower(entry2->name_sorting);
1353 compare_result = strcmp(name1, name2);
1358 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1359 compare_result = entry1->sort_priority - entry2->sort_priority;
1361 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1363 return compare_result;
1366 static void createParentTreeInfoNode(TreeInfo *node_parent)
1370 if (node_parent == NULL)
1373 ti_new = newTreeInfo();
1374 setTreeInfoToDefaults(ti_new, node_parent->type);
1376 ti_new->node_parent = node_parent;
1377 ti_new->parent_link = TRUE;
1379 ti_new->name = ".. (parent directory)";
1380 ti_new->name_short = getStringCopy(ti_new->name);
1381 ti_new->name_sorting = getStringCopy(ti_new->name);
1383 ti_new->filename = "..";
1384 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1386 ti_new->sort_priority = node_parent->sort_priority;
1387 ti_new->class_desc = getLevelClassDescription(ti_new);
1389 pushTreeInfo(&node_parent->node_group, ti_new);
1392 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1393 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1395 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1396 TreeInfo *node_parent,
1397 char *level_directory,
1398 char *directory_name)
1400 char *directory_path = getPath2(level_directory, directory_name);
1401 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1402 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1403 LevelDirTree *leveldir_new = NULL;
1406 if (setup_file_list == NULL)
1408 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1410 free(directory_path);
1416 leveldir_new = newTreeInfo();
1419 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1421 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1423 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1425 /* set all structure fields according to the token/value pairs */
1426 ldi = *leveldir_new;
1427 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1428 setSetupInfo(levelinfo_tokens, i,
1429 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1430 *leveldir_new = ldi;
1432 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1434 if (leveldir_new->name_short == NULL)
1435 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1437 if (leveldir_new->name_sorting == NULL)
1438 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1440 leveldir_new->filename = getStringCopy(directory_name);
1442 if (node_parent == NULL) /* top level group */
1444 leveldir_new->basepath = level_directory;
1445 leveldir_new->fullpath = leveldir_new->filename;
1447 else /* sub level group */
1449 leveldir_new->basepath = node_parent->basepath;
1450 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1453 if (leveldir_new->levels < 1)
1454 leveldir_new->levels = 1;
1456 leveldir_new->last_level =
1457 leveldir_new->first_level + leveldir_new->levels - 1;
1459 leveldir_new->user_defined =
1460 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1462 leveldir_new->color = LEVELCOLOR(leveldir_new);
1463 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1465 leveldir_new->handicap_level = /* set handicap to default value */
1466 (leveldir_new->user_defined ?
1467 leveldir_new->last_level :
1468 leveldir_new->first_level);
1470 pushTreeInfo(node_first, leveldir_new);
1472 freeSetupFileList(setup_file_list);
1474 if (leveldir_new->level_group)
1476 /* create node to link back to current level directory */
1477 createParentTreeInfoNode(leveldir_new);
1479 /* step into sub-directory and look for more level series */
1480 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1481 leveldir_new, directory_path);
1484 free(directory_path);
1490 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1491 TreeInfo *node_parent,
1492 char *level_directory)
1495 struct dirent *dir_entry;
1496 boolean valid_entry_found = FALSE;
1498 if ((dir = opendir(level_directory)) == NULL)
1500 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1504 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1506 struct stat file_status;
1507 char *directory_name = dir_entry->d_name;
1508 char *directory_path = getPath2(level_directory, directory_name);
1510 /* skip entries for current and parent directory */
1511 if (strcmp(directory_name, ".") == 0 ||
1512 strcmp(directory_name, "..") == 0)
1514 free(directory_path);
1518 /* find out if directory entry is itself a directory */
1519 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1520 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1522 free(directory_path);
1526 free(directory_path);
1528 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1535 if (!valid_entry_found)
1537 /* check if this directory directly contains a file "levelinfo.conf" */
1538 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1539 level_directory, ".");
1542 if (!valid_entry_found)
1543 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1547 void LoadLevelInfo()
1549 InitUserLevelDirectory(getLoginName());
1551 DrawInitText("Loading level series:", 120, FC_GREEN);
1553 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1554 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1556 /* before sorting, the first entries will be from the user directory */
1557 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1559 if (leveldir_first == NULL)
1560 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1562 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1565 dumpTreeInfo(leveldir_first, 0);
1569 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1570 TreeInfo *node_parent,
1571 char *base_directory,
1572 char *directory_name, int type)
1574 char *directory_path = getPath2(base_directory, directory_name);
1576 getPath2(directory_path,
1577 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1578 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1579 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1580 struct SetupFileList *setup_file_list = NULL;
1581 TreeInfo *artwork_new = NULL;
1582 char *check_dir = NULL;
1585 if (access(filename, F_OK) == 0) /* file exists */
1586 loadSetupFileList(filename);
1588 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1591 struct dirent *dir_entry;
1592 boolean valid_file_found = FALSE;
1594 if ((dir = opendir(directory_path)) != NULL)
1596 while ((dir_entry = readdir(dir)) != NULL)
1598 char *entry_name = dir_entry->d_name;
1600 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1601 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1602 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1604 valid_file_found = TRUE;
1612 if (!valid_file_found)
1615 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1617 free(directory_path);
1624 artwork_new = newTreeInfo();
1627 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1629 setTreeInfoToDefaults(artwork_new, type);
1631 artwork_new->filename = getStringCopy(directory_name);
1633 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1636 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1639 /* set all structure fields according to the token/value pairs */
1641 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1642 setSetupInfo(levelinfo_tokens, i,
1643 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1646 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1648 if (artwork_new->name_short == NULL)
1649 artwork_new->name_short = getStringCopy(artwork_new->name);
1651 if (artwork_new->name_sorting == NULL)
1652 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1655 if (node_parent == NULL) /* top level group */
1657 artwork_new->basepath = getStringCopy(base_directory);
1658 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1660 else /* sub level group */
1662 artwork_new->basepath = getStringCopy(node_parent->basepath);
1663 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1666 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1667 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1668 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1669 artwork_new->user_defined =
1670 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1672 /* (may use ".sort_priority" from "setup_file_list" above) */
1673 artwork_new->color = LEVELCOLOR(artwork_new);
1674 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1676 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1678 if (artwork_new->name != NULL)
1679 free(artwork_new->name);
1681 if (strcmp(artwork_new->filename, ".") == 0)
1683 if (artwork_new->user_defined)
1684 artwork_new->name = getStringCopy("private");
1686 artwork_new->name = getStringCopy("default");
1689 artwork_new->name = getStringCopy(artwork_new->filename);
1691 artwork_new->name_short = getStringCopy(artwork_new->name);
1692 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1695 pushTreeInfo(node_first, artwork_new);
1697 freeSetupFileList(setup_file_list);
1699 free(directory_path);
1705 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1706 TreeInfo *node_parent,
1707 char *base_directory, int type)
1710 struct dirent *dir_entry;
1711 boolean valid_entry_found = FALSE;
1713 if ((dir = opendir(base_directory)) == NULL)
1715 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1716 base_directory == options.graphics_directory) ||
1717 (type == TREE_TYPE_SOUNDS_DIR &&
1718 base_directory == options.sounds_directory) ||
1719 (type == TREE_TYPE_MUSIC_DIR &&
1720 base_directory == options.music_directory))
1721 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1725 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1727 struct stat file_status;
1728 char *directory_name = dir_entry->d_name;
1729 char *directory_path = getPath2(base_directory, directory_name);
1731 /* skip entries for current and parent directory */
1732 if (strcmp(directory_name, ".") == 0 ||
1733 strcmp(directory_name, "..") == 0)
1735 free(directory_path);
1739 /* find out if directory entry is itself a directory */
1740 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1741 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1743 free(directory_path);
1747 free(directory_path);
1749 /* check if this directory contains artwork with or without config file */
1750 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1752 directory_name, type);
1757 /* check if this directory directly contains artwork itself */
1758 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1759 base_directory, ".",
1761 if (!valid_entry_found)
1762 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1766 void LoadArtworkInfo()
1768 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1770 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1771 options.graphics_directory,
1772 TREE_TYPE_GRAPHICS_DIR);
1773 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1774 getUserGraphicsDir(),
1775 TREE_TYPE_GRAPHICS_DIR);
1777 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1778 options.sounds_directory,
1779 TREE_TYPE_SOUNDS_DIR);
1780 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1782 TREE_TYPE_SOUNDS_DIR);
1784 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1785 options.music_directory,
1786 TREE_TYPE_MUSIC_DIR);
1787 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1789 TREE_TYPE_MUSIC_DIR);
1791 /* before sorting, the first entries will be from the user directory */
1792 artwork.gfx_current =
1793 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1794 if (artwork.gfx_current == NULL)
1795 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1797 artwork.snd_current =
1798 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1799 if (artwork.snd_current == NULL)
1800 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1802 artwork.mus_current =
1803 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1804 if (artwork.mus_current == NULL)
1805 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1807 artwork.graphics_set_current = artwork.gfx_current->name;
1808 artwork.sounds_set_current = artwork.snd_current->name;
1809 artwork.music_set_current = artwork.mus_current->name;
1811 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1812 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1813 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1816 dumpTreeInfo(artwork.gfx_first, 0);
1817 dumpTreeInfo(artwork.snd_first, 0);
1818 dumpTreeInfo(artwork.mus_first, 0);
1822 static void SaveUserLevelInfo()
1828 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1830 if (!(file = fopen(filename, MODE_WRITE)))
1832 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1837 /* always start with reliable default values */
1838 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1840 ldi.name = getLoginName();
1841 ldi.author = getRealName();
1843 ldi.first_level = 1;
1844 ldi.sort_priority = LEVELCLASS_USER_START;
1845 ldi.readonly = FALSE;
1847 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1848 getCookie("LEVELINFO")));
1850 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1851 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1852 i != LEVELINFO_TOKEN_NAME_SORTING &&
1853 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1854 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1859 SetFilePermissions(filename, PERMS_PRIVATE);
1862 char *getSetupValue(int type, void *value)
1864 static char value_string[MAX_LINE_LEN];
1869 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1873 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1877 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1881 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1885 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1889 sprintf(value_string, "%d", *(int *)value);
1893 strcpy(value_string, *(char **)value);
1897 value_string[0] = '\0';
1901 return value_string;
1904 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1907 static char entry[MAX_LINE_LEN];
1908 int token_type = token_info[token_nr].type;
1909 void *setup_value = token_info[token_nr].value;
1910 char *token_text = token_info[token_nr].text;
1911 char *value_string = getSetupValue(token_type, setup_value);
1913 /* start with the prefix, token and some spaces to format output line */
1914 sprintf(entry, "%s%s:", prefix, token_text);
1915 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1918 /* continue with the token's value (which can have different types) */
1919 strcat(entry, value_string);
1921 if (token_type == TYPE_KEY_X11)
1923 Key key = *(Key *)setup_value;
1924 char *keyname = getKeyNameFromKey(key);
1926 /* add comment, if useful */
1927 if (strcmp(keyname, "(undefined)") != 0 &&
1928 strcmp(keyname, "(unknown)") != 0)
1930 for (i=strlen(entry); i<50; i++)
1933 strcat(entry, "# ");
1934 strcat(entry, keyname);
1941 void LoadLevelSetup_LastSeries()
1944 struct SetupFileList *level_setup_list = NULL;
1946 /* always start with reliable default values */
1947 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1949 /* ----------------------------------------------------------------------- */
1950 /* ~/.<program>/levelsetup.conf */
1951 /* ----------------------------------------------------------------------- */
1953 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1955 if ((level_setup_list = loadSetupFileList(filename)))
1957 char *last_level_series =
1958 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1960 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1962 if (leveldir_current == NULL)
1963 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1965 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1967 freeSetupFileList(level_setup_list);
1970 Error(ERR_WARN, "using default setup values");
1975 void SaveLevelSetup_LastSeries()
1978 char *level_subdir = leveldir_current->filename;
1981 /* ----------------------------------------------------------------------- */
1982 /* ~/.<program>/levelsetup.conf */
1983 /* ----------------------------------------------------------------------- */
1985 InitUserDataDirectory();
1987 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1989 if (!(file = fopen(filename, MODE_WRITE)))
1991 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1996 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1997 getCookie("LEVELSETUP")));
1998 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2004 SetFilePermissions(filename, PERMS_PRIVATE);
2007 static void checkSeriesInfo()
2009 static char *level_directory = NULL;
2011 struct dirent *dir_entry;
2013 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2015 level_directory = getPath2((leveldir_current->user_defined ?
2016 getUserLevelDir(NULL) :
2017 options.level_directory),
2018 leveldir_current->fullpath);
2020 if ((dir = opendir(level_directory)) == NULL)
2022 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2026 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2028 if (strlen(dir_entry->d_name) > 4 &&
2029 dir_entry->d_name[3] == '.' &&
2030 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2032 char levelnum_str[4];
2035 strncpy(levelnum_str, dir_entry->d_name, 3);
2036 levelnum_str[3] = '\0';
2038 levelnum_value = atoi(levelnum_str);
2040 if (levelnum_value < leveldir_current->first_level)
2042 Error(ERR_WARN, "additional level %d found", levelnum_value);
2043 leveldir_current->first_level = levelnum_value;
2045 else if (levelnum_value > leveldir_current->last_level)
2047 Error(ERR_WARN, "additional level %d found", levelnum_value);
2048 leveldir_current->last_level = levelnum_value;
2056 void LoadLevelSetup_SeriesInfo()
2059 struct SetupFileList *level_setup_list = NULL;
2060 char *level_subdir = leveldir_current->filename;
2062 /* always start with reliable default values */
2063 level_nr = leveldir_current->first_level;
2065 checkSeriesInfo(leveldir_current);
2067 /* ----------------------------------------------------------------------- */
2068 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2069 /* ----------------------------------------------------------------------- */
2071 level_subdir = leveldir_current->filename;
2073 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2075 if ((level_setup_list = loadSetupFileList(filename)))
2079 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2083 level_nr = atoi(token_value);
2085 if (level_nr < leveldir_current->first_level)
2086 level_nr = leveldir_current->first_level;
2087 if (level_nr > leveldir_current->last_level)
2088 level_nr = leveldir_current->last_level;
2091 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2095 int level_nr = atoi(token_value);
2097 if (level_nr < leveldir_current->first_level)
2098 level_nr = leveldir_current->first_level;
2099 if (level_nr > leveldir_current->last_level + 1)
2100 level_nr = leveldir_current->last_level;
2102 if (leveldir_current->user_defined)
2103 level_nr = leveldir_current->last_level;
2105 leveldir_current->handicap_level = level_nr;
2108 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2110 freeSetupFileList(level_setup_list);
2113 Error(ERR_WARN, "using default setup values");
2118 void SaveLevelSetup_SeriesInfo()
2121 char *level_subdir = leveldir_current->filename;
2122 char *level_nr_str = int2str(level_nr, 0);
2123 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2126 /* ----------------------------------------------------------------------- */
2127 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2128 /* ----------------------------------------------------------------------- */
2130 InitLevelSetupDirectory(level_subdir);
2132 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2134 if (!(file = fopen(filename, MODE_WRITE)))
2136 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2141 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2142 getCookie("LEVELSETUP")));
2143 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2145 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2146 handicap_level_str));
2151 SetFilePermissions(filename, PERMS_PRIVATE);