1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
24 /* file names and filename extensions */
25 #if !defined(PLATFORM_MSDOS)
26 #define LEVELSETUP_DIRECTORY "levelsetup"
27 #define SETUP_FILENAME "setup.conf"
28 #define LEVELSETUP_FILENAME "levelsetup.conf"
29 #define LEVELINFO_FILENAME "levelinfo.conf"
30 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
31 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
32 #define MUSICINFO_FILENAME "musicinfo.conf"
33 #define LEVELFILE_EXTENSION "level"
34 #define TAPEFILE_EXTENSION "tape"
35 #define SCOREFILE_EXTENSION "score"
37 #define LEVELSETUP_DIRECTORY "lvlsetup"
38 #define SETUP_FILENAME "setup.cnf"
39 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
40 #define LEVELINFO_FILENAME "lvlinfo.cnf"
41 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
42 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
43 #define MUSICINFO_FILENAME "musinfo.cnf"
44 #define LEVELFILE_EXTENSION "lvl"
45 #define TAPEFILE_EXTENSION "tap"
46 #define SCOREFILE_EXTENSION "sco"
49 #define NUM_LEVELCLASS_DESC 8
50 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
62 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
63 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
64 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
65 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
66 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
67 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
68 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
69 IS_LEVELCLASS_USER(n) ? FC_RED : \
72 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
73 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
74 IS_LEVELCLASS_BD(n) ? 2 : \
75 IS_LEVELCLASS_EM(n) ? 3 : \
76 IS_LEVELCLASS_SP(n) ? 4 : \
77 IS_LEVELCLASS_DX(n) ? 5 : \
78 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
79 IS_LEVELCLASS_USER(n) ? 7 : \
82 #define TOKEN_VALUE_POSITION 40
83 #define TOKEN_COMMENT_POSITION 60
85 #define MAX_COOKIE_LEN 256
88 /* ------------------------------------------------------------------------- */
90 /* ------------------------------------------------------------------------- */
92 static char *getLevelClassDescription(TreeInfo *ldi)
94 int position = ldi->sort_priority / 100;
96 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
97 return levelclass_desc[position];
99 return "Unknown Level Class";
102 static char *getUserLevelDir(char *level_subdir)
104 static char *userlevel_dir = NULL;
105 char *data_dir = getUserDataDir();
106 char *userlevel_subdir = LEVELS_DIRECTORY;
111 if (level_subdir != NULL)
112 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
114 userlevel_dir = getPath2(data_dir, userlevel_subdir);
116 return userlevel_dir;
119 static char *getTapeDir(char *level_subdir)
121 static char *tape_dir = NULL;
122 char *data_dir = getUserDataDir();
123 char *tape_subdir = TAPES_DIRECTORY;
128 if (level_subdir != NULL)
129 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
131 tape_dir = getPath2(data_dir, tape_subdir);
136 static char *getScoreDir(char *level_subdir)
138 static char *score_dir = NULL;
139 char *data_dir = options.rw_base_directory;
140 char *score_subdir = SCORES_DIRECTORY;
145 if (level_subdir != NULL)
146 score_dir = getPath3(data_dir, score_subdir, level_subdir);
148 score_dir = getPath2(data_dir, score_subdir);
153 static char *getLevelSetupDir(char *level_subdir)
155 static char *levelsetup_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
160 free(levelsetup_dir);
162 if (level_subdir != NULL)
163 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
165 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
167 return levelsetup_dir;
170 static char *getCurrentLevelDir()
172 static char *level_dir = NULL;
177 if (leveldir_current == NULL)
178 return options.level_directory;
180 level_dir = getPath2((leveldir_current->user_defined ?
181 getUserLevelDir(NULL) : options.level_directory),
182 leveldir_current->fullpath);
187 static char *getDefaultGraphicsDir(char *graphics_subdir)
189 static char *graphics_dir = NULL;
191 if (graphics_subdir == NULL)
192 return options.graphics_directory;
197 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
202 static char *getDefaultSoundsDir(char *sounds_subdir)
204 static char *sounds_dir = NULL;
206 if (sounds_subdir == NULL)
207 return options.sounds_directory;
212 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
217 static char *getDefaultMusicDir(char *music_subdir)
219 static char *music_dir = NULL;
221 if (music_subdir == NULL)
222 return options.music_directory;
227 music_dir = getPath2(options.music_directory, music_subdir);
232 static char *getUserGraphicsDir()
234 static char *usergraphics_dir = NULL;
236 if (usergraphics_dir == NULL)
237 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
239 return usergraphics_dir;
242 static char *getUserSoundsDir()
244 static char *usersounds_dir = NULL;
246 if (usersounds_dir == NULL)
247 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
249 return usersounds_dir;
252 static char *getUserMusicDir()
254 static char *usermusic_dir = NULL;
256 if (usermusic_dir == NULL)
257 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
259 return usermusic_dir;
262 char *getLevelFilename(int nr)
264 static char *filename = NULL;
265 char basename[MAX_FILENAME_LEN];
267 if (filename != NULL)
270 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
271 filename = getPath2(getCurrentLevelDir(), basename);
276 char *getTapeFilename(int nr)
278 static char *filename = NULL;
279 char basename[MAX_FILENAME_LEN];
281 if (filename != NULL)
284 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
285 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
290 char *getScoreFilename(int nr)
292 static char *filename = NULL;
293 char basename[MAX_FILENAME_LEN];
295 if (filename != NULL)
298 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
299 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
304 char *getSetupFilename()
306 static char *filename = NULL;
308 if (filename != NULL)
311 filename = getPath2(getSetupDir(), SETUP_FILENAME);
316 static char *getSetupArtworkDir(TreeInfo *ti)
318 static char *artwork_dir = NULL;
320 if (artwork_dir != NULL)
323 artwork_dir = getPath2(ti->basepath, ti->fullpath);
328 static char *getCorrectedImageBasename(char *basename)
330 char *result = basename;
332 #if defined(PLATFORM_MSDOS)
333 if (program.filename_prefix != NULL)
335 int prefix_len = strlen(program.filename_prefix);
337 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
338 result = &basename[prefix_len];
345 static boolean fileExists(char *filename)
348 printf("checking file '%s'\n", filename);
351 return (access(filename, F_OK) == 0);
354 char *getCustomImageFilename(char *basename)
356 static char *filename = NULL;
358 if (filename != NULL)
361 basename = getCorrectedImageBasename(basename);
363 /* 1st try: look for special artwork in current level series directory */
364 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
365 if (fileExists(filename))
368 /* 2nd try: look for special artwork in private artwork directory */
369 filename = getPath2(getUserGraphicsDir(), basename);
370 if (fileExists(filename))
373 /* 3rd try: look for special artwork in configured artwork directory */
374 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
375 if (fileExists(filename))
378 /* 4th try: look for default artwork in new default artwork directory */
379 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
380 if (fileExists(filename))
383 /* 5th try: look for default artwork in old default artwork directory */
384 filename = getPath2(options.graphics_directory, basename);
385 if (fileExists(filename))
388 return NULL; /* cannot find image file */
391 char *getCustomSoundFilename(char *basename)
393 static char *filename = NULL;
395 if (filename != NULL)
399 /* 1st try: look for special artwork in current level series directory */
400 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
401 if (fileExists(filename))
406 /* 2nd try: look for special artwork in private artwork directory */
407 filename = getPath2(getUserSoundsDir(), basename);
408 if (fileExists(filename))
412 /* 3rd try: look for special artwork in configured artwork directory */
413 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
414 if (fileExists(filename))
417 /* 4th try: look for default artwork in new default artwork directory */
418 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
419 if (fileExists(filename))
422 /* 5th try: look for default artwork in old default artwork directory */
423 filename = getPath2(options.sounds_directory, basename);
424 if (fileExists(filename))
427 return NULL; /* cannot find image file */
430 char *getCustomSoundConfigFilename()
432 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
435 char *getCustomMusicDirectory(void)
437 static char *directory = NULL;
439 if (directory != NULL)
442 /* 1st try: look for special artwork in current level series directory */
443 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
444 if (fileExists(directory))
447 /* 2nd try: look for special artwork in private artwork directory */
448 directory = getStringCopy(getUserMusicDir());
449 if (fileExists(directory))
452 /* 3rd try: look for special artwork in configured artwork directory */
453 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
454 if (fileExists(directory))
457 /* 4th try: look for default artwork in new default artwork directory */
458 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
459 if (fileExists(directory))
462 /* 5th try: look for default artwork in old default artwork directory */
463 directory = getStringCopy(options.music_directory);
464 if (fileExists(directory))
467 return NULL; /* cannot find image file */
470 void InitTapeDirectory(char *level_subdir)
472 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
473 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
474 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
477 void InitScoreDirectory(char *level_subdir)
479 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
480 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
483 static void SaveUserLevelInfo();
485 void InitUserLevelDirectory(char *level_subdir)
487 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
489 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
490 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
491 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
497 void InitLevelSetupDirectory(char *level_subdir)
499 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
500 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
501 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
504 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
506 int file_version_major, file_version_minor, file_version_patch;
507 int game_version_major, game_version_minor, game_version_patch;
509 file_version_major = fgetc(file);
510 file_version_minor = fgetc(file);
511 file_version_patch = fgetc(file);
512 fgetc(file); /* not used */
514 game_version_major = fgetc(file);
515 game_version_minor = fgetc(file);
516 game_version_patch = fgetc(file);
517 fgetc(file); /* not used */
519 *file_version = VERSION_IDENT(file_version_major,
523 *game_version = VERSION_IDENT(game_version_major,
528 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
530 int file_version_major = VERSION_MAJOR(file_version);
531 int file_version_minor = VERSION_MINOR(file_version);
532 int file_version_patch = VERSION_PATCH(file_version);
533 int game_version_major = VERSION_MAJOR(game_version);
534 int game_version_minor = VERSION_MINOR(game_version);
535 int game_version_patch = VERSION_PATCH(game_version);
537 fputc(file_version_major, file);
538 fputc(file_version_minor, file);
539 fputc(file_version_patch, file);
540 fputc(0, file); /* not used */
542 fputc(game_version_major, file);
543 fputc(game_version_minor, file);
544 fputc(game_version_patch, file);
545 fputc(0, file); /* not used */
549 /* ------------------------------------------------------------------------- */
550 /* some functions to handle lists of level directories */
551 /* ------------------------------------------------------------------------- */
553 TreeInfo *newTreeInfo()
555 return checked_calloc(sizeof(TreeInfo));
558 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
560 node_new->next = *node_first;
561 *node_first = node_new;
564 int numTreeInfo(TreeInfo *node)
577 boolean validLevelSeries(TreeInfo *node)
579 return (node != NULL && !node->node_group && !node->parent_link);
582 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
586 if (node->node_top) /* start with first tree entry */
587 return getFirstValidTreeInfoEntry(*node->node_top);
591 else if (node->node_group) /* enter level group (step down into tree) */
592 return getFirstValidTreeInfoEntry(node->node_group);
593 else if (node->parent_link) /* skip start entry of level group */
595 if (node->next) /* get first real level series entry */
596 return getFirstValidTreeInfoEntry(node->next);
597 else /* leave empty level group and go on */
598 return getFirstValidTreeInfoEntry(node->node_parent->next);
600 else /* this seems to be a regular level series */
604 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
609 if (node->node_parent == NULL) /* top level group */
610 return *node->node_top;
611 else /* sub level group */
612 return node->node_parent->node_group;
615 int numTreeInfoInGroup(TreeInfo *node)
617 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
620 int posTreeInfo(TreeInfo *node)
622 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
627 if (node_cmp == node)
631 node_cmp = node_cmp->next;
637 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
639 TreeInfo *node_default = node;
654 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
656 if (filename == NULL)
661 if (node->node_group)
663 TreeInfo *node_group;
665 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
670 else if (!node->parent_link)
672 if (strcmp(filename, node->filename) == 0)
682 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
684 return getTreeInfoFromFilenameExt(ti, filename);
687 void dumpTreeInfo(TreeInfo *node, int depth)
691 printf("Dumping TreeInfo:\n");
695 for (i=0; i<(depth + 1) * 3; i++)
698 printf("filename == '%s' (%s) [%s]\n",
699 node->filename, node->name, node->name_short);
701 if (node->node_group != NULL)
702 dumpTreeInfo(node->node_group, depth + 1);
708 void sortTreeInfo(TreeInfo **node_first,
709 int (*compare_function)(const void *, const void *))
711 int num_nodes = numTreeInfo(*node_first);
712 TreeInfo **sort_array;
713 TreeInfo *node = *node_first;
719 /* allocate array for sorting structure pointers */
720 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
722 /* writing structure pointers to sorting array */
723 while (i < num_nodes && node) /* double boundary check... */
725 sort_array[i] = node;
731 /* sorting the structure pointers in the sorting array */
732 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
735 /* update the linkage of list elements with the sorted node array */
736 for (i=0; i<num_nodes - 1; i++)
737 sort_array[i]->next = sort_array[i + 1];
738 sort_array[num_nodes - 1]->next = NULL;
740 /* update the linkage of the main list anchor pointer */
741 *node_first = sort_array[0];
745 /* now recursively sort the level group structures */
749 if (node->node_group != NULL)
750 sortTreeInfo(&node->node_group, compare_function);
757 /* ========================================================================= */
758 /* some stuff from "files.c" */
759 /* ========================================================================= */
761 #if defined(PLATFORM_WIN32)
763 #define S_IRGRP S_IRUSR
766 #define S_IROTH S_IRUSR
769 #define S_IWGRP S_IWUSR
772 #define S_IWOTH S_IWUSR
775 #define S_IXGRP S_IXUSR
778 #define S_IXOTH S_IXUSR
781 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
786 #endif /* PLATFORM_WIN32 */
788 /* file permissions for newly written files */
789 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
790 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
791 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
793 #define MODE_W_PRIVATE (S_IWUSR)
794 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
795 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
797 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
798 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
800 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
801 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
803 char *getUserDataDir(void)
805 static char *userdata_dir = NULL;
809 char *home_dir = getHomeDir();
810 char *data_dir = program.userdata_directory;
812 userdata_dir = getPath2(home_dir, data_dir);
820 return getUserDataDir();
823 static mode_t posix_umask(mode_t mask)
825 #if defined(PLATFORM_UNIX)
832 static int posix_mkdir(const char *pathname, mode_t mode)
834 #if defined(PLATFORM_WIN32)
835 return mkdir(pathname);
837 return mkdir(pathname, mode);
841 void createDirectory(char *dir, char *text, int permission_class)
843 /* leave "other" permissions in umask untouched, but ensure group parts
844 of USERDATA_DIR_MODE are not masked */
845 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
846 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
847 mode_t normal_umask = posix_umask(0);
848 mode_t group_umask = ~(dir_mode & S_IRWXG);
849 posix_umask(normal_umask & group_umask);
851 if (access(dir, F_OK) != 0)
852 if (posix_mkdir(dir, dir_mode) != 0)
853 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
855 posix_umask(normal_umask); /* reset normal umask */
858 void InitUserDataDirectory()
860 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
863 void SetFilePermissions(char *filename, int permission_class)
865 chmod(filename, (permission_class == PERMS_PRIVATE ?
866 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
869 char *getCookie(char *file_type)
871 static char cookie[MAX_COOKIE_LEN + 1];
873 if (strlen(program.cookie_prefix) + 1 +
874 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
875 return "[COOKIE ERROR]"; /* should never happen */
877 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
878 program.cookie_prefix, file_type,
879 program.version_major, program.version_minor);
884 int getFileVersionFromCookieString(const char *cookie)
886 const char *ptr_cookie1, *ptr_cookie2;
887 const char *pattern1 = "_FILE_VERSION_";
888 const char *pattern2 = "?.?";
889 const int len_cookie = strlen(cookie);
890 const int len_pattern1 = strlen(pattern1);
891 const int len_pattern2 = strlen(pattern2);
892 const int len_pattern = len_pattern1 + len_pattern2;
893 int version_major, version_minor;
895 if (len_cookie <= len_pattern)
898 ptr_cookie1 = &cookie[len_cookie - len_pattern];
899 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
901 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
904 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
905 ptr_cookie2[1] != '.' ||
906 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
909 version_major = ptr_cookie2[0] - '0';
910 version_minor = ptr_cookie2[2] - '0';
912 return VERSION_IDENT(version_major, version_minor, 0);
915 boolean checkCookieString(const char *cookie, const char *template)
917 const char *pattern = "_FILE_VERSION_?.?";
918 const int len_cookie = strlen(cookie);
919 const int len_template = strlen(template);
920 const int len_pattern = strlen(pattern);
922 if (len_cookie != len_template)
925 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
931 /* ------------------------------------------------------------------------- */
932 /* setup file list handling functions */
933 /* ------------------------------------------------------------------------- */
935 int get_string_integer_value(char *s)
937 static char *number_text[][3] =
939 { "0", "zero", "null", },
940 { "1", "one", "first" },
941 { "2", "two", "second" },
942 { "3", "three", "third" },
943 { "4", "four", "fourth" },
944 { "5", "five", "fifth" },
945 { "6", "six", "sixth" },
946 { "7", "seven", "seventh" },
947 { "8", "eight", "eighth" },
948 { "9", "nine", "ninth" },
949 { "10", "ten", "tenth" },
950 { "11", "eleven", "eleventh" },
951 { "12", "twelve", "twelfth" },
955 char *s_lower = getStringToLower(s);
960 if (strcmp(s_lower, number_text[i][j]) == 0)
971 boolean get_string_boolean_value(char *s)
973 char *s_lower = getStringToLower(s);
974 boolean result = FALSE;
976 if (strcmp(s_lower, "true") == 0 ||
977 strcmp(s_lower, "yes") == 0 ||
978 strcmp(s_lower, "on") == 0 ||
979 get_string_integer_value(s) == 1)
987 char *getFormattedSetupEntry(char *token, char *value)
990 static char entry[MAX_LINE_LEN];
992 /* start with the token and some spaces to format output line */
993 sprintf(entry, "%s:", token);
994 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
997 /* continue with the token's value */
998 strcat(entry, value);
1003 void freeSetupFileList(struct SetupFileList *setup_file_list)
1005 if (!setup_file_list)
1008 if (setup_file_list->token)
1009 free(setup_file_list->token);
1010 if (setup_file_list->value)
1011 free(setup_file_list->value);
1012 if (setup_file_list->next)
1013 freeSetupFileList(setup_file_list->next);
1014 free(setup_file_list);
1017 static struct SetupFileList *newSetupFileList(char *token, char *value)
1019 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1021 new->token = checked_malloc(strlen(token) + 1);
1022 strcpy(new->token, token);
1024 new->value = checked_malloc(strlen(value) + 1);
1025 strcpy(new->value, value);
1032 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1034 if (!setup_file_list)
1037 if (strcmp(setup_file_list->token, token) == 0)
1038 return setup_file_list->value;
1040 return getTokenValue(setup_file_list->next, token);
1043 static void setTokenValue(struct SetupFileList *setup_file_list,
1044 char *token, char *value)
1046 if (!setup_file_list)
1049 if (strcmp(setup_file_list->token, token) == 0)
1051 free(setup_file_list->value);
1052 setup_file_list->value = checked_malloc(strlen(value) + 1);
1053 strcpy(setup_file_list->value, value);
1055 else if (setup_file_list->next == NULL)
1056 setup_file_list->next = newSetupFileList(token, value);
1058 setTokenValue(setup_file_list->next, token, value);
1062 static void printSetupFileList(struct SetupFileList *setup_file_list)
1064 if (!setup_file_list)
1067 printf("token: '%s'\n", setup_file_list->token);
1068 printf("value: '%s'\n", setup_file_list->value);
1070 printSetupFileList(setup_file_list->next);
1074 struct SetupFileList *loadSetupFileList(char *filename)
1077 char line[MAX_LINE_LEN];
1078 char *token, *value, *line_ptr;
1079 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1080 struct SetupFileList *first_valid_list_entry;
1084 if (!(file = fopen(filename, MODE_READ)))
1086 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1092 /* read next line of input file */
1093 if (!fgets(line, MAX_LINE_LEN, file))
1096 /* cut trailing comment or whitespace from input line */
1097 for (line_ptr = line; *line_ptr; line_ptr++)
1099 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1106 /* cut trailing whitespaces from input line */
1107 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1108 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1111 /* ignore empty lines */
1115 line_len = strlen(line);
1117 /* cut leading whitespaces from token */
1118 for (token = line; *token; token++)
1119 if (*token != ' ' && *token != '\t')
1122 /* find end of token */
1123 for (line_ptr = token; *line_ptr; line_ptr++)
1125 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1132 if (line_ptr < line + line_len)
1133 value = line_ptr + 1;
1137 /* cut leading whitespaces from value */
1138 for (; *value; value++)
1139 if (*value != ' ' && *value != '\t')
1142 if (*token && *value)
1143 setTokenValue(setup_file_list, token, value);
1148 first_valid_list_entry = setup_file_list->next;
1150 /* free empty list header */
1151 setup_file_list->next = NULL;
1152 freeSetupFileList(setup_file_list);
1154 if (first_valid_list_entry == NULL)
1155 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1157 return first_valid_list_entry;
1160 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1163 if (!setup_file_list)
1166 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1168 if (!checkCookieString(setup_file_list->value, identifier))
1170 Error(ERR_WARN, "configuration file has wrong file identifier");
1177 if (setup_file_list->next)
1178 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1181 Error(ERR_WARN, "configuration file has no file identifier");
1187 /* ========================================================================= */
1188 /* setup file stuff */
1189 /* ========================================================================= */
1191 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1192 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1193 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1195 /* level directory info */
1196 #define LEVELINFO_TOKEN_NAME 0
1197 #define LEVELINFO_TOKEN_NAME_SHORT 1
1198 #define LEVELINFO_TOKEN_NAME_SORTING 2
1199 #define LEVELINFO_TOKEN_AUTHOR 3
1200 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1201 #define LEVELINFO_TOKEN_LEVELS 5
1202 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1203 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1204 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1205 #define LEVELINFO_TOKEN_READONLY 9
1207 #define NUM_LEVELINFO_TOKENS 10
1209 static LevelDirTree ldi;
1211 static struct TokenInfo levelinfo_tokens[] =
1213 /* level directory info */
1214 { TYPE_STRING, &ldi.name, "name" },
1215 { TYPE_STRING, &ldi.name_short, "name_short" },
1216 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1217 { TYPE_STRING, &ldi.author, "author" },
1218 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1219 { TYPE_INTEGER, &ldi.levels, "levels" },
1220 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1221 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1222 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1223 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1226 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1230 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1231 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1232 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1233 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1236 ldi->node_parent = NULL;
1237 ldi->node_group = NULL;
1241 ldi->cl_cursor = -1;
1243 ldi->filename = NULL;
1244 ldi->fullpath = NULL;
1245 ldi->basepath = NULL;
1246 ldi->name = getStringCopy(ANONYMOUS_NAME);
1247 ldi->name_short = NULL;
1248 ldi->name_sorting = NULL;
1249 ldi->author = getStringCopy(ANONYMOUS_NAME);
1251 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1252 ldi->parent_link = FALSE;
1253 ldi->user_defined = FALSE;
1255 ldi->class_desc = NULL;
1257 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1259 ldi->imported_from = NULL;
1261 ldi->first_level = 0;
1262 ldi->last_level = 0;
1263 ldi->level_group = FALSE;
1264 ldi->handicap_level = 0;
1265 ldi->readonly = TRUE;
1269 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1273 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1275 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1279 /* first copy all values from the parent structure ... */
1282 /* ... then set all fields to default that cannot be inherited from parent.
1283 This is especially important for all those fields that can be set from
1284 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1285 calls 'free()' for all already set token values which requires that no
1286 other structure's pointer may point to them!
1289 ldi->filename = NULL;
1290 ldi->fullpath = NULL;
1291 ldi->basepath = NULL;
1292 ldi->name = getStringCopy(ANONYMOUS_NAME);
1293 ldi->name_short = NULL;
1294 ldi->name_sorting = NULL;
1295 ldi->author = getStringCopy(parent->author);
1296 ldi->imported_from = getStringCopy(parent->imported_from);
1298 ldi->level_group = FALSE;
1299 ldi->parent_link = FALSE;
1301 ldi->node_top = parent->node_top;
1302 ldi->node_parent = parent;
1303 ldi->node_group = NULL;
1307 void setSetupInfo(struct TokenInfo *token_info,
1308 int token_nr, char *token_value)
1310 int token_type = token_info[token_nr].type;
1311 void *setup_value = token_info[token_nr].value;
1313 if (token_value == NULL)
1316 /* set setup field to corresponding token value */
1321 *(boolean *)setup_value = get_string_boolean_value(token_value);
1325 *(Key *)setup_value = getKeyFromKeyName(token_value);
1329 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1333 *(int *)setup_value = get_string_integer_value(token_value);
1337 if (*(char **)setup_value != NULL)
1338 free(*(char **)setup_value);
1339 *(char **)setup_value = getStringCopy(token_value);
1347 static int compareTreeInfoEntries(const void *object1, const void *object2)
1349 const TreeInfo *entry1 = *((TreeInfo **)object1);
1350 const TreeInfo *entry2 = *((TreeInfo **)object2);
1353 if (entry1->parent_link || entry2->parent_link)
1354 compare_result = (entry1->parent_link ? -1 : +1);
1355 else if (entry1->sort_priority == entry2->sort_priority)
1357 char *name1 = getStringToLower(entry1->name_sorting);
1358 char *name2 = getStringToLower(entry2->name_sorting);
1360 compare_result = strcmp(name1, name2);
1365 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1366 compare_result = entry1->sort_priority - entry2->sort_priority;
1368 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1370 return compare_result;
1373 static void createParentTreeInfoNode(TreeInfo *node_parent)
1377 if (node_parent == NULL)
1380 ti_new = newTreeInfo();
1381 setTreeInfoToDefaults(ti_new, node_parent->type);
1383 ti_new->node_parent = node_parent;
1384 ti_new->parent_link = TRUE;
1386 ti_new->name = ".. (parent directory)";
1387 ti_new->name_short = getStringCopy(ti_new->name);
1388 ti_new->name_sorting = getStringCopy(ti_new->name);
1390 ti_new->filename = "..";
1391 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1393 ti_new->sort_priority = node_parent->sort_priority;
1394 ti_new->class_desc = getLevelClassDescription(ti_new);
1396 pushTreeInfo(&node_parent->node_group, ti_new);
1399 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1400 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1402 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1403 TreeInfo *node_parent,
1404 char *level_directory,
1405 char *directory_name)
1407 char *directory_path = getPath2(level_directory, directory_name);
1408 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1409 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1410 LevelDirTree *leveldir_new = NULL;
1413 if (setup_file_list == NULL)
1415 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1417 free(directory_path);
1423 leveldir_new = newTreeInfo();
1426 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1428 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1430 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1432 /* set all structure fields according to the token/value pairs */
1433 ldi = *leveldir_new;
1434 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1435 setSetupInfo(levelinfo_tokens, i,
1436 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1437 *leveldir_new = ldi;
1439 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1441 free(leveldir_new->name);
1442 leveldir_new->name = getStringCopy(leveldir_new->filename);
1445 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1447 if (leveldir_new->name_short == NULL)
1448 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1450 if (leveldir_new->name_sorting == NULL)
1451 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1453 leveldir_new->filename = getStringCopy(directory_name);
1455 if (node_parent == NULL) /* top level group */
1457 leveldir_new->basepath = level_directory;
1458 leveldir_new->fullpath = leveldir_new->filename;
1460 else /* sub level group */
1462 leveldir_new->basepath = node_parent->basepath;
1463 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1466 if (leveldir_new->levels < 1)
1467 leveldir_new->levels = 1;
1469 leveldir_new->last_level =
1470 leveldir_new->first_level + leveldir_new->levels - 1;
1472 leveldir_new->user_defined =
1473 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1475 leveldir_new->color = LEVELCOLOR(leveldir_new);
1476 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1478 leveldir_new->handicap_level = /* set handicap to default value */
1479 (leveldir_new->user_defined ?
1480 leveldir_new->last_level :
1481 leveldir_new->first_level);
1483 pushTreeInfo(node_first, leveldir_new);
1485 freeSetupFileList(setup_file_list);
1487 if (leveldir_new->level_group)
1489 /* create node to link back to current level directory */
1490 createParentTreeInfoNode(leveldir_new);
1492 /* step into sub-directory and look for more level series */
1493 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1494 leveldir_new, directory_path);
1497 free(directory_path);
1503 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1504 TreeInfo *node_parent,
1505 char *level_directory)
1508 struct dirent *dir_entry;
1509 boolean valid_entry_found = FALSE;
1511 if ((dir = opendir(level_directory)) == NULL)
1513 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1517 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1519 struct stat file_status;
1520 char *directory_name = dir_entry->d_name;
1521 char *directory_path = getPath2(level_directory, directory_name);
1523 /* skip entries for current and parent directory */
1524 if (strcmp(directory_name, ".") == 0 ||
1525 strcmp(directory_name, "..") == 0)
1527 free(directory_path);
1531 /* find out if directory entry is itself a directory */
1532 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1533 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1535 free(directory_path);
1539 free(directory_path);
1541 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1542 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1543 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1546 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1553 if (!valid_entry_found)
1555 /* check if this directory directly contains a file "levelinfo.conf" */
1556 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1557 level_directory, ".");
1560 if (!valid_entry_found)
1561 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1565 void LoadLevelInfo()
1567 InitUserLevelDirectory(getLoginName());
1569 DrawInitText("Loading level series:", 120, FC_GREEN);
1571 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1572 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1574 /* before sorting, the first entries will be from the user directory */
1575 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1577 if (leveldir_first == NULL)
1578 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1580 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1583 dumpTreeInfo(leveldir_first, 0);
1587 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1588 TreeInfo *node_parent,
1589 char *base_directory,
1590 char *directory_name, int type)
1592 char *directory_path = getPath2(base_directory, directory_name);
1594 getPath2(directory_path,
1595 (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICSINFO_FILENAME :
1596 type == TREE_TYPE_SOUNDS_DIR ? SOUNDSINFO_FILENAME :
1597 type == TREE_TYPE_MUSIC_DIR ? MUSICINFO_FILENAME : ""));
1598 struct SetupFileList *setup_file_list = NULL;
1599 TreeInfo *artwork_new = NULL;
1600 char *check_dir = NULL;
1603 if (access(filename, F_OK) == 0) /* file exists */
1604 setup_file_list = loadSetupFileList(filename);
1606 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1609 struct dirent *dir_entry;
1610 boolean valid_file_found = FALSE;
1612 if ((dir = opendir(directory_path)) != NULL)
1614 while ((dir_entry = readdir(dir)) != NULL)
1616 char *entry_name = dir_entry->d_name;
1618 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(entry_name)) ||
1619 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(entry_name)) ||
1620 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(entry_name)))
1622 valid_file_found = TRUE;
1630 if (!valid_file_found)
1632 if (strcmp(directory_name, ".") != 0)
1633 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1635 free(directory_path);
1642 artwork_new = newTreeInfo();
1645 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1647 setTreeInfoToDefaults(artwork_new, type);
1649 artwork_new->filename = getStringCopy(directory_name);
1651 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1654 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1657 /* set all structure fields according to the token/value pairs */
1659 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1660 setSetupInfo(levelinfo_tokens, i,
1661 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1664 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1666 free(artwork_new->name);
1667 artwork_new->name = getStringCopy(artwork_new->filename);
1670 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1672 if (artwork_new->name_short == NULL)
1673 artwork_new->name_short = getStringCopy(artwork_new->name);
1675 if (artwork_new->name_sorting == NULL)
1676 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1679 if (node_parent == NULL) /* top level group */
1681 artwork_new->basepath = getStringCopy(base_directory);
1682 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1684 else /* sub level group */
1686 artwork_new->basepath = getStringCopy(node_parent->basepath);
1687 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1690 check_dir = (type == TREE_TYPE_GRAPHICS_DIR ? options.graphics_directory :
1691 type == TREE_TYPE_SOUNDS_DIR ? options.sounds_directory :
1692 type == TREE_TYPE_MUSIC_DIR ? options.music_directory : "");
1693 artwork_new->user_defined =
1694 (artwork_new->basepath == check_dir ? FALSE : TRUE);
1696 /* (may use ".sort_priority" from "setup_file_list" above) */
1697 artwork_new->color = LEVELCOLOR(artwork_new);
1698 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1700 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1702 if (artwork_new->name != NULL)
1703 free(artwork_new->name);
1705 if (strcmp(artwork_new->filename, ".") == 0)
1707 if (artwork_new->user_defined)
1709 artwork_new->name = getStringCopy("private");
1710 artwork_new->sort_priority = LEVELCLASS_USER;
1714 artwork_new->name = getStringCopy("classic");
1715 artwork_new->sort_priority = LEVELCLASS_CLASSICS;
1718 artwork_new->color = LEVELCOLOR(artwork_new);
1719 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1722 artwork_new->name = getStringCopy(artwork_new->filename);
1724 artwork_new->name_short = getStringCopy(artwork_new->name);
1725 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1728 pushTreeInfo(node_first, artwork_new);
1730 freeSetupFileList(setup_file_list);
1732 free(directory_path);
1738 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1739 TreeInfo *node_parent,
1740 char *base_directory, int type)
1743 struct dirent *dir_entry;
1744 boolean valid_entry_found = FALSE;
1746 if ((dir = opendir(base_directory)) == NULL)
1748 if ((type == TREE_TYPE_GRAPHICS_DIR &&
1749 base_directory == options.graphics_directory) ||
1750 (type == TREE_TYPE_SOUNDS_DIR &&
1751 base_directory == options.sounds_directory) ||
1752 (type == TREE_TYPE_MUSIC_DIR &&
1753 base_directory == options.music_directory))
1754 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1758 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1760 struct stat file_status;
1761 char *directory_name = dir_entry->d_name;
1762 char *directory_path = getPath2(base_directory, directory_name);
1764 /* skip entries for current and parent directory */
1765 if (strcmp(directory_name, ".") == 0 ||
1766 strcmp(directory_name, "..") == 0)
1768 free(directory_path);
1772 /* find out if directory entry is itself a directory */
1773 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1774 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1776 free(directory_path);
1780 free(directory_path);
1782 /* check if this directory contains artwork with or without config file */
1783 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1785 directory_name, type);
1790 /* check if this directory directly contains artwork itself */
1791 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1792 base_directory, ".",
1794 if (!valid_entry_found)
1795 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1799 void LoadArtworkInfo()
1801 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1803 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1804 options.graphics_directory,
1805 TREE_TYPE_GRAPHICS_DIR);
1806 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1807 getUserGraphicsDir(),
1808 TREE_TYPE_GRAPHICS_DIR);
1810 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1811 options.sounds_directory,
1812 TREE_TYPE_SOUNDS_DIR);
1813 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1815 TREE_TYPE_SOUNDS_DIR);
1817 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1818 options.music_directory,
1819 TREE_TYPE_MUSIC_DIR);
1820 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1822 TREE_TYPE_MUSIC_DIR);
1824 /* before sorting, the first entries will be from the user directory */
1825 artwork.gfx_current =
1826 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1827 if (artwork.gfx_current == NULL)
1828 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1830 artwork.snd_current =
1831 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1832 if (artwork.snd_current == NULL)
1833 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1835 artwork.mus_current =
1836 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1837 if (artwork.mus_current == NULL)
1838 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1840 artwork.graphics_set_current = artwork.gfx_current->name;
1841 artwork.sounds_set_current = artwork.snd_current->name;
1842 artwork.music_set_current = artwork.mus_current->name;
1845 printf("graphics set == %s\n\n", artwork.graphics_set_current);
1846 printf("sounds set == %s\n\n", artwork.sounds_set_current);
1847 printf("music set == %s\n\n", artwork.music_set_current);
1850 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1851 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1852 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1855 dumpTreeInfo(artwork.gfx_first, 0);
1856 dumpTreeInfo(artwork.snd_first, 0);
1857 dumpTreeInfo(artwork.mus_first, 0);
1861 void LoadArtworkInfoFromLevelInfo(TreeInfo *node)
1865 char *path = getPath3((node->user_defined ?
1866 getUserLevelDir(NULL) : options.level_directory),
1867 node->fullpath, SOUNDS_DIRECTORY);
1870 if (!node->parent_link)
1871 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1872 node->filename, node->name);
1875 if (!node->parent_link)
1877 TreeInfo *topnode_last = artwork.snd_first;
1879 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1881 TREE_TYPE_SOUNDS_DIR);
1883 if (topnode_last != artwork.snd_first)
1886 printf("NEW NODE: '%s'\n", artwork.snd_first->name);
1889 free(artwork.snd_first->name);
1890 free(artwork.snd_first->name_sorting);
1891 free(artwork.snd_first->name_short);
1893 artwork.snd_first->name = getStringCopy(node->name);
1894 artwork.snd_first->name_sorting = getStringCopy(node->name);
1895 artwork.snd_first->name_short = getStringCopy(node->filename);
1897 artwork.snd_first->sort_priority = node->sort_priority;
1898 artwork.snd_first->color = LEVELCOLOR(artwork.snd_first);
1904 if (node->node_group != NULL)
1905 LoadArtworkInfoFromLevelInfo(node->node_group);
1911 void LoadLevelArtworkInfo()
1913 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1915 LoadArtworkInfoFromLevelInfo(leveldir_first);
1917 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1920 dumpTreeInfo(artwork.snd_first, 0);
1924 static void SaveUserLevelInfo()
1930 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1932 if (!(file = fopen(filename, MODE_WRITE)))
1934 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1939 /* always start with reliable default values */
1940 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1942 ldi.name = getLoginName();
1943 ldi.author = getRealName();
1945 ldi.first_level = 1;
1946 ldi.sort_priority = LEVELCLASS_USER_START;
1947 ldi.readonly = FALSE;
1949 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1950 getCookie("LEVELINFO")));
1952 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1953 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1954 i != LEVELINFO_TOKEN_NAME_SORTING &&
1955 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1956 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1961 SetFilePermissions(filename, PERMS_PRIVATE);
1964 char *getSetupValue(int type, void *value)
1966 static char value_string[MAX_LINE_LEN];
1971 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1975 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1979 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1983 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1987 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1991 sprintf(value_string, "%d", *(int *)value);
1995 strcpy(value_string, *(char **)value);
1999 value_string[0] = '\0';
2003 return value_string;
2006 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2010 static char token_string[MAX_LINE_LEN];
2011 int token_type = token_info[token_nr].type;
2012 void *setup_value = token_info[token_nr].value;
2013 char *token_text = token_info[token_nr].text;
2014 char *value_string = getSetupValue(token_type, setup_value);
2016 /* build complete token string */
2017 sprintf(token_string, "%s%s", prefix, token_text);
2019 /* build setup entry line */
2020 line = getFormattedSetupEntry(token_string, value_string);
2022 if (token_type == TYPE_KEY_X11)
2024 Key key = *(Key *)setup_value;
2025 char *keyname = getKeyNameFromKey(key);
2027 /* add comment, if useful */
2028 if (strcmp(keyname, "(undefined)") != 0 &&
2029 strcmp(keyname, "(unknown)") != 0)
2031 /* add at least one whitespace */
2033 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2037 strcat(line, keyname);
2044 void LoadLevelSetup_LastSeries()
2047 struct SetupFileList *level_setup_list = NULL;
2049 /* always start with reliable default values */
2050 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2052 /* ----------------------------------------------------------------------- */
2053 /* ~/.<program>/levelsetup.conf */
2054 /* ----------------------------------------------------------------------- */
2056 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2058 if ((level_setup_list = loadSetupFileList(filename)))
2060 char *last_level_series =
2061 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2063 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2065 if (leveldir_current == NULL)
2066 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2068 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2070 freeSetupFileList(level_setup_list);
2073 Error(ERR_WARN, "using default setup values");
2078 void SaveLevelSetup_LastSeries()
2081 char *level_subdir = leveldir_current->filename;
2084 /* ----------------------------------------------------------------------- */
2085 /* ~/.<program>/levelsetup.conf */
2086 /* ----------------------------------------------------------------------- */
2088 InitUserDataDirectory();
2090 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2092 if (!(file = fopen(filename, MODE_WRITE)))
2094 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2099 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2100 getCookie("LEVELSETUP")));
2101 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2107 SetFilePermissions(filename, PERMS_PRIVATE);
2110 static void checkSeriesInfo()
2112 static char *level_directory = NULL;
2114 struct dirent *dir_entry;
2116 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2118 level_directory = getPath2((leveldir_current->user_defined ?
2119 getUserLevelDir(NULL) :
2120 options.level_directory),
2121 leveldir_current->fullpath);
2123 if ((dir = opendir(level_directory)) == NULL)
2125 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2129 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2131 if (strlen(dir_entry->d_name) > 4 &&
2132 dir_entry->d_name[3] == '.' &&
2133 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2135 char levelnum_str[4];
2138 strncpy(levelnum_str, dir_entry->d_name, 3);
2139 levelnum_str[3] = '\0';
2141 levelnum_value = atoi(levelnum_str);
2143 if (levelnum_value < leveldir_current->first_level)
2145 Error(ERR_WARN, "additional level %d found", levelnum_value);
2146 leveldir_current->first_level = levelnum_value;
2148 else if (levelnum_value > leveldir_current->last_level)
2150 Error(ERR_WARN, "additional level %d found", levelnum_value);
2151 leveldir_current->last_level = levelnum_value;
2159 void LoadLevelSetup_SeriesInfo()
2162 struct SetupFileList *level_setup_list = NULL;
2163 char *level_subdir = leveldir_current->filename;
2165 /* always start with reliable default values */
2166 level_nr = leveldir_current->first_level;
2168 checkSeriesInfo(leveldir_current);
2170 /* ----------------------------------------------------------------------- */
2171 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2172 /* ----------------------------------------------------------------------- */
2174 level_subdir = leveldir_current->filename;
2176 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2178 if ((level_setup_list = loadSetupFileList(filename)))
2182 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2186 level_nr = atoi(token_value);
2188 if (level_nr < leveldir_current->first_level)
2189 level_nr = leveldir_current->first_level;
2190 if (level_nr > leveldir_current->last_level)
2191 level_nr = leveldir_current->last_level;
2194 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2198 int level_nr = atoi(token_value);
2200 if (level_nr < leveldir_current->first_level)
2201 level_nr = leveldir_current->first_level;
2202 if (level_nr > leveldir_current->last_level + 1)
2203 level_nr = leveldir_current->last_level;
2205 if (leveldir_current->user_defined)
2206 level_nr = leveldir_current->last_level;
2208 leveldir_current->handicap_level = level_nr;
2211 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2213 freeSetupFileList(level_setup_list);
2216 Error(ERR_WARN, "using default setup values");
2221 void SaveLevelSetup_SeriesInfo()
2224 char *level_subdir = leveldir_current->filename;
2225 char *level_nr_str = int2str(level_nr, 0);
2226 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2229 /* ----------------------------------------------------------------------- */
2230 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2231 /* ----------------------------------------------------------------------- */
2233 InitLevelSetupDirectory(level_subdir);
2235 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2237 if (!(file = fopen(filename, MODE_WRITE)))
2239 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2244 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2245 getCookie("LEVELSETUP")));
2246 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2248 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2249 handicap_level_str));
2254 SetFilePermissions(filename, PERMS_PRIVATE);