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 *getCustomMusicDirectory(void)
427 static char *directory = NULL;
429 if (directory != NULL)
432 /* 1st try: look for special artwork in current level series directory */
433 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
434 if (fileExists(directory))
437 /* 2nd try: look for special artwork in private artwork directory */
438 directory = getStringCopy(getUserMusicDir());
439 if (fileExists(directory))
442 /* 3rd try: look for special artwork in configured artwork directory */
443 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
444 if (fileExists(directory))
447 /* 4th try: look for default artwork in new default artwork directory */
448 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
449 if (fileExists(directory))
452 /* 5th try: look for default artwork in old default artwork directory */
453 directory = getStringCopy(options.music_directory);
454 if (fileExists(directory))
457 return NULL; /* cannot find image file */
460 void InitTapeDirectory(char *level_subdir)
462 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
463 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
464 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
467 void InitScoreDirectory(char *level_subdir)
469 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
470 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
473 static void SaveUserLevelInfo();
475 void InitUserLevelDirectory(char *level_subdir)
477 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
479 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
480 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
481 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
487 void InitLevelSetupDirectory(char *level_subdir)
489 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
490 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
491 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
494 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
496 int file_version_major, file_version_minor, file_version_patch;
497 int game_version_major, game_version_minor, game_version_patch;
499 file_version_major = fgetc(file);
500 file_version_minor = fgetc(file);
501 file_version_patch = fgetc(file);
502 fgetc(file); /* not used */
504 game_version_major = fgetc(file);
505 game_version_minor = fgetc(file);
506 game_version_patch = fgetc(file);
507 fgetc(file); /* not used */
509 *file_version = VERSION_IDENT(file_version_major,
513 *game_version = VERSION_IDENT(game_version_major,
518 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
520 int file_version_major = VERSION_MAJOR(file_version);
521 int file_version_minor = VERSION_MINOR(file_version);
522 int file_version_patch = VERSION_PATCH(file_version);
523 int game_version_major = VERSION_MAJOR(game_version);
524 int game_version_minor = VERSION_MINOR(game_version);
525 int game_version_patch = VERSION_PATCH(game_version);
527 fputc(file_version_major, file);
528 fputc(file_version_minor, file);
529 fputc(file_version_patch, file);
530 fputc(0, file); /* not used */
532 fputc(game_version_major, file);
533 fputc(game_version_minor, file);
534 fputc(game_version_patch, file);
535 fputc(0, file); /* not used */
539 /* ------------------------------------------------------------------------- */
540 /* some functions to handle lists of level directories */
541 /* ------------------------------------------------------------------------- */
543 TreeInfo *newTreeInfo()
545 return checked_calloc(sizeof(TreeInfo));
548 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
550 node_new->next = *node_first;
551 *node_first = node_new;
554 int numTreeInfo(TreeInfo *node)
567 boolean validLevelSeries(TreeInfo *node)
569 return (node != NULL && !node->node_group && !node->parent_link);
572 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
576 if (node->node_top) /* start with first tree entry */
577 return getFirstValidTreeInfoEntry(*node->node_top);
581 else if (node->node_group) /* enter level group (step down into tree) */
582 return getFirstValidTreeInfoEntry(node->node_group);
583 else if (node->parent_link) /* skip start entry of level group */
585 if (node->next) /* get first real level series entry */
586 return getFirstValidTreeInfoEntry(node->next);
587 else /* leave empty level group and go on */
588 return getFirstValidTreeInfoEntry(node->node_parent->next);
590 else /* this seems to be a regular level series */
594 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
599 if (node->node_parent == NULL) /* top level group */
600 return *node->node_top;
601 else /* sub level group */
602 return node->node_parent->node_group;
605 int numTreeInfoInGroup(TreeInfo *node)
607 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
610 int posTreeInfo(TreeInfo *node)
612 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
617 if (node_cmp == node)
621 node_cmp = node_cmp->next;
627 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
629 TreeInfo *node_default = node;
644 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
646 if (filename == NULL)
651 if (node->node_group)
653 TreeInfo *node_group;
655 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
660 else if (!node->parent_link)
662 if (strcmp(filename, node->filename) == 0)
672 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
674 return getTreeInfoFromFilenameExt(ti, filename);
677 void dumpTreeInfo(TreeInfo *node, int depth)
681 printf("Dumping TreeInfo:\n");
685 for (i=0; i<(depth + 1) * 3; i++)
688 printf("filename == '%s' [%s]\n", node->filename, node->name);
690 if (node->node_group != NULL)
691 dumpTreeInfo(node->node_group, depth + 1);
697 void sortTreeInfo(TreeInfo **node_first,
698 int (*compare_function)(const void *, const void *))
700 int num_nodes = numTreeInfo(*node_first);
701 TreeInfo **sort_array;
702 TreeInfo *node = *node_first;
708 /* allocate array for sorting structure pointers */
709 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
711 /* writing structure pointers to sorting array */
712 while (i < num_nodes && node) /* double boundary check... */
714 sort_array[i] = node;
720 /* sorting the structure pointers in the sorting array */
721 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
724 /* update the linkage of list elements with the sorted node array */
725 for (i=0; i<num_nodes - 1; i++)
726 sort_array[i]->next = sort_array[i + 1];
727 sort_array[num_nodes - 1]->next = NULL;
729 /* update the linkage of the main list anchor pointer */
730 *node_first = sort_array[0];
734 /* now recursively sort the level group structures */
738 if (node->node_group != NULL)
739 sortTreeInfo(&node->node_group, compare_function);
746 /* ========================================================================= */
747 /* some stuff from "files.c" */
748 /* ========================================================================= */
750 #if defined(PLATFORM_WIN32)
752 #define S_IRGRP S_IRUSR
755 #define S_IROTH S_IRUSR
758 #define S_IWGRP S_IWUSR
761 #define S_IWOTH S_IWUSR
764 #define S_IXGRP S_IXUSR
767 #define S_IXOTH S_IXUSR
770 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
775 #endif /* PLATFORM_WIN32 */
777 /* file permissions for newly written files */
778 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
779 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
780 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
782 #define MODE_W_PRIVATE (S_IWUSR)
783 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
784 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
786 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
787 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
789 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
790 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
792 char *getUserDataDir(void)
794 static char *userdata_dir = NULL;
798 char *home_dir = getHomeDir();
799 char *data_dir = program.userdata_directory;
801 userdata_dir = getPath2(home_dir, data_dir);
809 return getUserDataDir();
812 static mode_t posix_umask(mode_t mask)
814 #if defined(PLATFORM_UNIX)
821 static int posix_mkdir(const char *pathname, mode_t mode)
823 #if defined(PLATFORM_WIN32)
824 return mkdir(pathname);
826 return mkdir(pathname, mode);
830 void createDirectory(char *dir, char *text, int permission_class)
832 /* leave "other" permissions in umask untouched, but ensure group parts
833 of USERDATA_DIR_MODE are not masked */
834 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
835 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
836 mode_t normal_umask = posix_umask(0);
837 mode_t group_umask = ~(dir_mode & S_IRWXG);
838 posix_umask(normal_umask & group_umask);
840 if (access(dir, F_OK) != 0)
841 if (posix_mkdir(dir, dir_mode) != 0)
842 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
844 posix_umask(normal_umask); /* reset normal umask */
847 void InitUserDataDirectory()
849 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
852 void SetFilePermissions(char *filename, int permission_class)
854 chmod(filename, (permission_class == PERMS_PRIVATE ?
855 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
858 char *getCookie(char *file_type)
860 static char cookie[MAX_COOKIE_LEN + 1];
862 if (strlen(program.cookie_prefix) + 1 +
863 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
864 return "[COOKIE ERROR]"; /* should never happen */
866 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
867 program.cookie_prefix, file_type,
868 program.version_major, program.version_minor);
873 int getFileVersionFromCookieString(const char *cookie)
875 const char *ptr_cookie1, *ptr_cookie2;
876 const char *pattern1 = "_FILE_VERSION_";
877 const char *pattern2 = "?.?";
878 const int len_cookie = strlen(cookie);
879 const int len_pattern1 = strlen(pattern1);
880 const int len_pattern2 = strlen(pattern2);
881 const int len_pattern = len_pattern1 + len_pattern2;
882 int version_major, version_minor;
884 if (len_cookie <= len_pattern)
887 ptr_cookie1 = &cookie[len_cookie - len_pattern];
888 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
890 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
893 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
894 ptr_cookie2[1] != '.' ||
895 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
898 version_major = ptr_cookie2[0] - '0';
899 version_minor = ptr_cookie2[2] - '0';
901 return VERSION_IDENT(version_major, version_minor, 0);
904 boolean checkCookieString(const char *cookie, const char *template)
906 const char *pattern = "_FILE_VERSION_?.?";
907 const int len_cookie = strlen(cookie);
908 const int len_template = strlen(template);
909 const int len_pattern = strlen(pattern);
911 if (len_cookie != len_template)
914 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
920 /* ------------------------------------------------------------------------- */
921 /* setup file list handling functions */
922 /* ------------------------------------------------------------------------- */
924 int get_string_integer_value(char *s)
926 static char *number_text[][3] =
928 { "0", "zero", "null", },
929 { "1", "one", "first" },
930 { "2", "two", "second" },
931 { "3", "three", "third" },
932 { "4", "four", "fourth" },
933 { "5", "five", "fifth" },
934 { "6", "six", "sixth" },
935 { "7", "seven", "seventh" },
936 { "8", "eight", "eighth" },
937 { "9", "nine", "ninth" },
938 { "10", "ten", "tenth" },
939 { "11", "eleven", "eleventh" },
940 { "12", "twelve", "twelfth" },
944 char *s_lower = getStringToLower(s);
949 if (strcmp(s_lower, number_text[i][j]) == 0)
960 boolean get_string_boolean_value(char *s)
962 char *s_lower = getStringToLower(s);
963 boolean result = FALSE;
965 if (strcmp(s_lower, "true") == 0 ||
966 strcmp(s_lower, "yes") == 0 ||
967 strcmp(s_lower, "on") == 0 ||
968 get_string_integer_value(s) == 1)
976 char *getFormattedSetupEntry(char *token, char *value)
979 static char entry[MAX_LINE_LEN];
981 sprintf(entry, "%s:", token);
982 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
986 strcat(entry, value);
991 void freeSetupFileList(struct SetupFileList *setup_file_list)
993 if (!setup_file_list)
996 if (setup_file_list->token)
997 free(setup_file_list->token);
998 if (setup_file_list->value)
999 free(setup_file_list->value);
1000 if (setup_file_list->next)
1001 freeSetupFileList(setup_file_list->next);
1002 free(setup_file_list);
1005 static struct SetupFileList *newSetupFileList(char *token, char *value)
1007 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1009 new->token = checked_malloc(strlen(token) + 1);
1010 strcpy(new->token, token);
1012 new->value = checked_malloc(strlen(value) + 1);
1013 strcpy(new->value, value);
1020 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1022 if (!setup_file_list)
1025 if (strcmp(setup_file_list->token, token) == 0)
1026 return setup_file_list->value;
1028 return getTokenValue(setup_file_list->next, token);
1031 static void setTokenValue(struct SetupFileList *setup_file_list,
1032 char *token, char *value)
1034 if (!setup_file_list)
1037 if (strcmp(setup_file_list->token, token) == 0)
1039 free(setup_file_list->value);
1040 setup_file_list->value = checked_malloc(strlen(value) + 1);
1041 strcpy(setup_file_list->value, value);
1043 else if (setup_file_list->next == NULL)
1044 setup_file_list->next = newSetupFileList(token, value);
1046 setTokenValue(setup_file_list->next, token, value);
1050 static void printSetupFileList(struct SetupFileList *setup_file_list)
1052 if (!setup_file_list)
1055 printf("token: '%s'\n", setup_file_list->token);
1056 printf("value: '%s'\n", setup_file_list->value);
1058 printSetupFileList(setup_file_list->next);
1062 struct SetupFileList *loadSetupFileList(char *filename)
1065 char line[MAX_LINE_LEN];
1066 char *token, *value, *line_ptr;
1067 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1068 struct SetupFileList *first_valid_list_entry;
1072 if (!(file = fopen(filename, MODE_READ)))
1074 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1080 /* read next line of input file */
1081 if (!fgets(line, MAX_LINE_LEN, file))
1084 /* cut trailing comment or whitespace from input line */
1085 for (line_ptr = line; *line_ptr; line_ptr++)
1087 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1094 /* cut trailing whitespaces from input line */
1095 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1096 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1099 /* ignore empty lines */
1103 line_len = strlen(line);
1105 /* cut leading whitespaces from token */
1106 for (token = line; *token; token++)
1107 if (*token != ' ' && *token != '\t')
1110 /* find end of token */
1111 for (line_ptr = token; *line_ptr; line_ptr++)
1113 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1120 if (line_ptr < line + line_len)
1121 value = line_ptr + 1;
1125 /* cut leading whitespaces from value */
1126 for (; *value; value++)
1127 if (*value != ' ' && *value != '\t')
1130 if (*token && *value)
1131 setTokenValue(setup_file_list, token, value);
1136 first_valid_list_entry = setup_file_list->next;
1138 /* free empty list header */
1139 setup_file_list->next = NULL;
1140 freeSetupFileList(setup_file_list);
1142 if (first_valid_list_entry == NULL)
1143 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1145 return first_valid_list_entry;
1148 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1151 if (!setup_file_list)
1154 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1156 if (!checkCookieString(setup_file_list->value, identifier))
1158 Error(ERR_WARN, "configuration file has wrong file identifier");
1165 if (setup_file_list->next)
1166 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1169 Error(ERR_WARN, "configuration file has no file identifier");
1175 /* ========================================================================= */
1176 /* setup file stuff */
1177 /* ========================================================================= */
1179 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1180 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1181 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1183 /* level directory info */
1184 #define LEVELINFO_TOKEN_NAME 0
1185 #define LEVELINFO_TOKEN_NAME_SHORT 1
1186 #define LEVELINFO_TOKEN_NAME_SORTING 2
1187 #define LEVELINFO_TOKEN_AUTHOR 3
1188 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1189 #define LEVELINFO_TOKEN_LEVELS 5
1190 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1191 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1192 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1193 #define LEVELINFO_TOKEN_READONLY 9
1195 #define NUM_LEVELINFO_TOKENS 10
1197 static LevelDirTree ldi;
1199 static struct TokenInfo levelinfo_tokens[] =
1201 /* level directory info */
1202 { TYPE_STRING, &ldi.name, "name" },
1203 { TYPE_STRING, &ldi.name_short, "name_short" },
1204 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1205 { TYPE_STRING, &ldi.author, "author" },
1206 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1207 { TYPE_INTEGER, &ldi.levels, "levels" },
1208 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1209 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1210 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1211 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1214 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1218 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1219 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1220 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1221 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1224 ldi->node_parent = NULL;
1225 ldi->node_group = NULL;
1229 ldi->cl_cursor = -1;
1231 ldi->filename = NULL;
1232 ldi->fullpath = NULL;
1233 ldi->basepath = NULL;
1234 ldi->name = getStringCopy(ANONYMOUS_NAME);
1235 ldi->name_short = NULL;
1236 ldi->name_sorting = NULL;
1237 ldi->author = getStringCopy(ANONYMOUS_NAME);
1239 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1240 ldi->parent_link = FALSE;
1241 ldi->user_defined = FALSE;
1243 ldi->class_desc = NULL;
1245 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1247 ldi->imported_from = NULL;
1249 ldi->first_level = 0;
1250 ldi->last_level = 0;
1251 ldi->level_group = FALSE;
1252 ldi->handicap_level = 0;
1253 ldi->readonly = TRUE;
1257 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1261 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1263 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1267 /* first copy all values from the parent structure ... */
1270 /* ... then set all fields to default that cannot be inherited from parent.
1271 This is especially important for all those fields that can be set from
1272 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1273 calls 'free()' for all already set token values which requires that no
1274 other structure's pointer may point to them!
1277 ldi->filename = NULL;
1278 ldi->fullpath = NULL;
1279 ldi->basepath = NULL;
1280 ldi->name = getStringCopy(ANONYMOUS_NAME);
1281 ldi->name_short = NULL;
1282 ldi->name_sorting = NULL;
1283 ldi->author = getStringCopy(parent->author);
1284 ldi->imported_from = getStringCopy(parent->imported_from);
1286 ldi->level_group = FALSE;
1287 ldi->parent_link = FALSE;
1289 ldi->node_top = parent->node_top;
1290 ldi->node_parent = parent;
1291 ldi->node_group = NULL;
1295 void setSetupInfo(struct TokenInfo *token_info,
1296 int token_nr, char *token_value)
1298 int token_type = token_info[token_nr].type;
1299 void *setup_value = token_info[token_nr].value;
1301 if (token_value == NULL)
1304 /* set setup field to corresponding token value */
1309 *(boolean *)setup_value = get_string_boolean_value(token_value);
1313 *(Key *)setup_value = getKeyFromKeyName(token_value);
1317 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1321 *(int *)setup_value = get_string_integer_value(token_value);
1325 if (*(char **)setup_value != NULL)
1326 free(*(char **)setup_value);
1327 *(char **)setup_value = getStringCopy(token_value);
1335 static int compareTreeInfoEntries(const void *object1, const void *object2)
1337 const TreeInfo *entry1 = *((TreeInfo **)object1);
1338 const TreeInfo *entry2 = *((TreeInfo **)object2);
1341 if (entry1->parent_link || entry2->parent_link)
1342 compare_result = (entry1->parent_link ? -1 : +1);
1343 else if (entry1->sort_priority == entry2->sort_priority)
1345 char *name1 = getStringToLower(entry1->name_sorting);
1346 char *name2 = getStringToLower(entry2->name_sorting);
1348 compare_result = strcmp(name1, name2);
1353 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1354 compare_result = entry1->sort_priority - entry2->sort_priority;
1356 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1358 return compare_result;
1361 static void createParentTreeInfoNode(TreeInfo *node_parent)
1365 if (node_parent == NULL)
1368 ti_new = newTreeInfo();
1369 setTreeInfoToDefaults(ti_new, node_parent->type);
1371 ti_new->node_parent = node_parent;
1372 ti_new->parent_link = TRUE;
1374 ti_new->name = ".. (parent directory)";
1375 ti_new->name_short = getStringCopy(ti_new->name);
1376 ti_new->name_sorting = getStringCopy(ti_new->name);
1378 ti_new->filename = "..";
1379 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1381 ti_new->sort_priority = node_parent->sort_priority;
1382 ti_new->class_desc = getLevelClassDescription(ti_new);
1384 pushTreeInfo(&node_parent->node_group, ti_new);
1387 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1388 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1390 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1391 TreeInfo *node_parent,
1392 char *level_directory,
1393 char *directory_name)
1395 char *directory_path = getPath2(level_directory, directory_name);
1396 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1397 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1398 LevelDirTree *leveldir_new = NULL;
1401 if (setup_file_list == NULL)
1403 Error(ERR_WARN, "ignoring level directory '%s'", level_directory);
1405 free(directory_path);
1411 leveldir_new = newTreeInfo();
1414 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1416 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1418 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1420 /* set all structure fields according to the token/value pairs */
1421 ldi = *leveldir_new;
1422 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1423 setSetupInfo(levelinfo_tokens, i,
1424 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1425 *leveldir_new = ldi;
1427 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1429 if (leveldir_new->name_short == NULL)
1430 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1432 if (leveldir_new->name_sorting == NULL)
1433 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1435 leveldir_new->filename = getStringCopy(directory_name);
1437 if (node_parent == NULL) /* top level group */
1439 leveldir_new->basepath = level_directory;
1440 leveldir_new->fullpath = leveldir_new->filename;
1442 else /* sub level group */
1444 leveldir_new->basepath = node_parent->basepath;
1445 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1448 if (leveldir_new->levels < 1)
1449 leveldir_new->levels = 1;
1451 leveldir_new->last_level =
1452 leveldir_new->first_level + leveldir_new->levels - 1;
1454 leveldir_new->user_defined =
1455 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1457 leveldir_new->color = LEVELCOLOR(leveldir_new);
1458 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1460 leveldir_new->handicap_level = /* set handicap to default value */
1461 (leveldir_new->user_defined ?
1462 leveldir_new->last_level :
1463 leveldir_new->first_level);
1465 pushTreeInfo(node_first, leveldir_new);
1467 freeSetupFileList(setup_file_list);
1469 if (leveldir_new->level_group)
1471 /* create node to link back to current level directory */
1472 createParentTreeInfoNode(leveldir_new);
1474 /* step into sub-directory and look for more level series */
1475 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1476 leveldir_new, directory_path);
1479 free(directory_path);
1485 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1486 TreeInfo *node_parent,
1487 char *level_directory)
1490 struct dirent *dir_entry;
1491 boolean valid_entry_found = FALSE;
1493 if ((dir = opendir(level_directory)) == NULL)
1495 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1499 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1501 struct stat file_status;
1502 char *directory_name = dir_entry->d_name;
1503 char *directory_path = getPath2(level_directory, directory_name);
1505 /* skip entries for current and parent directory */
1506 if (strcmp(directory_name, ".") == 0 ||
1507 strcmp(directory_name, "..") == 0)
1509 free(directory_path);
1513 /* find out if directory entry is itself a directory */
1514 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1515 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1517 free(directory_path);
1521 free(directory_path);
1523 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1530 if (!valid_entry_found)
1532 /* check if this directory directly contains a file "levelinfo.conf" */
1533 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1534 level_directory, ".");
1537 if (!valid_entry_found)
1538 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1542 void LoadLevelInfo()
1544 InitUserLevelDirectory(getLoginName());
1546 DrawInitText("Loading level series:", 120, FC_GREEN);
1548 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1549 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1551 /* before sorting, the first entries will be from the user directory */
1552 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1554 if (leveldir_first == NULL)
1555 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1557 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1560 dumpTreeInfo(leveldir_first, 0);
1564 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1565 TreeInfo *node_parent,
1566 char *base_directory,
1567 char *directory_name, int type)
1569 char *directory_path = getPath2(base_directory, directory_name);
1571 getPath2(directory_path,
1572 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1573 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1574 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1575 struct SetupFileList *setup_file_list = NULL;
1576 TreeInfo *artwork_new = NULL;
1577 char *check_dir = NULL;
1580 if (access(filename, F_OK) == 0) /* file exists */
1581 loadSetupFileList(filename);
1583 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1586 struct dirent *dir_entry;
1587 boolean valid_file_found = FALSE;
1589 if ((dir = opendir(directory_path)) != NULL)
1591 while ((dir_entry = readdir(dir)) != NULL)
1593 char *entry_name = dir_entry->d_name;
1595 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1596 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1597 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1599 valid_file_found = TRUE;
1607 if (!valid_file_found)
1610 Error(ERR_WARN, "ignoring artwork directory '%s'", base_directory);
1612 free(directory_path);
1619 artwork_new = newTreeInfo();
1622 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1624 setTreeInfoToDefaults(artwork_new, type);
1626 artwork_new->filename = getStringCopy(directory_name);
1628 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1631 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1634 /* set all structure fields according to the token/value pairs */
1636 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1637 setSetupInfo(levelinfo_tokens, i,
1638 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1641 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1643 if (artwork_new->name_short == NULL)
1644 artwork_new->name_short = getStringCopy(artwork_new->name);
1646 if (artwork_new->name_sorting == NULL)
1647 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1650 if (node_parent == NULL) /* top level group */
1652 artwork_new->basepath = getStringCopy(base_directory);
1653 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1655 else /* sub level group */
1657 artwork_new->basepath = getStringCopy(node_parent->basepath);
1658 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1661 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1662 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1663 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1664 artwork_new->user_defined =
1665 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1667 /* (may use ".sort_priority" from "setup_file_list" above) */
1668 artwork_new->color = LEVELCOLOR(artwork_new);
1669 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1671 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1673 if (artwork_new->name != NULL)
1674 free(artwork_new->name);
1676 if (strcmp(artwork_new->filename, ".") == 0)
1678 if (artwork_new->user_defined)
1679 artwork_new->name = getStringCopy("private");
1681 artwork_new->name = getStringCopy("default");
1684 artwork_new->name = getStringCopy(artwork_new->filename);
1686 artwork_new->name_short = getStringCopy(artwork_new->name);
1687 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1690 pushTreeInfo(node_first, artwork_new);
1692 freeSetupFileList(setup_file_list);
1694 free(directory_path);
1700 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1701 TreeInfo *node_parent,
1702 char *base_directory, int type)
1705 struct dirent *dir_entry;
1706 boolean valid_entry_found = FALSE;
1708 if ((dir = opendir(base_directory)) == NULL)
1710 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1711 base_directory == options.graphics_directory) ||
1712 (type == TREE_TYPE_SOUNDS_DIR &&
1713 base_directory == options.sounds_directory) ||
1714 (type == TREE_TYPE_MUSIC_DIR &&
1715 base_directory == options.music_directory))
1716 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1720 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1722 struct stat file_status;
1723 char *directory_name = dir_entry->d_name;
1724 char *directory_path = getPath2(base_directory, directory_name);
1726 /* skip entries for current and parent directory */
1727 if (strcmp(directory_name, ".") == 0 ||
1728 strcmp(directory_name, "..") == 0)
1730 free(directory_path);
1734 /* find out if directory entry is itself a directory */
1735 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1736 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1738 free(directory_path);
1742 free(directory_path);
1744 /* check if this directory contains artwork with or without config file */
1745 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1747 directory_name, type);
1752 /* check if this directory directly contains artwork itself */
1753 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1754 base_directory, ".",
1756 if (!valid_entry_found)
1757 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1761 void LoadArtworkInfo()
1763 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1765 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1766 options.graphics_directory,
1767 TREE_TYPE_GRAPHICS_DIR);
1768 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1769 getUserGraphicsDir(),
1770 TREE_TYPE_GRAPHICS_DIR);
1772 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1773 options.sounds_directory,
1774 TREE_TYPE_SOUNDS_DIR);
1775 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1777 TREE_TYPE_SOUNDS_DIR);
1779 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1780 options.music_directory,
1781 TREE_TYPE_MUSIC_DIR);
1782 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1784 TREE_TYPE_MUSIC_DIR);
1786 /* before sorting, the first entries will be from the user directory */
1787 artwork.gfx_current =
1788 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1789 if (artwork.gfx_current == NULL)
1790 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1792 artwork.snd_current =
1793 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1794 if (artwork.snd_current == NULL)
1795 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1797 artwork.mus_current =
1798 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1799 if (artwork.mus_current == NULL)
1800 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1802 artwork.graphics_set_current = artwork.gfx_current->name;
1803 artwork.sounds_set_current = artwork.snd_current->name;
1804 artwork.music_set_current = artwork.mus_current->name;
1806 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1807 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1808 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1811 dumpTreeInfo(artwork.gfx_first, 0);
1812 dumpTreeInfo(artwork.snd_first, 0);
1813 dumpTreeInfo(artwork.mus_first, 0);
1817 static void SaveUserLevelInfo()
1823 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1825 if (!(file = fopen(filename, MODE_WRITE)))
1827 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1832 /* always start with reliable default values */
1833 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1835 ldi.name = getLoginName();
1836 ldi.author = getRealName();
1838 ldi.first_level = 1;
1839 ldi.sort_priority = LEVELCLASS_USER_START;
1840 ldi.readonly = FALSE;
1842 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1843 getCookie("LEVELINFO")));
1845 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1846 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1847 i != LEVELINFO_TOKEN_NAME_SORTING &&
1848 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1849 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1854 SetFilePermissions(filename, PERMS_PRIVATE);
1857 char *getSetupValue(int type, void *value)
1859 static char value_string[MAX_LINE_LEN];
1864 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1868 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1872 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1876 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1880 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1884 sprintf(value_string, "%d", *(int *)value);
1888 strcpy(value_string, *(char **)value);
1892 value_string[0] = '\0';
1896 return value_string;
1899 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
1902 static char entry[MAX_LINE_LEN];
1903 int token_type = token_info[token_nr].type;
1904 void *setup_value = token_info[token_nr].value;
1905 char *token_text = token_info[token_nr].text;
1906 char *value_string = getSetupValue(token_type, setup_value);
1908 /* start with the prefix, token and some spaces to format output line */
1909 sprintf(entry, "%s%s:", prefix, token_text);
1910 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1913 /* continue with the token's value (which can have different types) */
1914 strcat(entry, value_string);
1916 if (token_type == TYPE_KEY_X11)
1918 Key key = *(Key *)setup_value;
1919 char *keyname = getKeyNameFromKey(key);
1921 /* add comment, if useful */
1922 if (strcmp(keyname, "(undefined)") != 0 &&
1923 strcmp(keyname, "(unknown)") != 0)
1925 for (i=strlen(entry); i<50; i++)
1928 strcat(entry, "# ");
1929 strcat(entry, keyname);
1936 void LoadLevelSetup_LastSeries()
1939 struct SetupFileList *level_setup_list = NULL;
1941 /* always start with reliable default values */
1942 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1944 /* ----------------------------------------------------------------------- */
1945 /* ~/.<program>/levelsetup.conf */
1946 /* ----------------------------------------------------------------------- */
1948 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1950 if ((level_setup_list = loadSetupFileList(filename)))
1952 char *last_level_series =
1953 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
1955 leveldir_current = getTreeInfoFromFilename(leveldir_first,
1957 if (leveldir_current == NULL)
1958 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1960 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
1962 freeSetupFileList(level_setup_list);
1965 Error(ERR_WARN, "using default setup values");
1970 void SaveLevelSetup_LastSeries()
1973 char *level_subdir = leveldir_current->filename;
1976 /* ----------------------------------------------------------------------- */
1977 /* ~/.<program>/levelsetup.conf */
1978 /* ----------------------------------------------------------------------- */
1980 InitUserDataDirectory();
1982 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
1984 if (!(file = fopen(filename, MODE_WRITE)))
1986 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1991 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1992 getCookie("LEVELSETUP")));
1993 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
1999 SetFilePermissions(filename, PERMS_PRIVATE);
2002 static void checkSeriesInfo()
2004 static char *level_directory = NULL;
2006 struct dirent *dir_entry;
2008 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2010 level_directory = getPath2((leveldir_current->user_defined ?
2011 getUserLevelDir(NULL) :
2012 options.level_directory),
2013 leveldir_current->fullpath);
2015 if ((dir = opendir(level_directory)) == NULL)
2017 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2021 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2023 if (strlen(dir_entry->d_name) > 4 &&
2024 dir_entry->d_name[3] == '.' &&
2025 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2027 char levelnum_str[4];
2030 strncpy(levelnum_str, dir_entry->d_name, 3);
2031 levelnum_str[3] = '\0';
2033 levelnum_value = atoi(levelnum_str);
2035 if (levelnum_value < leveldir_current->first_level)
2037 Error(ERR_WARN, "additional level %d found", levelnum_value);
2038 leveldir_current->first_level = levelnum_value;
2040 else if (levelnum_value > leveldir_current->last_level)
2042 Error(ERR_WARN, "additional level %d found", levelnum_value);
2043 leveldir_current->last_level = levelnum_value;
2051 void LoadLevelSetup_SeriesInfo()
2054 struct SetupFileList *level_setup_list = NULL;
2055 char *level_subdir = leveldir_current->filename;
2057 /* always start with reliable default values */
2058 level_nr = leveldir_current->first_level;
2060 checkSeriesInfo(leveldir_current);
2062 /* ----------------------------------------------------------------------- */
2063 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2064 /* ----------------------------------------------------------------------- */
2066 level_subdir = leveldir_current->filename;
2068 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2070 if ((level_setup_list = loadSetupFileList(filename)))
2074 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2078 level_nr = atoi(token_value);
2080 if (level_nr < leveldir_current->first_level)
2081 level_nr = leveldir_current->first_level;
2082 if (level_nr > leveldir_current->last_level)
2083 level_nr = leveldir_current->last_level;
2086 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2090 int level_nr = atoi(token_value);
2092 if (level_nr < leveldir_current->first_level)
2093 level_nr = leveldir_current->first_level;
2094 if (level_nr > leveldir_current->last_level + 1)
2095 level_nr = leveldir_current->last_level;
2097 if (leveldir_current->user_defined)
2098 level_nr = leveldir_current->last_level;
2100 leveldir_current->handicap_level = level_nr;
2103 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2105 freeSetupFileList(level_setup_list);
2108 Error(ERR_WARN, "using default setup values");
2113 void SaveLevelSetup_SeriesInfo()
2116 char *level_subdir = leveldir_current->filename;
2117 char *level_nr_str = int2str(level_nr, 0);
2118 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2121 /* ----------------------------------------------------------------------- */
2122 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2123 /* ----------------------------------------------------------------------- */
2125 InitLevelSetupDirectory(level_subdir);
2127 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2129 if (!(file = fopen(filename, MODE_WRITE)))
2131 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2136 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2137 getCookie("LEVELSETUP")));
2138 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2140 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2141 handicap_level_str));
2146 SetFilePermissions(filename, PERMS_PRIVATE);