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
87 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
88 GRAPHICSINFO_FILENAME : \
89 (type) == TREE_TYPE_SOUNDS_DIR ? \
90 SOUNDSINFO_FILENAME : \
91 (type) == TREE_TYPE_MUSIC_DIR ? \
92 MUSICINFO_FILENAME : "")
94 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
95 GRAPHICS_DIRECTORY : \
96 (type) == TREE_TYPE_SOUNDS_DIR ? \
98 (type) == TREE_TYPE_MUSIC_DIR ? \
101 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
102 options.graphics_directory : \
103 (type) == TREE_TYPE_SOUNDS_DIR ? \
104 options.sounds_directory : \
105 (type) == TREE_TYPE_MUSIC_DIR ? \
106 options.music_directory : "")
109 /* ------------------------------------------------------------------------- */
111 /* ------------------------------------------------------------------------- */
113 static char *getLevelClassDescription(TreeInfo *ldi)
115 int position = ldi->sort_priority / 100;
117 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
118 return levelclass_desc[position];
120 return "Unknown Level Class";
123 static char *getUserLevelDir(char *level_subdir)
125 static char *userlevel_dir = NULL;
126 char *data_dir = getUserDataDir();
127 char *userlevel_subdir = LEVELS_DIRECTORY;
132 if (level_subdir != NULL)
133 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
135 userlevel_dir = getPath2(data_dir, userlevel_subdir);
137 return userlevel_dir;
140 static char *getTapeDir(char *level_subdir)
142 static char *tape_dir = NULL;
143 char *data_dir = getUserDataDir();
144 char *tape_subdir = TAPES_DIRECTORY;
149 if (level_subdir != NULL)
150 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
152 tape_dir = getPath2(data_dir, tape_subdir);
157 static char *getScoreDir(char *level_subdir)
159 static char *score_dir = NULL;
160 char *data_dir = options.rw_base_directory;
161 char *score_subdir = SCORES_DIRECTORY;
166 if (level_subdir != NULL)
167 score_dir = getPath3(data_dir, score_subdir, level_subdir);
169 score_dir = getPath2(data_dir, score_subdir);
174 static char *getLevelSetupDir(char *level_subdir)
176 static char *levelsetup_dir = NULL;
177 char *data_dir = getUserDataDir();
178 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
181 free(levelsetup_dir);
183 if (level_subdir != NULL)
184 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
186 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
188 return levelsetup_dir;
191 static char *getLevelDirFromTreeInfo(TreeInfo *node)
193 static char *level_dir = NULL;
196 return options.level_directory;
201 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
202 options.level_directory), node->fullpath);
207 static char *getCurrentLevelDir()
209 return getLevelDirFromTreeInfo(leveldir_current);
212 static char *getDefaultGraphicsDir(char *graphics_subdir)
214 static char *graphics_dir = NULL;
216 if (graphics_subdir == NULL)
217 return options.graphics_directory;
222 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
227 static char *getDefaultSoundsDir(char *sounds_subdir)
229 static char *sounds_dir = NULL;
231 if (sounds_subdir == NULL)
232 return options.sounds_directory;
237 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
242 static char *getDefaultMusicDir(char *music_subdir)
244 static char *music_dir = NULL;
246 if (music_subdir == NULL)
247 return options.music_directory;
252 music_dir = getPath2(options.music_directory, music_subdir);
257 static char *getUserGraphicsDir()
259 static char *usergraphics_dir = NULL;
261 if (usergraphics_dir == NULL)
262 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
264 return usergraphics_dir;
267 static char *getUserSoundsDir()
269 static char *usersounds_dir = NULL;
271 if (usersounds_dir == NULL)
272 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
274 return usersounds_dir;
277 static char *getUserMusicDir()
279 static char *usermusic_dir = NULL;
281 if (usermusic_dir == NULL)
282 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
284 return usermusic_dir;
287 char *getLevelFilename(int nr)
289 static char *filename = NULL;
290 char basename[MAX_FILENAME_LEN];
292 if (filename != NULL)
295 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
296 filename = getPath2(getCurrentLevelDir(), basename);
301 char *getTapeFilename(int nr)
303 static char *filename = NULL;
304 char basename[MAX_FILENAME_LEN];
306 if (filename != NULL)
309 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
310 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
315 char *getScoreFilename(int nr)
317 static char *filename = NULL;
318 char basename[MAX_FILENAME_LEN];
320 if (filename != NULL)
323 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
324 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
329 char *getSetupFilename()
331 static char *filename = NULL;
333 if (filename != NULL)
336 filename = getPath2(getSetupDir(), SETUP_FILENAME);
341 static char *getSetupArtworkDir(TreeInfo *ti)
343 static char *artwork_dir = NULL;
345 if (artwork_dir != NULL)
348 artwork_dir = getPath2(ti->basepath, ti->fullpath);
353 static char *getCorrectedImageBasename(char *basename)
355 char *result = basename;
357 #if defined(PLATFORM_MSDOS)
358 if (program.filename_prefix != NULL)
360 int prefix_len = strlen(program.filename_prefix);
362 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
363 result = &basename[prefix_len];
370 static boolean fileExists(char *filename)
373 printf("checking file '%s'\n", filename);
376 return (access(filename, F_OK) == 0);
379 char *getCustomImageFilename(char *basename)
381 static char *filename = NULL;
383 if (filename != NULL)
386 basename = getCorrectedImageBasename(basename);
388 /* 1st try: look for special artwork in current level series directory */
389 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
390 if (fileExists(filename))
393 /* 2nd try: look for special artwork in private artwork directory */
394 filename = getPath2(getUserGraphicsDir(), basename);
395 if (fileExists(filename))
398 /* 3rd try: look for special artwork in configured artwork directory */
399 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
400 if (fileExists(filename))
403 /* 4th try: look for default artwork in new default artwork directory */
404 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
405 if (fileExists(filename))
408 /* 5th try: look for default artwork in old default artwork directory */
409 filename = getPath2(options.graphics_directory, basename);
410 if (fileExists(filename))
413 return NULL; /* cannot find image file */
416 char *getCustomSoundFilename(char *basename)
418 static char *filename = NULL;
420 if (filename != NULL)
424 /* 1st try: look for special artwork in current level series directory */
425 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
426 if (fileExists(filename))
431 /* 2nd try: look for special artwork in private artwork directory */
432 filename = getPath2(getUserSoundsDir(), basename);
433 if (fileExists(filename))
437 /* 3rd try: look for special artwork in configured artwork directory */
438 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
439 if (fileExists(filename))
442 /* 4th try: look for default artwork in new default artwork directory */
443 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
444 if (fileExists(filename))
447 /* 5th try: look for default artwork in old default artwork directory */
448 filename = getPath2(options.sounds_directory, basename);
449 if (fileExists(filename))
452 return NULL; /* cannot find image file */
455 char *getCustomSoundConfigFilename()
457 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
460 char *getCustomMusicDirectory(void)
462 static char *directory = NULL;
464 if (directory != NULL)
467 /* 1st try: look for special artwork in current level series directory */
468 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
469 if (fileExists(directory))
472 /* 2nd try: look for special artwork in private artwork directory */
473 directory = getStringCopy(getUserMusicDir());
474 if (fileExists(directory))
477 /* 3rd try: look for special artwork in configured artwork directory */
478 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
479 if (fileExists(directory))
482 /* 4th try: look for default artwork in new default artwork directory */
483 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
484 if (fileExists(directory))
487 /* 5th try: look for default artwork in old default artwork directory */
488 directory = getStringCopy(options.music_directory);
489 if (fileExists(directory))
492 return NULL; /* cannot find image file */
495 void InitTapeDirectory(char *level_subdir)
497 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
498 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
499 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
502 void InitScoreDirectory(char *level_subdir)
504 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
505 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
508 static void SaveUserLevelInfo();
510 void InitUserLevelDirectory(char *level_subdir)
512 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
514 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
515 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
516 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
522 void InitLevelSetupDirectory(char *level_subdir)
524 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
525 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
526 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
529 void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
531 int file_version_major, file_version_minor, file_version_patch;
532 int game_version_major, game_version_minor, game_version_patch;
534 file_version_major = fgetc(file);
535 file_version_minor = fgetc(file);
536 file_version_patch = fgetc(file);
537 fgetc(file); /* not used */
539 game_version_major = fgetc(file);
540 game_version_minor = fgetc(file);
541 game_version_patch = fgetc(file);
542 fgetc(file); /* not used */
544 *file_version = VERSION_IDENT(file_version_major,
548 *game_version = VERSION_IDENT(game_version_major,
553 void WriteChunk_VERS(FILE *file, int file_version, int game_version)
555 int file_version_major = VERSION_MAJOR(file_version);
556 int file_version_minor = VERSION_MINOR(file_version);
557 int file_version_patch = VERSION_PATCH(file_version);
558 int game_version_major = VERSION_MAJOR(game_version);
559 int game_version_minor = VERSION_MINOR(game_version);
560 int game_version_patch = VERSION_PATCH(game_version);
562 fputc(file_version_major, file);
563 fputc(file_version_minor, file);
564 fputc(file_version_patch, file);
565 fputc(0, file); /* not used */
567 fputc(game_version_major, file);
568 fputc(game_version_minor, file);
569 fputc(game_version_patch, file);
570 fputc(0, file); /* not used */
574 /* ------------------------------------------------------------------------- */
575 /* some functions to handle lists of level directories */
576 /* ------------------------------------------------------------------------- */
578 TreeInfo *newTreeInfo()
580 return checked_calloc(sizeof(TreeInfo));
583 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
585 node_new->next = *node_first;
586 *node_first = node_new;
589 int numTreeInfo(TreeInfo *node)
602 boolean validLevelSeries(TreeInfo *node)
604 return (node != NULL && !node->node_group && !node->parent_link);
607 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
611 if (node->node_top) /* start with first tree entry */
612 return getFirstValidTreeInfoEntry(*node->node_top);
616 else if (node->node_group) /* enter level group (step down into tree) */
617 return getFirstValidTreeInfoEntry(node->node_group);
618 else if (node->parent_link) /* skip start entry of level group */
620 if (node->next) /* get first real level series entry */
621 return getFirstValidTreeInfoEntry(node->next);
622 else /* leave empty level group and go on */
623 return getFirstValidTreeInfoEntry(node->node_parent->next);
625 else /* this seems to be a regular level series */
629 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
634 if (node->node_parent == NULL) /* top level group */
635 return *node->node_top;
636 else /* sub level group */
637 return node->node_parent->node_group;
640 int numTreeInfoInGroup(TreeInfo *node)
642 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
645 int posTreeInfo(TreeInfo *node)
647 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
652 if (node_cmp == node)
656 node_cmp = node_cmp->next;
662 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
664 TreeInfo *node_default = node;
679 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
681 if (filename == NULL)
686 if (node->node_group)
688 TreeInfo *node_group;
690 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
695 else if (!node->parent_link)
697 if (strcmp(filename, node->filename) == 0)
707 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
709 return getTreeInfoFromFilenameExt(ti, filename);
712 void dumpTreeInfo(TreeInfo *node, int depth)
716 printf("Dumping TreeInfo:\n");
720 for (i=0; i<(depth + 1) * 3; i++)
723 printf("filename == '%s' (%s) [%s]\n",
724 node->filename, node->name, node->name_short);
726 if (node->node_group != NULL)
727 dumpTreeInfo(node->node_group, depth + 1);
733 void sortTreeInfo(TreeInfo **node_first,
734 int (*compare_function)(const void *, const void *))
736 int num_nodes = numTreeInfo(*node_first);
737 TreeInfo **sort_array;
738 TreeInfo *node = *node_first;
744 /* allocate array for sorting structure pointers */
745 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
747 /* writing structure pointers to sorting array */
748 while (i < num_nodes && node) /* double boundary check... */
750 sort_array[i] = node;
756 /* sorting the structure pointers in the sorting array */
757 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
760 /* update the linkage of list elements with the sorted node array */
761 for (i=0; i<num_nodes - 1; i++)
762 sort_array[i]->next = sort_array[i + 1];
763 sort_array[num_nodes - 1]->next = NULL;
765 /* update the linkage of the main list anchor pointer */
766 *node_first = sort_array[0];
770 /* now recursively sort the level group structures */
774 if (node->node_group != NULL)
775 sortTreeInfo(&node->node_group, compare_function);
782 /* ========================================================================= */
783 /* some stuff from "files.c" */
784 /* ========================================================================= */
786 #if defined(PLATFORM_WIN32)
788 #define S_IRGRP S_IRUSR
791 #define S_IROTH S_IRUSR
794 #define S_IWGRP S_IWUSR
797 #define S_IWOTH S_IWUSR
800 #define S_IXGRP S_IXUSR
803 #define S_IXOTH S_IXUSR
806 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
811 #endif /* PLATFORM_WIN32 */
813 /* file permissions for newly written files */
814 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
815 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
816 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
818 #define MODE_W_PRIVATE (S_IWUSR)
819 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
820 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
822 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
823 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
825 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
826 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
828 char *getUserDataDir(void)
830 static char *userdata_dir = NULL;
834 char *home_dir = getHomeDir();
835 char *data_dir = program.userdata_directory;
837 userdata_dir = getPath2(home_dir, data_dir);
845 return getUserDataDir();
848 static mode_t posix_umask(mode_t mask)
850 #if defined(PLATFORM_UNIX)
857 static int posix_mkdir(const char *pathname, mode_t mode)
859 #if defined(PLATFORM_WIN32)
860 return mkdir(pathname);
862 return mkdir(pathname, mode);
866 void createDirectory(char *dir, char *text, int permission_class)
868 /* leave "other" permissions in umask untouched, but ensure group parts
869 of USERDATA_DIR_MODE are not masked */
870 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
871 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
872 mode_t normal_umask = posix_umask(0);
873 mode_t group_umask = ~(dir_mode & S_IRWXG);
874 posix_umask(normal_umask & group_umask);
876 if (access(dir, F_OK) != 0)
877 if (posix_mkdir(dir, dir_mode) != 0)
878 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
880 posix_umask(normal_umask); /* reset normal umask */
883 void InitUserDataDirectory()
885 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
888 void SetFilePermissions(char *filename, int permission_class)
890 chmod(filename, (permission_class == PERMS_PRIVATE ?
891 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
894 char *getCookie(char *file_type)
896 static char cookie[MAX_COOKIE_LEN + 1];
898 if (strlen(program.cookie_prefix) + 1 +
899 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
900 return "[COOKIE ERROR]"; /* should never happen */
902 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
903 program.cookie_prefix, file_type,
904 program.version_major, program.version_minor);
909 int getFileVersionFromCookieString(const char *cookie)
911 const char *ptr_cookie1, *ptr_cookie2;
912 const char *pattern1 = "_FILE_VERSION_";
913 const char *pattern2 = "?.?";
914 const int len_cookie = strlen(cookie);
915 const int len_pattern1 = strlen(pattern1);
916 const int len_pattern2 = strlen(pattern2);
917 const int len_pattern = len_pattern1 + len_pattern2;
918 int version_major, version_minor;
920 if (len_cookie <= len_pattern)
923 ptr_cookie1 = &cookie[len_cookie - len_pattern];
924 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
926 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
929 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
930 ptr_cookie2[1] != '.' ||
931 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
934 version_major = ptr_cookie2[0] - '0';
935 version_minor = ptr_cookie2[2] - '0';
937 return VERSION_IDENT(version_major, version_minor, 0);
940 boolean checkCookieString(const char *cookie, const char *template)
942 const char *pattern = "_FILE_VERSION_?.?";
943 const int len_cookie = strlen(cookie);
944 const int len_template = strlen(template);
945 const int len_pattern = strlen(pattern);
947 if (len_cookie != len_template)
950 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
956 /* ------------------------------------------------------------------------- */
957 /* setup file list handling functions */
958 /* ------------------------------------------------------------------------- */
960 int get_string_integer_value(char *s)
962 static char *number_text[][3] =
964 { "0", "zero", "null", },
965 { "1", "one", "first" },
966 { "2", "two", "second" },
967 { "3", "three", "third" },
968 { "4", "four", "fourth" },
969 { "5", "five", "fifth" },
970 { "6", "six", "sixth" },
971 { "7", "seven", "seventh" },
972 { "8", "eight", "eighth" },
973 { "9", "nine", "ninth" },
974 { "10", "ten", "tenth" },
975 { "11", "eleven", "eleventh" },
976 { "12", "twelve", "twelfth" },
980 char *s_lower = getStringToLower(s);
985 if (strcmp(s_lower, number_text[i][j]) == 0)
996 boolean get_string_boolean_value(char *s)
998 char *s_lower = getStringToLower(s);
999 boolean result = FALSE;
1001 if (strcmp(s_lower, "true") == 0 ||
1002 strcmp(s_lower, "yes") == 0 ||
1003 strcmp(s_lower, "on") == 0 ||
1004 get_string_integer_value(s) == 1)
1012 char *getFormattedSetupEntry(char *token, char *value)
1015 static char entry[MAX_LINE_LEN];
1017 /* start with the token and some spaces to format output line */
1018 sprintf(entry, "%s:", token);
1019 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1022 /* continue with the token's value */
1023 strcat(entry, value);
1028 void freeSetupFileList(struct SetupFileList *setup_file_list)
1030 if (!setup_file_list)
1033 if (setup_file_list->token)
1034 free(setup_file_list->token);
1035 if (setup_file_list->value)
1036 free(setup_file_list->value);
1037 if (setup_file_list->next)
1038 freeSetupFileList(setup_file_list->next);
1039 free(setup_file_list);
1042 static struct SetupFileList *newSetupFileList(char *token, char *value)
1044 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1046 new->token = checked_malloc(strlen(token) + 1);
1047 strcpy(new->token, token);
1049 new->value = checked_malloc(strlen(value) + 1);
1050 strcpy(new->value, value);
1057 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1059 if (!setup_file_list)
1062 if (strcmp(setup_file_list->token, token) == 0)
1063 return setup_file_list->value;
1065 return getTokenValue(setup_file_list->next, token);
1068 static void setTokenValue(struct SetupFileList *setup_file_list,
1069 char *token, char *value)
1071 if (!setup_file_list)
1074 if (strcmp(setup_file_list->token, token) == 0)
1076 free(setup_file_list->value);
1077 setup_file_list->value = checked_malloc(strlen(value) + 1);
1078 strcpy(setup_file_list->value, value);
1080 else if (setup_file_list->next == NULL)
1081 setup_file_list->next = newSetupFileList(token, value);
1083 setTokenValue(setup_file_list->next, token, value);
1087 static void printSetupFileList(struct SetupFileList *setup_file_list)
1089 if (!setup_file_list)
1092 printf("token: '%s'\n", setup_file_list->token);
1093 printf("value: '%s'\n", setup_file_list->value);
1095 printSetupFileList(setup_file_list->next);
1099 struct SetupFileList *loadSetupFileList(char *filename)
1102 char line[MAX_LINE_LEN];
1103 char *token, *value, *line_ptr;
1104 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1105 struct SetupFileList *first_valid_list_entry;
1109 if (!(file = fopen(filename, MODE_READ)))
1111 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1117 /* read next line of input file */
1118 if (!fgets(line, MAX_LINE_LEN, file))
1121 /* cut trailing comment or whitespace from input line */
1122 for (line_ptr = line; *line_ptr; line_ptr++)
1124 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1131 /* cut trailing whitespaces from input line */
1132 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1133 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1136 /* ignore empty lines */
1140 line_len = strlen(line);
1142 /* cut leading whitespaces from token */
1143 for (token = line; *token; token++)
1144 if (*token != ' ' && *token != '\t')
1147 /* find end of token */
1148 for (line_ptr = token; *line_ptr; line_ptr++)
1150 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1157 if (line_ptr < line + line_len)
1158 value = line_ptr + 1;
1162 /* cut leading whitespaces from value */
1163 for (; *value; value++)
1164 if (*value != ' ' && *value != '\t')
1167 if (*token && *value)
1168 setTokenValue(setup_file_list, token, value);
1173 first_valid_list_entry = setup_file_list->next;
1175 /* free empty list header */
1176 setup_file_list->next = NULL;
1177 freeSetupFileList(setup_file_list);
1179 if (first_valid_list_entry == NULL)
1180 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1182 return first_valid_list_entry;
1185 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1188 if (!setup_file_list)
1191 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1193 if (!checkCookieString(setup_file_list->value, identifier))
1195 Error(ERR_WARN, "configuration file has wrong file identifier");
1202 if (setup_file_list->next)
1203 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1206 Error(ERR_WARN, "configuration file has no file identifier");
1212 /* ========================================================================= */
1213 /* setup file stuff */
1214 /* ========================================================================= */
1216 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1217 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1218 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1220 /* level directory info */
1221 #define LEVELINFO_TOKEN_NAME 0
1222 #define LEVELINFO_TOKEN_NAME_SHORT 1
1223 #define LEVELINFO_TOKEN_NAME_SORTING 2
1224 #define LEVELINFO_TOKEN_AUTHOR 3
1225 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1226 #define LEVELINFO_TOKEN_LEVELS 5
1227 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1228 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1229 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1230 #define LEVELINFO_TOKEN_READONLY 9
1232 #define NUM_LEVELINFO_TOKENS 10
1234 static LevelDirTree ldi;
1236 static struct TokenInfo levelinfo_tokens[] =
1238 /* level directory info */
1239 { TYPE_STRING, &ldi.name, "name" },
1240 { TYPE_STRING, &ldi.name_short, "name_short" },
1241 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1242 { TYPE_STRING, &ldi.author, "author" },
1243 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1244 { TYPE_INTEGER, &ldi.levels, "levels" },
1245 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1246 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1247 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1248 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1251 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1255 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1256 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1257 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1258 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1261 ldi->node_parent = NULL;
1262 ldi->node_group = NULL;
1266 ldi->cl_cursor = -1;
1268 ldi->filename = NULL;
1269 ldi->fullpath = NULL;
1270 ldi->basepath = NULL;
1271 ldi->name = getStringCopy(ANONYMOUS_NAME);
1272 ldi->name_short = NULL;
1273 ldi->name_sorting = NULL;
1274 ldi->author = getStringCopy(ANONYMOUS_NAME);
1276 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1277 ldi->parent_link = FALSE;
1278 ldi->user_defined = FALSE;
1280 ldi->class_desc = NULL;
1282 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1284 ldi->imported_from = NULL;
1286 ldi->first_level = 0;
1287 ldi->last_level = 0;
1288 ldi->level_group = FALSE;
1289 ldi->handicap_level = 0;
1290 ldi->readonly = TRUE;
1294 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1298 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1300 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1304 /* first copy all values from the parent structure ... */
1307 /* ... then set all fields to default that cannot be inherited from parent.
1308 This is especially important for all those fields that can be set from
1309 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1310 calls 'free()' for all already set token values which requires that no
1311 other structure's pointer may point to them!
1314 ldi->filename = NULL;
1315 ldi->fullpath = NULL;
1316 ldi->basepath = NULL;
1317 ldi->name = getStringCopy(ANONYMOUS_NAME);
1318 ldi->name_short = NULL;
1319 ldi->name_sorting = NULL;
1320 ldi->author = getStringCopy(parent->author);
1321 ldi->imported_from = getStringCopy(parent->imported_from);
1323 ldi->level_group = FALSE;
1324 ldi->parent_link = FALSE;
1326 ldi->node_top = parent->node_top;
1327 ldi->node_parent = parent;
1328 ldi->node_group = NULL;
1332 void setSetupInfo(struct TokenInfo *token_info,
1333 int token_nr, char *token_value)
1335 int token_type = token_info[token_nr].type;
1336 void *setup_value = token_info[token_nr].value;
1338 if (token_value == NULL)
1341 /* set setup field to corresponding token value */
1346 *(boolean *)setup_value = get_string_boolean_value(token_value);
1350 *(Key *)setup_value = getKeyFromKeyName(token_value);
1354 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1358 *(int *)setup_value = get_string_integer_value(token_value);
1362 if (*(char **)setup_value != NULL)
1363 free(*(char **)setup_value);
1364 *(char **)setup_value = getStringCopy(token_value);
1372 static int compareTreeInfoEntries(const void *object1, const void *object2)
1374 const TreeInfo *entry1 = *((TreeInfo **)object1);
1375 const TreeInfo *entry2 = *((TreeInfo **)object2);
1378 if (entry1->parent_link || entry2->parent_link)
1379 compare_result = (entry1->parent_link ? -1 : +1);
1380 else if (entry1->sort_priority == entry2->sort_priority)
1382 char *name1 = getStringToLower(entry1->name_sorting);
1383 char *name2 = getStringToLower(entry2->name_sorting);
1385 compare_result = strcmp(name1, name2);
1390 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1391 compare_result = entry1->sort_priority - entry2->sort_priority;
1393 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1395 return compare_result;
1398 static void createParentTreeInfoNode(TreeInfo *node_parent)
1402 if (node_parent == NULL)
1405 ti_new = newTreeInfo();
1406 setTreeInfoToDefaults(ti_new, node_parent->type);
1408 ti_new->node_parent = node_parent;
1409 ti_new->parent_link = TRUE;
1411 ti_new->name = ".. (parent directory)";
1412 ti_new->name_short = getStringCopy(ti_new->name);
1413 ti_new->name_sorting = getStringCopy(ti_new->name);
1415 ti_new->filename = "..";
1416 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1418 ti_new->sort_priority = node_parent->sort_priority;
1419 ti_new->class_desc = getLevelClassDescription(ti_new);
1421 pushTreeInfo(&node_parent->node_group, ti_new);
1424 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1425 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1427 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1428 TreeInfo *node_parent,
1429 char *level_directory,
1430 char *directory_name)
1432 char *directory_path = getPath2(level_directory, directory_name);
1433 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1434 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1435 LevelDirTree *leveldir_new = NULL;
1438 if (setup_file_list == NULL)
1440 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1442 free(directory_path);
1448 leveldir_new = newTreeInfo();
1451 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1453 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1455 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1457 /* set all structure fields according to the token/value pairs */
1458 ldi = *leveldir_new;
1459 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1460 setSetupInfo(levelinfo_tokens, i,
1461 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1462 *leveldir_new = ldi;
1464 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1466 free(leveldir_new->name);
1467 leveldir_new->name = getStringCopy(leveldir_new->filename);
1470 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1472 if (leveldir_new->name_short == NULL)
1473 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1475 if (leveldir_new->name_sorting == NULL)
1476 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1478 leveldir_new->filename = getStringCopy(directory_name);
1480 if (node_parent == NULL) /* top level group */
1482 leveldir_new->basepath = level_directory;
1483 leveldir_new->fullpath = leveldir_new->filename;
1485 else /* sub level group */
1487 leveldir_new->basepath = node_parent->basepath;
1488 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1491 if (leveldir_new->levels < 1)
1492 leveldir_new->levels = 1;
1494 leveldir_new->last_level =
1495 leveldir_new->first_level + leveldir_new->levels - 1;
1497 leveldir_new->user_defined =
1498 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1500 leveldir_new->color = LEVELCOLOR(leveldir_new);
1501 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1503 leveldir_new->handicap_level = /* set handicap to default value */
1504 (leveldir_new->user_defined ?
1505 leveldir_new->last_level :
1506 leveldir_new->first_level);
1508 pushTreeInfo(node_first, leveldir_new);
1510 freeSetupFileList(setup_file_list);
1512 if (leveldir_new->level_group)
1514 /* create node to link back to current level directory */
1515 createParentTreeInfoNode(leveldir_new);
1517 /* step into sub-directory and look for more level series */
1518 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1519 leveldir_new, directory_path);
1522 free(directory_path);
1528 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1529 TreeInfo *node_parent,
1530 char *level_directory)
1533 struct dirent *dir_entry;
1534 boolean valid_entry_found = FALSE;
1536 if ((dir = opendir(level_directory)) == NULL)
1538 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1542 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1544 struct stat file_status;
1545 char *directory_name = dir_entry->d_name;
1546 char *directory_path = getPath2(level_directory, directory_name);
1548 /* skip entries for current and parent directory */
1549 if (strcmp(directory_name, ".") == 0 ||
1550 strcmp(directory_name, "..") == 0)
1552 free(directory_path);
1556 /* find out if directory entry is itself a directory */
1557 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1558 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1560 free(directory_path);
1564 free(directory_path);
1566 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1567 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1568 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1571 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1578 if (!valid_entry_found)
1580 /* check if this directory directly contains a file "levelinfo.conf" */
1581 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1582 level_directory, ".");
1585 if (!valid_entry_found)
1586 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1590 void LoadLevelInfo()
1592 InitUserLevelDirectory(getLoginName());
1594 DrawInitText("Loading level series:", 120, FC_GREEN);
1596 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1597 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1599 /* before sorting, the first entries will be from the user directory */
1600 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1602 if (leveldir_first == NULL)
1603 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1605 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1608 dumpTreeInfo(leveldir_first, 0);
1612 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1613 TreeInfo *node_parent,
1614 char *base_directory,
1615 char *directory_name, int type)
1617 char *directory_path = getPath2(base_directory, directory_name);
1618 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1619 struct SetupFileList *setup_file_list = NULL;
1620 TreeInfo *artwork_new = NULL;
1623 if (access(filename, F_OK) == 0) /* file exists */
1624 setup_file_list = loadSetupFileList(filename);
1626 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1629 struct dirent *dir_entry;
1630 boolean valid_file_found = FALSE;
1632 if ((dir = opendir(directory_path)) != NULL)
1634 while ((dir_entry = readdir(dir)) != NULL)
1636 char *entry_name = dir_entry->d_name;
1638 if (FileIsArtworkType(entry_name, type))
1640 valid_file_found = TRUE;
1648 if (!valid_file_found)
1650 if (strcmp(directory_name, ".") != 0)
1651 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1653 free(directory_path);
1660 artwork_new = newTreeInfo();
1663 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1665 setTreeInfoToDefaults(artwork_new, type);
1667 artwork_new->filename = getStringCopy(directory_name);
1669 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1672 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1675 /* set all structure fields according to the token/value pairs */
1677 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1678 setSetupInfo(levelinfo_tokens, i,
1679 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1682 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1684 free(artwork_new->name);
1685 artwork_new->name = getStringCopy(artwork_new->filename);
1688 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1690 if (artwork_new->name_short == NULL)
1691 artwork_new->name_short = getStringCopy(artwork_new->name);
1693 if (artwork_new->name_sorting == NULL)
1694 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1697 if (node_parent == NULL) /* top level group */
1699 artwork_new->basepath = getStringCopy(base_directory);
1700 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1702 else /* sub level group */
1704 artwork_new->basepath = getStringCopy(node_parent->basepath);
1705 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1708 artwork_new->user_defined =
1709 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1711 /* (may use ".sort_priority" from "setup_file_list" above) */
1712 artwork_new->color = LEVELCOLOR(artwork_new);
1713 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1715 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1717 if (artwork_new->name != NULL)
1718 free(artwork_new->name);
1720 if (strcmp(artwork_new->filename, ".") == 0)
1722 if (artwork_new->user_defined)
1724 artwork_new->name = getStringCopy("private");
1725 artwork_new->sort_priority = LEVELCLASS_USER;
1729 artwork_new->name = getStringCopy("classic");
1730 artwork_new->sort_priority = LEVELCLASS_CLASSICS;
1733 artwork_new->color = LEVELCOLOR(artwork_new);
1734 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1737 artwork_new->name = getStringCopy(artwork_new->filename);
1739 artwork_new->name_short = getStringCopy(artwork_new->name);
1740 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1743 pushTreeInfo(node_first, artwork_new);
1745 freeSetupFileList(setup_file_list);
1747 free(directory_path);
1753 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1754 TreeInfo *node_parent,
1755 char *base_directory, int type)
1758 struct dirent *dir_entry;
1759 boolean valid_entry_found = FALSE;
1761 if ((dir = opendir(base_directory)) == NULL)
1763 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1764 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1768 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1770 struct stat file_status;
1771 char *directory_name = dir_entry->d_name;
1772 char *directory_path = getPath2(base_directory, directory_name);
1774 /* skip entries for current and parent directory */
1775 if (strcmp(directory_name, ".") == 0 ||
1776 strcmp(directory_name, "..") == 0)
1778 free(directory_path);
1782 /* find out if directory entry is itself a directory */
1783 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1784 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1786 free(directory_path);
1790 free(directory_path);
1792 /* check if this directory contains artwork with or without config file */
1793 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1795 directory_name, type);
1800 /* check if this directory directly contains artwork itself */
1801 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1802 base_directory, ".",
1804 if (!valid_entry_found)
1805 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1809 void LoadArtworkInfo()
1811 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1813 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1814 options.graphics_directory,
1815 TREE_TYPE_GRAPHICS_DIR);
1816 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1817 getUserGraphicsDir(),
1818 TREE_TYPE_GRAPHICS_DIR);
1820 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1821 options.sounds_directory,
1822 TREE_TYPE_SOUNDS_DIR);
1823 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1825 TREE_TYPE_SOUNDS_DIR);
1827 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1828 options.music_directory,
1829 TREE_TYPE_MUSIC_DIR);
1830 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1832 TREE_TYPE_MUSIC_DIR);
1834 /* before sorting, the first entries will be from the user directory */
1835 artwork.gfx_current =
1836 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1837 if (artwork.gfx_current == NULL)
1838 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1840 artwork.snd_current =
1841 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1842 if (artwork.snd_current == NULL)
1843 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1845 artwork.mus_current =
1846 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1847 if (artwork.mus_current == NULL)
1848 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1850 artwork.graphics_set_current = artwork.gfx_current->name;
1851 artwork.sounds_set_current = artwork.snd_current->name;
1852 artwork.music_set_current = artwork.mus_current->name;
1855 printf("graphics set == %s\n\n", artwork.graphics_set_current);
1856 printf("sounds set == %s\n\n", artwork.sounds_set_current);
1857 printf("music set == %s\n\n", artwork.music_set_current);
1860 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1861 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1862 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1865 dumpTreeInfo(artwork.gfx_first, 0);
1866 dumpTreeInfo(artwork.snd_first, 0);
1867 dumpTreeInfo(artwork.mus_first, 0);
1871 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1872 LevelDirTree *level_node)
1874 /* recursively check all level directories for artwork sub-directories */
1878 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1879 ARTWORK_DIRECTORY((*artwork_node)->type));
1882 if (!level_node->parent_link)
1883 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1884 level_node->filename, level_node->name);
1887 if (!level_node->parent_link)
1889 TreeInfo *topnode_last = *artwork_node;
1891 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1892 (*artwork_node)->type);
1894 if (topnode_last != *artwork_node)
1896 free((*artwork_node)->name);
1897 free((*artwork_node)->name_sorting);
1898 free((*artwork_node)->name_short);
1900 (*artwork_node)->name = getStringCopy(level_node->name);
1901 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1902 (*artwork_node)->name_short = getStringCopy(level_node->filename);
1904 (*artwork_node)->sort_priority = level_node->sort_priority;
1905 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1911 if (level_node->node_group != NULL)
1912 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1914 level_node = level_node->next;
1918 void LoadLevelArtworkInfo()
1920 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1922 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1923 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1924 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1926 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1927 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1928 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1931 dumpTreeInfo(artwork.gfx_first, 0);
1932 dumpTreeInfo(artwork.snd_first, 0);
1933 dumpTreeInfo(artwork.mus_first, 0);
1937 static void SaveUserLevelInfo()
1943 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1945 if (!(file = fopen(filename, MODE_WRITE)))
1947 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1952 /* always start with reliable default values */
1953 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1955 ldi.name = getLoginName();
1956 ldi.author = getRealName();
1958 ldi.first_level = 1;
1959 ldi.sort_priority = LEVELCLASS_USER_START;
1960 ldi.readonly = FALSE;
1962 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1963 getCookie("LEVELINFO")));
1965 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1966 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1967 i != LEVELINFO_TOKEN_NAME_SORTING &&
1968 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1969 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1974 SetFilePermissions(filename, PERMS_PRIVATE);
1977 char *getSetupValue(int type, void *value)
1979 static char value_string[MAX_LINE_LEN];
1984 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1988 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1992 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1996 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2000 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2004 sprintf(value_string, "%d", *(int *)value);
2008 strcpy(value_string, *(char **)value);
2012 value_string[0] = '\0';
2016 return value_string;
2019 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2023 static char token_string[MAX_LINE_LEN];
2024 int token_type = token_info[token_nr].type;
2025 void *setup_value = token_info[token_nr].value;
2026 char *token_text = token_info[token_nr].text;
2027 char *value_string = getSetupValue(token_type, setup_value);
2029 /* build complete token string */
2030 sprintf(token_string, "%s%s", prefix, token_text);
2032 /* build setup entry line */
2033 line = getFormattedSetupEntry(token_string, value_string);
2035 if (token_type == TYPE_KEY_X11)
2037 Key key = *(Key *)setup_value;
2038 char *keyname = getKeyNameFromKey(key);
2040 /* add comment, if useful */
2041 if (strcmp(keyname, "(undefined)") != 0 &&
2042 strcmp(keyname, "(unknown)") != 0)
2044 /* add at least one whitespace */
2046 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2050 strcat(line, keyname);
2057 void LoadLevelSetup_LastSeries()
2060 struct SetupFileList *level_setup_list = NULL;
2062 /* always start with reliable default values */
2063 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2065 /* ----------------------------------------------------------------------- */
2066 /* ~/.<program>/levelsetup.conf */
2067 /* ----------------------------------------------------------------------- */
2069 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2071 if ((level_setup_list = loadSetupFileList(filename)))
2073 char *last_level_series =
2074 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2076 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2078 if (leveldir_current == NULL)
2079 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2081 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2083 freeSetupFileList(level_setup_list);
2086 Error(ERR_WARN, "using default setup values");
2091 void SaveLevelSetup_LastSeries()
2094 char *level_subdir = leveldir_current->filename;
2097 /* ----------------------------------------------------------------------- */
2098 /* ~/.<program>/levelsetup.conf */
2099 /* ----------------------------------------------------------------------- */
2101 InitUserDataDirectory();
2103 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2105 if (!(file = fopen(filename, MODE_WRITE)))
2107 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2112 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2113 getCookie("LEVELSETUP")));
2114 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2120 SetFilePermissions(filename, PERMS_PRIVATE);
2123 static void checkSeriesInfo()
2125 static char *level_directory = NULL;
2127 struct dirent *dir_entry;
2129 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2131 level_directory = getPath2((leveldir_current->user_defined ?
2132 getUserLevelDir(NULL) :
2133 options.level_directory),
2134 leveldir_current->fullpath);
2136 if ((dir = opendir(level_directory)) == NULL)
2138 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2142 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2144 if (strlen(dir_entry->d_name) > 4 &&
2145 dir_entry->d_name[3] == '.' &&
2146 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2148 char levelnum_str[4];
2151 strncpy(levelnum_str, dir_entry->d_name, 3);
2152 levelnum_str[3] = '\0';
2154 levelnum_value = atoi(levelnum_str);
2156 if (levelnum_value < leveldir_current->first_level)
2158 Error(ERR_WARN, "additional level %d found", levelnum_value);
2159 leveldir_current->first_level = levelnum_value;
2161 else if (levelnum_value > leveldir_current->last_level)
2163 Error(ERR_WARN, "additional level %d found", levelnum_value);
2164 leveldir_current->last_level = levelnum_value;
2172 void LoadLevelSetup_SeriesInfo()
2175 struct SetupFileList *level_setup_list = NULL;
2176 char *level_subdir = leveldir_current->filename;
2178 /* always start with reliable default values */
2179 level_nr = leveldir_current->first_level;
2181 checkSeriesInfo(leveldir_current);
2183 /* ----------------------------------------------------------------------- */
2184 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2185 /* ----------------------------------------------------------------------- */
2187 level_subdir = leveldir_current->filename;
2189 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2191 if ((level_setup_list = loadSetupFileList(filename)))
2195 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2199 level_nr = atoi(token_value);
2201 if (level_nr < leveldir_current->first_level)
2202 level_nr = leveldir_current->first_level;
2203 if (level_nr > leveldir_current->last_level)
2204 level_nr = leveldir_current->last_level;
2207 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2211 int level_nr = atoi(token_value);
2213 if (level_nr < leveldir_current->first_level)
2214 level_nr = leveldir_current->first_level;
2215 if (level_nr > leveldir_current->last_level + 1)
2216 level_nr = leveldir_current->last_level;
2218 if (leveldir_current->user_defined)
2219 level_nr = leveldir_current->last_level;
2221 leveldir_current->handicap_level = level_nr;
2224 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2226 freeSetupFileList(level_setup_list);
2229 Error(ERR_WARN, "using default setup values");
2234 void SaveLevelSetup_SeriesInfo()
2237 char *level_subdir = leveldir_current->filename;
2238 char *level_nr_str = int2str(level_nr, 0);
2239 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2242 /* ----------------------------------------------------------------------- */
2243 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2244 /* ----------------------------------------------------------------------- */
2246 InitLevelSetupDirectory(level_subdir);
2248 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2250 if (!(file = fopen(filename, MODE_WRITE)))
2252 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2257 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2258 getCookie("LEVELSETUP")));
2259 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2261 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2262 handicap_level_str));
2267 SetFilePermissions(filename, PERMS_PRIVATE);