1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
16 #include <sys/types.h>
26 #if !defined(PLATFORM_WIN32)
28 #include <sys/param.h>
38 /* ========================================================================= */
39 /* some generic helper functions */
40 /* ========================================================================= */
42 /* ------------------------------------------------------------------------- */
43 /* platform independent wrappers for printf() et al. (newline aware) */
44 /* ------------------------------------------------------------------------- */
46 #if defined(PLATFORM_ANDROID)
47 static int android_log_prio = ANDROID_LOG_INFO;
51 static void vfPrintLog(FILE *stream, char *format, va_list ap)
55 static void vfPrintLog(FILE *stream, char *format, va_list ap)
59 static void fPrintLog(FILE *stream, char *format, va_list ap)
63 static void fPrintLog(FILE *stream, char *format, va_list ap)
68 static void vfprintf_nonewline(FILE *stream, char *format, va_list ap)
70 #if defined(PLATFORM_ANDROID)
71 // (prefix text of logging output is currently skipped on Android)
72 //__android_log_vprint(android_log_prio, program.program_title, format, ap);
77 vfprintf(stream, format, ap);
78 vfprintf(stderr, format, ap2);
84 static void vfprintf_newline(FILE *stream, char *format, va_list ap)
86 #if defined(PLATFORM_ANDROID)
87 __android_log_vprint(android_log_prio, program.program_title, format, ap);
89 char *newline = STRING_NEWLINE;
94 vfprintf(stream, format, ap);
95 fprintf(stream, "%s", newline);
97 vfprintf(stderr, format, ap2);
98 fprintf(stderr, "%s", newline);
104 static void fprintf_nonewline(FILE *stream, char *format, ...)
108 va_start(ap, format);
109 vfprintf_nonewline(stream, format, ap);
113 static void fprintf_newline(FILE *stream, char *format, ...)
117 va_start(ap, format);
118 vfprintf_newline(stream, format, ap);
122 void fprintf_line(FILE *stream, char *line_chars, int line_length)
126 for (i = 0; i < line_length; i++)
127 fprintf_nonewline(stream, "%s", line_chars);
129 fprintf_newline(stream, "");
132 void printf_line(char *line_chars, int line_length)
134 fprintf_line(stdout, line_chars, line_length);
137 void printf_line_with_prefix(char *prefix, char *line_chars, int line_length)
139 fprintf(stdout, "%s", prefix);
140 fprintf_line(stdout, line_chars, line_length);
144 /* ------------------------------------------------------------------------- */
145 /* string functions */
146 /* ------------------------------------------------------------------------- */
148 /* int2str() returns a number converted to a string;
149 the used memory is static, but will be overwritten by later calls,
150 so if you want to save the result, copy it to a private string buffer;
151 there can be 10 local calls of int2str() without buffering the result --
152 the 11th call will then destroy the result from the first call and so on.
155 char *int2str(int number, int size)
157 static char shift_array[10][40];
158 static int shift_counter = 0;
159 char *s = shift_array[shift_counter];
161 shift_counter = (shift_counter + 1) % 10;
168 sprintf(s, " %09d", number);
169 return &s[strlen(s) - size];
173 sprintf(s, "%d", number);
179 /* something similar to "int2str()" above, but allocates its own memory
180 and has a different interface; we cannot use "itoa()", because this
181 seems to be already defined when cross-compiling to the win32 target */
183 char *i_to_a(unsigned int i)
185 static char *a = NULL;
189 if (i > 2147483647) /* yes, this is a kludge */
192 a = checked_malloc(10 + 1);
200 /* calculate base-2 logarithm of argument (rounded down to integer;
201 this function returns the number of the highest bit set in argument) */
203 int log_2(unsigned int x)
209 x -= (1 << e); /* for rounding down (rounding up: remove this line) */
216 boolean getTokenValueFromString(char *string, char **token, char **value)
218 return getTokenValueFromSetupLine(string, token, value);
222 /* ------------------------------------------------------------------------- */
223 /* counter functions */
224 /* ------------------------------------------------------------------------- */
226 /* maximal allowed length of a command line option */
227 #define MAX_OPTION_LEN 256
231 #if defined(TARGET_SDL)
232 static unsigned int getCurrentMS()
234 return SDL_GetTicks();
237 #else /* !TARGET_SDL */
239 #if defined(PLATFORM_UNIX)
240 static unsigned int getCurrentMS()
242 struct timeval current_time;
244 gettimeofday(¤t_time, NULL);
246 return current_time.tv_sec * 1000 + current_time.tv_usec / 1000;
248 #endif /* PLATFORM_UNIX */
249 #endif /* !TARGET_SDL */
251 static unsigned int mainCounter(int mode)
253 static unsigned int base_ms = 0;
254 unsigned int current_ms;
256 /* get current system milliseconds */
257 current_ms = getCurrentMS();
259 /* reset base timestamp in case of counter reset or wrap-around */
260 if (mode == INIT_COUNTER || current_ms < base_ms)
261 base_ms = current_ms;
263 /* return milliseconds since last counter reset */
264 return current_ms - base_ms;
269 #if defined(TARGET_SDL)
270 static unsigned int mainCounter(int mode)
272 static unsigned int base_ms = 0;
273 unsigned int current_ms;
274 unsigned int counter_ms;
276 current_ms = SDL_GetTicks();
278 /* reset base time in case of counter initializing or wrap-around */
279 if (mode == INIT_COUNTER || current_ms < base_ms)
280 base_ms = current_ms;
282 counter_ms = current_ms - base_ms;
284 return counter_ms; /* return milliseconds since last init */
287 #else /* !TARGET_SDL */
289 #if defined(PLATFORM_UNIX)
290 static unsigned int mainCounter(int mode)
292 static struct timeval base_time = { 0, 0 };
293 struct timeval current_time;
294 unsigned int counter_ms;
296 gettimeofday(¤t_time, NULL);
298 /* reset base time in case of counter initializing or wrap-around */
299 if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
300 base_time = current_time;
302 counter_ms = (current_time.tv_sec - base_time.tv_sec) * 1000
303 + (current_time.tv_usec - base_time.tv_usec) / 1000;
305 return counter_ms; /* return milliseconds since last init */
307 #endif /* PLATFORM_UNIX */
308 #endif /* !TARGET_SDL */
312 void InitCounter() /* set counter back to zero */
314 mainCounter(INIT_COUNTER);
317 unsigned int Counter() /* get milliseconds since last call of InitCounter() */
319 return mainCounter(READ_COUNTER);
322 static void sleep_milliseconds(unsigned int milliseconds_delay)
324 boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
328 /* we want to wait only a few ms -- if we assume that we have a
329 kernel timer resolution of 10 ms, we would wait far to long;
330 therefore it's better to do a short interval of busy waiting
331 to get our sleeping time more accurate */
333 unsigned int base_counter = Counter(), actual_counter = Counter();
335 while (actual_counter < base_counter + milliseconds_delay &&
336 actual_counter >= base_counter)
337 actual_counter = Counter();
341 #if defined(TARGET_SDL)
342 SDL_Delay(milliseconds_delay);
343 #elif defined(TARGET_ALLEGRO)
344 rest(milliseconds_delay);
346 struct timeval delay;
348 delay.tv_sec = milliseconds_delay / 1000;
349 delay.tv_usec = 1000 * (milliseconds_delay % 1000);
351 if (select(0, NULL, NULL, NULL, &delay) != 0)
352 Error(ERR_WARN, "sleep_milliseconds(): select() failed");
357 void Delay(unsigned int delay) /* Sleep specified number of milliseconds */
359 sleep_milliseconds(delay);
362 boolean FrameReached(unsigned int *frame_counter_var,
363 unsigned int frame_delay)
365 unsigned int actual_frame_counter = FrameCounter;
367 if (actual_frame_counter >= *frame_counter_var &&
368 actual_frame_counter < *frame_counter_var + frame_delay)
371 *frame_counter_var = actual_frame_counter;
376 boolean DelayReached(unsigned int *counter_var,
379 unsigned int actual_counter = Counter();
381 if (actual_counter >= *counter_var &&
382 actual_counter < *counter_var + delay)
385 *counter_var = actual_counter;
390 void WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay)
392 unsigned int actual_counter;
396 actual_counter = Counter();
398 if (actual_counter >= *counter_var &&
399 actual_counter < *counter_var + delay)
400 sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
405 *counter_var = actual_counter;
409 /* ------------------------------------------------------------------------- */
410 /* random generator functions */
411 /* ------------------------------------------------------------------------- */
413 unsigned int init_random_number(int nr, int seed)
415 if (seed == NEW_RANDOMIZE)
417 /* default random seed */
418 seed = (int)time(NULL); // seconds since the epoch
420 #if !defined(PLATFORM_WIN32)
421 /* add some more randomness */
422 struct timeval current_time;
424 gettimeofday(¤t_time, NULL);
426 seed += (int)current_time.tv_usec; // microseconds since the epoch
429 #if defined(TARGET_SDL)
430 /* add some more randomness */
431 seed += (int)SDL_GetTicks(); // milliseconds since SDL init
435 /* add some more randomness */
436 seed += GetSimpleRandom(1000000);
440 srandom_linux_libc(nr, (unsigned int) seed);
442 return (unsigned int) seed;
445 unsigned int get_random_number(int nr, int max)
447 return (max > 0 ? random_linux_libc(nr) % max : 0);
451 /* ------------------------------------------------------------------------- */
452 /* system info functions */
453 /* ------------------------------------------------------------------------- */
455 #if !defined(PLATFORM_ANDROID)
456 static char *get_corrected_real_name(char *real_name)
458 char *real_name_new = checked_malloc(MAX_USERNAME_LEN + 1);
459 char *from_ptr = real_name;
460 char *to_ptr = real_name_new;
462 /* copy the name string, but not more than MAX_USERNAME_LEN characters */
463 while (*from_ptr && (int)(to_ptr - real_name_new) < MAX_USERNAME_LEN - 1)
465 /* the name field read from "passwd" file may also contain additional
466 user information, separated by commas, which will be removed here */
467 if (*from_ptr == ',')
470 /* the user's real name may contain 'ß' characters (german sharp s),
471 which have no equivalent in upper case letters (used by our fonts) */
472 if (*from_ptr == 'ß')
479 *to_ptr++ = *from_ptr++;
484 return real_name_new;
490 static char *login_name = NULL;
492 #if defined(PLATFORM_WIN32)
493 if (login_name == NULL)
495 unsigned long buffer_size = MAX_USERNAME_LEN + 1;
496 login_name = checked_malloc(buffer_size);
498 if (GetUserName(login_name, &buffer_size) == 0)
499 strcpy(login_name, ANONYMOUS_NAME);
502 if (login_name == NULL)
506 if ((pwd = getpwuid(getuid())) == NULL)
507 login_name = ANONYMOUS_NAME;
509 login_name = getStringCopy(pwd->pw_name);
518 static char *real_name = NULL;
520 #if defined(PLATFORM_WIN32)
521 if (real_name == NULL)
523 static char buffer[MAX_USERNAME_LEN + 1];
524 unsigned long buffer_size = MAX_USERNAME_LEN + 1;
526 if (GetUserName(buffer, &buffer_size) != 0)
527 real_name = get_corrected_real_name(buffer);
529 real_name = ANONYMOUS_NAME;
531 #elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID)
532 if (real_name == NULL)
536 if ((pwd = getpwuid(getuid())) != NULL && strlen(pwd->pw_gecos) != 0)
537 real_name = get_corrected_real_name(pwd->pw_gecos);
539 real_name = ANONYMOUS_NAME;
542 real_name = ANONYMOUS_NAME;
548 time_t getFileTimestampEpochSeconds(char *filename)
550 struct stat file_status;
552 if (stat(filename, &file_status) != 0) /* cannot stat file */
555 return file_status.st_mtime;
559 /* ------------------------------------------------------------------------- */
560 /* path manipulation functions */
561 /* ------------------------------------------------------------------------- */
563 static char *getLastPathSeparatorPtr(char *filename)
565 char *last_separator = strrchr(filename, CHAR_PATH_SEPARATOR_UNIX);
567 if (last_separator == NULL) /* also try DOS/Windows variant */
568 last_separator = strrchr(filename, CHAR_PATH_SEPARATOR_DOS);
570 return last_separator;
573 char *getBaseNamePtr(char *filename)
575 char *last_separator = getLastPathSeparatorPtr(filename);
577 if (last_separator != NULL)
578 return last_separator + 1; /* separator found: strip base path */
580 return filename; /* no separator found: filename has no path */
583 char *getBaseName(char *filename)
585 return getStringCopy(getBaseNamePtr(filename));
588 char *getBasePath(char *filename)
590 char *basepath = getStringCopy(filename);
591 char *last_separator = getLastPathSeparatorPtr(basepath);
593 if (last_separator != NULL)
594 *last_separator = '\0'; /* separator found: strip basename */
596 basepath = "."; /* no separator found: use current path */
601 static char *getProgramMainDataPath()
603 char *main_data_path = getStringCopy(program.command_basepath);
605 #if defined(PLATFORM_MACOSX)
606 static char *main_data_binary_subdir = NULL;
608 if (main_data_binary_subdir == NULL)
610 main_data_binary_subdir = checked_malloc(strlen(program.program_title) + 1 +
612 strlen(MAC_APP_BINARY_SUBDIR) + 1);
614 sprintf(main_data_binary_subdir, "%s.app/%s",
615 program.program_title, MAC_APP_BINARY_SUBDIR);
618 // cut relative path to Mac OS X application binary directory from path
619 if (strSuffix(main_data_path, main_data_binary_subdir))
620 main_data_path[strlen(main_data_path) -
621 strlen(main_data_binary_subdir)] = '\0';
623 // cut trailing path separator from path (but not if path is root directory)
624 if (strSuffix(main_data_path, "/") && !strEqual(main_data_path, "/"))
625 main_data_path[strlen(main_data_path) - 1] = '\0';
628 return main_data_path;
632 /* ------------------------------------------------------------------------- */
633 /* various string functions */
634 /* ------------------------------------------------------------------------- */
636 char *getStringCat2WithSeparator(char *s1, char *s2, char *sep)
638 char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
641 sprintf(complete_string, "%s%s%s", s1, sep, s2);
643 return complete_string;
646 char *getStringCat3WithSeparator(char *s1, char *s2, char *s3, char *sep)
648 char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
649 strlen(s2) + strlen(sep) +
652 sprintf(complete_string, "%s%s%s%s%s", s1, sep, s2, sep, s3);
654 return complete_string;
657 char *getStringCat2(char *s1, char *s2)
659 return getStringCat2WithSeparator(s1, s2, "");
662 char *getStringCat3(char *s1, char *s2, char *s3)
664 return getStringCat3WithSeparator(s1, s2, s3, "");
667 char *getPath2(char *path1, char *path2)
669 #if defined(PLATFORM_ANDROID)
670 // workaround for reading from APK assets directory -- skip leading "./"
671 if (strEqual(path1, "."))
672 return getStringCopy(path2);
675 return getStringCat2WithSeparator(path1, path2, STRING_PATH_SEPARATOR);
678 char *getPath3(char *path1, char *path2, char *path3)
680 #if defined(PLATFORM_ANDROID)
681 // workaround for reading from APK assets directory -- skip leading "./"
682 if (strEqual(path1, "."))
683 return getStringCat2WithSeparator(path2, path3, STRING_PATH_SEPARATOR);
686 return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR);
689 char *getStringCopy(const char *s)
696 s_copy = checked_malloc(strlen(s) + 1);
702 char *getStringCopyN(const char *s, int n)
705 int s_len = MAX(0, n);
710 s_copy = checked_malloc(s_len + 1);
711 strncpy(s_copy, s, s_len);
712 s_copy[s_len] = '\0';
717 char *getStringCopyNStatic(const char *s, int n)
719 static char *s_copy = NULL;
721 checked_free(s_copy);
723 s_copy = getStringCopyN(s, n);
728 char *getStringToLower(const char *s)
730 char *s_copy = checked_malloc(strlen(s) + 1);
731 char *s_ptr = s_copy;
734 *s_ptr++ = tolower(*s++);
740 void setString(char **old_value, char *new_value)
742 checked_free(*old_value);
744 *old_value = getStringCopy(new_value);
747 boolean strEqual(char *s1, char *s2)
749 return (s1 == NULL && s2 == NULL ? TRUE :
750 s1 == NULL && s2 != NULL ? FALSE :
751 s1 != NULL && s2 == NULL ? FALSE :
752 strcmp(s1, s2) == 0);
755 boolean strEqualN(char *s1, char *s2, int n)
757 return (s1 == NULL && s2 == NULL ? TRUE :
758 s1 == NULL && s2 != NULL ? FALSE :
759 s1 != NULL && s2 == NULL ? FALSE :
760 strncmp(s1, s2, n) == 0);
763 boolean strPrefix(char *s, char *prefix)
765 return (s == NULL && prefix == NULL ? TRUE :
766 s == NULL && prefix != NULL ? FALSE :
767 s != NULL && prefix == NULL ? FALSE :
768 strncmp(s, prefix, strlen(prefix)) == 0);
771 boolean strSuffix(char *s, char *suffix)
773 return (s == NULL && suffix == NULL ? TRUE :
774 s == NULL && suffix != NULL ? FALSE :
775 s != NULL && suffix == NULL ? FALSE :
776 strlen(s) < strlen(suffix) ? FALSE :
777 strncmp(&s[strlen(s) - strlen(suffix)], suffix, strlen(suffix)) == 0);
780 boolean strPrefixLower(char *s, char *prefix)
782 char *s_lower = getStringToLower(s);
783 boolean match = strPrefix(s_lower, prefix);
790 boolean strSuffixLower(char *s, char *suffix)
792 char *s_lower = getStringToLower(s);
793 boolean match = strSuffix(s_lower, suffix);
801 /* ------------------------------------------------------------------------- */
802 /* command line option handling functions */
803 /* ------------------------------------------------------------------------- */
805 void GetOptions(char *argv[], void (*print_usage_function)(void))
807 char *ro_base_path = RO_BASE_PATH;
808 char *rw_base_path = RW_BASE_PATH;
809 char **options_left = &argv[1];
812 /* if the program is configured to start from current directory (default),
813 determine program package directory from program binary (some versions
814 of KDE/Konqueror and Mac OS X (especially "Mavericks") apparently do not
815 set the current working directory to the program package directory) */
817 if (strEqual(ro_base_path, "."))
818 ro_base_path = getProgramMainDataPath();
819 if (strEqual(rw_base_path, "."))
820 rw_base_path = getProgramMainDataPath();
823 #if !defined(PLATFORM_MACOSX)
824 /* if the program is configured to start from current directory (default),
825 determine program package directory (KDE/Konqueror does not do this by
826 itself and fails otherwise); on Mac OS X, the program binary is stored
827 in an application package directory -- do not try to use this directory
828 as the program data directory (Mac OS X handles this correctly anyway) */
830 if (strEqual(ro_base_path, "."))
831 ro_base_path = program.command_basepath;
832 if (strEqual(rw_base_path, "."))
833 rw_base_path = program.command_basepath;
838 /* initialize global program options */
839 options.display_name = NULL;
840 options.server_host = NULL;
841 options.server_port = 0;
843 options.ro_base_directory = ro_base_path;
844 options.rw_base_directory = rw_base_path;
845 options.level_directory = getPath2(ro_base_path, LEVELS_DIRECTORY);
846 options.graphics_directory = getPath2(ro_base_path, GRAPHICS_DIRECTORY);
847 options.sounds_directory = getPath2(ro_base_path, SOUNDS_DIRECTORY);
848 options.music_directory = getPath2(ro_base_path, MUSIC_DIRECTORY);
849 options.docs_directory = getPath2(ro_base_path, DOCS_DIRECTORY);
851 options.execute_command = NULL;
852 options.special_flags = NULL;
854 options.serveronly = FALSE;
855 options.network = FALSE;
856 options.verbose = FALSE;
857 options.debug = FALSE;
858 options.debug_x11_sync = FALSE;
861 options.verbose = TRUE;
863 #if !defined(PLATFORM_UNIX)
864 if (*options_left == NULL) /* no options given -- enable verbose mode */
865 options.verbose = TRUE;
869 while (*options_left)
871 char option_str[MAX_OPTION_LEN];
872 char *option = options_left[0];
873 char *next_option = options_left[1];
874 char *option_arg = NULL;
875 int option_len = strlen(option);
877 if (option_len >= MAX_OPTION_LEN)
878 Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
880 strcpy(option_str, option); /* copy argument into buffer */
883 if (strEqual(option, "--")) /* stop scanning arguments */
886 if (strPrefix(option, "--")) /* treat '--' like '-' */
889 option_arg = strchr(option, '=');
890 if (option_arg == NULL) /* no '=' in option */
891 option_arg = next_option;
894 *option_arg++ = '\0'; /* cut argument from option */
895 if (*option_arg == '\0') /* no argument after '=' */
896 Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
899 option_len = strlen(option);
901 if (strEqual(option, "-"))
902 Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
903 else if (strncmp(option, "-help", option_len) == 0)
905 print_usage_function();
909 else if (strncmp(option, "-display", option_len) == 0)
911 if (option_arg == NULL)
912 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
914 options.display_name = option_arg;
915 if (option_arg == next_option)
918 else if (strncmp(option, "-basepath", option_len) == 0)
920 if (option_arg == NULL)
921 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
923 /* this should be extended to separate options for ro and rw data */
924 options.ro_base_directory = ro_base_path = option_arg;
925 options.rw_base_directory = rw_base_path = option_arg;
926 if (option_arg == next_option)
929 /* adjust paths for sub-directories in base directory accordingly */
930 options.level_directory = getPath2(ro_base_path, LEVELS_DIRECTORY);
931 options.graphics_directory = getPath2(ro_base_path, GRAPHICS_DIRECTORY);
932 options.sounds_directory = getPath2(ro_base_path, SOUNDS_DIRECTORY);
933 options.music_directory = getPath2(ro_base_path, MUSIC_DIRECTORY);
934 options.docs_directory = getPath2(ro_base_path, DOCS_DIRECTORY);
936 else if (strncmp(option, "-levels", option_len) == 0)
938 if (option_arg == NULL)
939 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
941 options.level_directory = option_arg;
942 if (option_arg == next_option)
945 else if (strncmp(option, "-graphics", option_len) == 0)
947 if (option_arg == NULL)
948 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
950 options.graphics_directory = option_arg;
951 if (option_arg == next_option)
954 else if (strncmp(option, "-sounds", option_len) == 0)
956 if (option_arg == NULL)
957 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
959 options.sounds_directory = option_arg;
960 if (option_arg == next_option)
963 else if (strncmp(option, "-music", option_len) == 0)
965 if (option_arg == NULL)
966 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
968 options.music_directory = option_arg;
969 if (option_arg == next_option)
972 else if (strncmp(option, "-network", option_len) == 0)
974 options.network = TRUE;
976 else if (strncmp(option, "-serveronly", option_len) == 0)
978 options.serveronly = TRUE;
980 else if (strncmp(option, "-verbose", option_len) == 0)
982 options.verbose = TRUE;
984 else if (strncmp(option, "-debug", option_len) == 0)
986 options.debug = TRUE;
988 else if (strncmp(option, "-debug-x11-sync", option_len) == 0)
990 options.debug_x11_sync = TRUE;
992 else if (strPrefix(option, "-D"))
995 options.special_flags = getStringCopy(&option[2]);
997 char *flags_string = &option[2];
998 unsigned int flags_value;
1000 if (*flags_string == '\0')
1001 Error(ERR_EXIT_HELP, "empty flag ignored");
1003 flags_value = get_special_flags_function(flags_string);
1005 if (flags_value == 0)
1006 Error(ERR_EXIT_HELP, "unknown flag '%s'", flags_string);
1008 options.special_flags |= flags_value;
1011 else if (strncmp(option, "-execute", option_len) == 0)
1013 if (option_arg == NULL)
1014 Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
1016 options.execute_command = option_arg;
1017 if (option_arg == next_option)
1020 /* when doing batch processing, always enable verbose mode (warnings) */
1021 options.verbose = TRUE;
1023 else if (*option == '-')
1025 Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
1027 else if (options.server_host == NULL)
1029 options.server_host = *options_left;
1031 else if (options.server_port == 0)
1033 options.server_port = atoi(*options_left);
1034 if (options.server_port < 1024)
1035 Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
1038 Error(ERR_EXIT_HELP, "too many arguments");
1045 /* ------------------------------------------------------------------------- */
1046 /* error handling functions */
1047 /* ------------------------------------------------------------------------- */
1049 #define MAX_INTERNAL_ERROR_SIZE 1024
1051 /* used by SetError() and GetError() to store internal error messages */
1052 static char internal_error[MAX_INTERNAL_ERROR_SIZE];
1054 void SetError(char *format, ...)
1058 va_start(ap, format);
1059 vsnprintf(internal_error, MAX_INTERNAL_ERROR_SIZE, format, ap);
1065 return internal_error;
1068 void Error(int mode, char *format, ...)
1070 static boolean last_line_was_separator = FALSE;
1071 char *process_name = "";
1073 #if defined(PLATFORM_ANDROID)
1074 android_log_prio = (mode & ERR_DEBUG ? ANDROID_LOG_DEBUG :
1075 mode & ERR_INFO ? ANDROID_LOG_INFO :
1076 mode & ERR_WARN ? ANDROID_LOG_WARN :
1077 mode & ERR_EXIT ? ANDROID_LOG_FATAL :
1078 ANDROID_LOG_UNKNOWN);
1082 /* display warnings only when running in verbose mode */
1083 if (mode & ERR_WARN && !options.verbose)
1087 if (mode == ERR_INFO_LINE)
1089 if (!last_line_was_separator)
1090 fprintf_line(program.error_file, format, 79);
1092 last_line_was_separator = TRUE;
1097 last_line_was_separator = FALSE;
1099 if (mode & ERR_SOUND_SERVER)
1100 process_name = " sound server";
1101 else if (mode & ERR_NETWORK_SERVER)
1102 process_name = " network server";
1103 else if (mode & ERR_NETWORK_CLIENT)
1104 process_name = " network client **";
1110 fprintf_nonewline(program.error_file, "%s%s: ", program.command_basename,
1113 if (mode & ERR_WARN)
1114 fprintf_nonewline(program.error_file, "warning: ");
1116 va_start(ap, format);
1117 vfprintf_newline(program.error_file, format, ap);
1120 if ((mode & ERR_EXIT) && !(mode & ERR_FROM_SERVER))
1122 va_start(ap, format);
1123 program.exit_message_function(format, ap);
1128 if (mode & ERR_HELP)
1129 fprintf_newline(program.error_file,
1130 "%s: Try option '--help' for more information.",
1131 program.command_basename);
1133 if (mode & ERR_EXIT)
1134 fprintf_newline(program.error_file, "%s%s: aborting",
1135 program.command_basename, process_name);
1137 if (mode & ERR_EXIT)
1139 if (mode & ERR_FROM_SERVER)
1140 exit(1); /* child process: normal exit */
1142 program.exit_function(1); /* main process: clean up stuff */
1147 /* ------------------------------------------------------------------------- */
1148 /* checked memory allocation and freeing functions */
1149 /* ------------------------------------------------------------------------- */
1151 void *checked_malloc(unsigned int size)
1158 Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
1163 void *checked_calloc(unsigned int size)
1167 ptr = calloc(1, size);
1170 Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
1175 void *checked_realloc(void *ptr, unsigned int size)
1177 ptr = realloc(ptr, size);
1180 Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
1185 void checked_free(void *ptr)
1187 if (ptr != NULL) /* this check should be done by free() anyway */
1191 void clear_mem(void *ptr, unsigned int size)
1193 #if defined(PLATFORM_WIN32)
1194 /* for unknown reason, memset() sometimes crashes when compiled with MinGW */
1195 char *cptr = (char *)ptr;
1200 memset(ptr, 0, size);
1205 /* ------------------------------------------------------------------------- */
1206 /* various helper functions */
1207 /* ------------------------------------------------------------------------- */
1209 inline void swap_numbers(int *i1, int *i2)
1217 inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
1229 /* the "put" variants of the following file access functions check for the file
1230 pointer being != NULL and return the number of bytes they have or would have
1231 written; this allows for chunk writing functions to first determine the size
1232 of the (not yet written) chunk, write the correct chunk size and finally
1233 write the chunk itself */
1237 int getFile8BitInteger(File *file)
1239 return getByteFromFile(file);
1244 int getFile8BitInteger(FILE *file)
1251 int putFile8BitInteger(FILE *file, int value)
1261 int getFile16BitInteger(File *file, int byte_order)
1263 if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1264 return ((getByteFromFile(file) << 8) |
1265 (getByteFromFile(file) << 0));
1266 else /* BYTE_ORDER_LITTLE_ENDIAN */
1267 return ((getByteFromFile(file) << 0) |
1268 (getByteFromFile(file) << 8));
1273 int getFile16BitInteger(FILE *file, int byte_order)
1275 if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1276 return ((fgetc(file) << 8) |
1277 (fgetc(file) << 0));
1278 else /* BYTE_ORDER_LITTLE_ENDIAN */
1279 return ((fgetc(file) << 0) |
1280 (fgetc(file) << 8));
1285 int putFile16BitInteger(FILE *file, int value, int byte_order)
1289 if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1291 fputc((value >> 8) & 0xff, file);
1292 fputc((value >> 0) & 0xff, file);
1294 else /* BYTE_ORDER_LITTLE_ENDIAN */
1296 fputc((value >> 0) & 0xff, file);
1297 fputc((value >> 8) & 0xff, file);
1306 int getFile32BitInteger(File *file, int byte_order)
1308 if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1309 return ((getByteFromFile(file) << 24) |
1310 (getByteFromFile(file) << 16) |
1311 (getByteFromFile(file) << 8) |
1312 (getByteFromFile(file) << 0));
1313 else /* BYTE_ORDER_LITTLE_ENDIAN */
1314 return ((getByteFromFile(file) << 0) |
1315 (getByteFromFile(file) << 8) |
1316 (getByteFromFile(file) << 16) |
1317 (getByteFromFile(file) << 24));
1322 int getFile32BitInteger(FILE *file, int byte_order)
1324 if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1325 return ((fgetc(file) << 24) |
1326 (fgetc(file) << 16) |
1327 (fgetc(file) << 8) |
1328 (fgetc(file) << 0));
1329 else /* BYTE_ORDER_LITTLE_ENDIAN */
1330 return ((fgetc(file) << 0) |
1331 (fgetc(file) << 8) |
1332 (fgetc(file) << 16) |
1333 (fgetc(file) << 24));
1338 int putFile32BitInteger(FILE *file, int value, int byte_order)
1342 if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1344 fputc((value >> 24) & 0xff, file);
1345 fputc((value >> 16) & 0xff, file);
1346 fputc((value >> 8) & 0xff, file);
1347 fputc((value >> 0) & 0xff, file);
1349 else /* BYTE_ORDER_LITTLE_ENDIAN */
1351 fputc((value >> 0) & 0xff, file);
1352 fputc((value >> 8) & 0xff, file);
1353 fputc((value >> 16) & 0xff, file);
1354 fputc((value >> 24) & 0xff, file);
1363 boolean getFileChunk(File *file, char *chunk_name, int *chunk_size,
1366 const int chunk_name_length = 4;
1368 /* read chunk name */
1369 if (getStringFromFile(file, chunk_name, chunk_name_length + 1) == NULL)
1372 if (chunk_size != NULL)
1374 /* read chunk size */
1375 *chunk_size = getFile32BitInteger(file, byte_order);
1378 return (checkEndOfFile(file) ? FALSE : TRUE);
1383 boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
1386 const int chunk_name_length = 4;
1388 /* read chunk name */
1389 if (fgets(chunk_name, chunk_name_length + 1, file) == NULL)
1392 if (chunk_size != NULL)
1394 /* read chunk size */
1395 *chunk_size = getFile32BitInteger(file, byte_order);
1398 return (feof(file) || ferror(file) ? FALSE : TRUE);
1403 int putFileChunk(FILE *file, char *chunk_name, int chunk_size,
1408 /* write chunk name */
1410 fputs(chunk_name, file);
1412 num_bytes += strlen(chunk_name);
1414 if (chunk_size >= 0)
1416 /* write chunk size */
1418 putFile32BitInteger(file, chunk_size, byte_order);
1428 int getFileVersion(File *file)
1430 int version_major = getByteFromFile(file);
1431 int version_minor = getByteFromFile(file);
1432 int version_patch = getByteFromFile(file);
1433 int version_build = getByteFromFile(file);
1435 return VERSION_IDENT(version_major, version_minor, version_patch,
1441 int getFileVersion(FILE *file)
1443 int version_major = fgetc(file);
1444 int version_minor = fgetc(file);
1445 int version_patch = fgetc(file);
1446 int version_build = fgetc(file);
1448 return VERSION_IDENT(version_major, version_minor, version_patch,
1454 int putFileVersion(FILE *file, int version)
1458 int version_major = VERSION_MAJOR(version);
1459 int version_minor = VERSION_MINOR(version);
1460 int version_patch = VERSION_PATCH(version);
1461 int version_build = VERSION_BUILD(version);
1463 fputc(version_major, file);
1464 fputc(version_minor, file);
1465 fputc(version_patch, file);
1466 fputc(version_build, file);
1474 void ReadBytesFromFile(File *file, byte *buffer, unsigned int bytes)
1478 for (i = 0; i < bytes && !checkEndOfFile(file); i++)
1479 buffer[i] = getByteFromFile(file);
1484 void ReadBytesFromFile(FILE *file, byte *buffer, unsigned int bytes)
1488 for(i = 0; i < bytes && !feof(file); i++)
1489 buffer[i] = fgetc(file);
1494 void WriteBytesToFile(FILE *file, byte *buffer, unsigned int bytes)
1498 for(i = 0; i < bytes; i++)
1499 fputc(buffer[i], file);
1504 void ReadUnusedBytesFromFile(File *file, unsigned int bytes)
1506 while (bytes-- && !checkEndOfFile(file))
1507 getByteFromFile(file);
1512 void ReadUnusedBytesFromFile(FILE *file, unsigned int bytes)
1514 while (bytes-- && !feof(file))
1520 void WriteUnusedBytesToFile(FILE *file, unsigned int bytes)
1527 /* ------------------------------------------------------------------------- */
1528 /* functions to translate key identifiers between different format */
1529 /* ------------------------------------------------------------------------- */
1531 #define TRANSLATE_KEYSYM_TO_KEYNAME 0
1532 #define TRANSLATE_KEYSYM_TO_X11KEYNAME 1
1533 #define TRANSLATE_KEYNAME_TO_KEYSYM 2
1534 #define TRANSLATE_X11KEYNAME_TO_KEYSYM 3
1536 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
1545 /* normal cursor keys */
1546 { KSYM_Left, "XK_Left", "cursor left" },
1547 { KSYM_Right, "XK_Right", "cursor right" },
1548 { KSYM_Up, "XK_Up", "cursor up" },
1549 { KSYM_Down, "XK_Down", "cursor down" },
1551 /* keypad cursor keys */
1553 { KSYM_KP_Left, "XK_KP_Left", "keypad left" },
1554 { KSYM_KP_Right, "XK_KP_Right", "keypad right" },
1555 { KSYM_KP_Up, "XK_KP_Up", "keypad up" },
1556 { KSYM_KP_Down, "XK_KP_Down", "keypad down" },
1559 /* other keypad keys */
1560 #ifdef KSYM_KP_Enter
1561 { KSYM_KP_Enter, "XK_KP_Enter", "keypad enter" },
1562 { KSYM_KP_Add, "XK_KP_Add", "keypad +" },
1563 { KSYM_KP_Subtract, "XK_KP_Subtract", "keypad -" },
1564 { KSYM_KP_Multiply, "XK_KP_Multiply", "keypad mltply" },
1565 { KSYM_KP_Divide, "XK_KP_Divide", "keypad /" },
1566 { KSYM_KP_Separator,"XK_KP_Separator", "keypad ," },
1570 { KSYM_Shift_L, "XK_Shift_L", "left shift" },
1571 { KSYM_Shift_R, "XK_Shift_R", "right shift" },
1572 { KSYM_Control_L, "XK_Control_L", "left control" },
1573 { KSYM_Control_R, "XK_Control_R", "right control" },
1574 { KSYM_Meta_L, "XK_Meta_L", "left meta" },
1575 { KSYM_Meta_R, "XK_Meta_R", "right meta" },
1576 { KSYM_Alt_L, "XK_Alt_L", "left alt" },
1577 { KSYM_Alt_R, "XK_Alt_R", "right alt" },
1578 #if !defined(TARGET_SDL2)
1579 { KSYM_Super_L, "XK_Super_L", "left super" }, /* Win-L */
1580 { KSYM_Super_R, "XK_Super_R", "right super" }, /* Win-R */
1582 { KSYM_Mode_switch, "XK_Mode_switch", "mode switch" }, /* Alt-R */
1583 { KSYM_Multi_key, "XK_Multi_key", "multi key" }, /* Ctrl-R */
1585 /* some special keys */
1586 { KSYM_BackSpace, "XK_BackSpace", "backspace" },
1587 { KSYM_Delete, "XK_Delete", "delete" },
1588 { KSYM_Insert, "XK_Insert", "insert" },
1589 { KSYM_Tab, "XK_Tab", "tab" },
1590 { KSYM_Home, "XK_Home", "home" },
1591 { KSYM_End, "XK_End", "end" },
1592 { KSYM_Page_Up, "XK_Page_Up", "page up" },
1593 { KSYM_Page_Down, "XK_Page_Down", "page down" },
1595 #if defined(TARGET_SDL2)
1596 { KSYM_Menu, "XK_Menu", "menu" }, /* menu key */
1597 { KSYM_Back, "XK_Back", "back" }, /* back key */
1600 /* ASCII 0x20 to 0x40 keys (except numbers) */
1601 { KSYM_space, "XK_space", "space" },
1602 { KSYM_exclam, "XK_exclam", "!" },
1603 { KSYM_quotedbl, "XK_quotedbl", "\"" },
1604 { KSYM_numbersign, "XK_numbersign", "#" },
1605 { KSYM_dollar, "XK_dollar", "$" },
1606 { KSYM_percent, "XK_percent", "%" },
1607 { KSYM_ampersand, "XK_ampersand", "&" },
1608 { KSYM_apostrophe, "XK_apostrophe", "'" },
1609 { KSYM_parenleft, "XK_parenleft", "(" },
1610 { KSYM_parenright, "XK_parenright", ")" },
1611 { KSYM_asterisk, "XK_asterisk", "*" },
1612 { KSYM_plus, "XK_plus", "+" },
1613 { KSYM_comma, "XK_comma", "," },
1614 { KSYM_minus, "XK_minus", "-" },
1615 { KSYM_period, "XK_period", "." },
1616 { KSYM_slash, "XK_slash", "/" },
1617 { KSYM_colon, "XK_colon", ":" },
1618 { KSYM_semicolon, "XK_semicolon", ";" },
1619 { KSYM_less, "XK_less", "<" },
1620 { KSYM_equal, "XK_equal", "=" },
1621 { KSYM_greater, "XK_greater", ">" },
1622 { KSYM_question, "XK_question", "?" },
1623 { KSYM_at, "XK_at", "@" },
1625 /* more ASCII keys */
1626 { KSYM_bracketleft, "XK_bracketleft", "[" },
1627 { KSYM_backslash, "XK_backslash", "\\" },
1628 { KSYM_bracketright,"XK_bracketright", "]" },
1629 { KSYM_asciicircum, "XK_asciicircum", "^" },
1630 { KSYM_underscore, "XK_underscore", "_" },
1631 { KSYM_grave, "XK_grave", "grave" },
1632 { KSYM_quoteleft, "XK_quoteleft", "quote left" },
1633 { KSYM_braceleft, "XK_braceleft", "brace left" },
1634 { KSYM_bar, "XK_bar", "bar" },
1635 { KSYM_braceright, "XK_braceright", "brace right" },
1636 { KSYM_asciitilde, "XK_asciitilde", "~" },
1638 /* special (non-ASCII) keys (ISO-Latin-1) */
1639 { KSYM_degree, "XK_degree", "°" },
1640 { KSYM_Adiaeresis, "XK_Adiaeresis", "Ä" },
1641 { KSYM_Odiaeresis, "XK_Odiaeresis", "Ö" },
1642 { KSYM_Udiaeresis, "XK_Udiaeresis", "Ü" },
1643 { KSYM_adiaeresis, "XK_adiaeresis", "ä" },
1644 { KSYM_odiaeresis, "XK_odiaeresis", "ö" },
1645 { KSYM_udiaeresis, "XK_udiaeresis", "ü" },
1646 { KSYM_ssharp, "XK_ssharp", "sharp s" },
1648 #if defined(TARGET_SDL2)
1649 /* special (non-ASCII) keys (UTF-8, for reverse mapping only) */
1650 { KSYM_degree, "XK_degree", "\xc2\xb0" },
1651 { KSYM_Adiaeresis, "XK_Adiaeresis", "\xc3\x84" },
1652 { KSYM_Odiaeresis, "XK_Odiaeresis", "\xc3\x96" },
1653 { KSYM_Udiaeresis, "XK_Udiaeresis", "\xc3\x9c" },
1654 { KSYM_adiaeresis, "XK_adiaeresis", "\xc3\xa4" },
1655 { KSYM_odiaeresis, "XK_odiaeresis", "\xc3\xb6" },
1656 { KSYM_udiaeresis, "XK_udiaeresis", "\xc3\xbc" },
1657 { KSYM_ssharp, "XK_ssharp", "\xc3\x9f" },
1659 /* other keys (for reverse mapping only) */
1660 { KSYM_space, "XK_space", " " },
1663 #if defined(TARGET_SDL2)
1664 /* keypad keys are not in numerical order in SDL2 */
1665 { KSYM_KP_0, "XK_KP_0", "keypad 0" },
1666 { KSYM_KP_1, "XK_KP_1", "keypad 1" },
1667 { KSYM_KP_2, "XK_KP_2", "keypad 2" },
1668 { KSYM_KP_3, "XK_KP_3", "keypad 3" },
1669 { KSYM_KP_4, "XK_KP_4", "keypad 4" },
1670 { KSYM_KP_5, "XK_KP_5", "keypad 5" },
1671 { KSYM_KP_6, "XK_KP_6", "keypad 6" },
1672 { KSYM_KP_7, "XK_KP_7", "keypad 7" },
1673 { KSYM_KP_8, "XK_KP_8", "keypad 8" },
1674 { KSYM_KP_9, "XK_KP_9", "keypad 9" },
1677 /* end-of-array identifier */
1683 if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
1685 static char name_buffer[30];
1688 if (key >= KSYM_A && key <= KSYM_Z)
1689 sprintf(name_buffer, "%c", 'A' + (char)(key - KSYM_A));
1690 else if (key >= KSYM_a && key <= KSYM_z)
1691 sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
1692 else if (key >= KSYM_0 && key <= KSYM_9)
1693 sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
1694 #if !defined(TARGET_SDL2)
1695 else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
1696 sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
1698 else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
1699 sprintf(name_buffer, "F%d", (int)(key - KSYM_FKEY_FIRST + 1));
1700 else if (key == KSYM_UNDEFINED)
1701 strcpy(name_buffer, "(undefined)");
1708 if (key == translate_key[i].key)
1710 strcpy(name_buffer, translate_key[i].name);
1714 while (translate_key[++i].name);
1716 if (!translate_key[i].name)
1717 strcpy(name_buffer, "(unknown)");
1720 *name = name_buffer;
1722 else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
1724 static char name_buffer[30];
1727 if (key >= KSYM_A && key <= KSYM_Z)
1728 sprintf(name_buffer, "XK_%c", 'A' + (char)(key - KSYM_A));
1729 else if (key >= KSYM_a && key <= KSYM_z)
1730 sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
1731 else if (key >= KSYM_0 && key <= KSYM_9)
1732 sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
1733 #if !defined(TARGET_SDL2)
1734 else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
1735 sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
1737 else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
1738 sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_FKEY_FIRST + 1));
1739 else if (key == KSYM_UNDEFINED)
1740 strcpy(name_buffer, "[undefined]");
1747 if (key == translate_key[i].key)
1749 strcpy(name_buffer, translate_key[i].x11name);
1753 while (translate_key[++i].x11name);
1755 if (!translate_key[i].x11name)
1756 sprintf(name_buffer, "0x%04x", (unsigned int)key);
1759 *x11name = name_buffer;
1761 else if (mode == TRANSLATE_KEYNAME_TO_KEYSYM)
1763 Key key = KSYM_UNDEFINED;
1764 char *name_ptr = *name;
1766 if (strlen(*name) == 1)
1768 char c = name_ptr[0];
1770 if (c >= 'A' && c <= 'Z')
1771 key = KSYM_A + (Key)(c - 'A');
1772 else if (c >= 'a' && c <= 'z')
1773 key = KSYM_a + (Key)(c - 'a');
1774 else if (c >= '0' && c <= '9')
1775 key = KSYM_0 + (Key)(c - '0');
1778 if (key == KSYM_UNDEFINED)
1784 if (strEqual(translate_key[i].name, *name))
1786 key = translate_key[i].key;
1790 while (translate_key[++i].x11name);
1793 if (key == KSYM_UNDEFINED)
1794 Error(ERR_WARN, "getKeyFromKeyName(): not completely implemented");
1798 else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
1800 Key key = KSYM_UNDEFINED;
1801 char *name_ptr = *x11name;
1803 if (strPrefix(name_ptr, "XK_") && strlen(name_ptr) == 4)
1805 char c = name_ptr[3];
1807 if (c >= 'A' && c <= 'Z')
1808 key = KSYM_A + (Key)(c - 'A');
1809 else if (c >= 'a' && c <= 'z')
1810 key = KSYM_a + (Key)(c - 'a');
1811 else if (c >= '0' && c <= '9')
1812 key = KSYM_0 + (Key)(c - '0');
1814 #if !defined(TARGET_SDL2)
1815 else if (strPrefix(name_ptr, "XK_KP_") && strlen(name_ptr) == 7)
1817 char c = name_ptr[6];
1819 if (c >= '0' && c <= '9')
1820 key = KSYM_KP_0 + (Key)(c - '0');
1823 else if (strPrefix(name_ptr, "XK_F") && strlen(name_ptr) <= 6)
1825 char c1 = name_ptr[4];
1826 char c2 = name_ptr[5];
1829 if ((c1 >= '0' && c1 <= '9') &&
1830 ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
1831 d = atoi(&name_ptr[4]);
1833 if (d >= 1 && d <= KSYM_NUM_FKEYS)
1834 key = KSYM_F1 + (Key)(d - 1);
1836 else if (strPrefix(name_ptr, "XK_"))
1842 if (strEqual(name_ptr, translate_key[i].x11name))
1844 key = translate_key[i].key;
1848 while (translate_key[++i].x11name);
1850 else if (strPrefix(name_ptr, "0x"))
1852 unsigned int value = 0;
1858 char c = *name_ptr++;
1861 if (c >= '0' && c <= '9')
1863 else if (c >= 'a' && c <= 'f')
1864 d = (int)(c - 'a' + 10);
1865 else if (c >= 'A' && c <= 'F')
1866 d = (int)(c - 'A' + 10);
1874 value = value * 16 + d;
1885 char *getKeyNameFromKey(Key key)
1889 translate_keyname(&key, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
1893 char *getX11KeyNameFromKey(Key key)
1897 translate_keyname(&key, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
1901 Key getKeyFromKeyName(char *name)
1905 translate_keyname(&key, NULL, &name, TRANSLATE_KEYNAME_TO_KEYSYM);
1909 Key getKeyFromX11KeyName(char *x11name)
1913 translate_keyname(&key, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
1917 char getCharFromKey(Key key)
1919 char *keyname = getKeyNameFromKey(key);
1922 if (strlen(keyname) == 1)
1924 else if (strEqual(keyname, "space"))
1930 char getValidConfigValueChar(char c)
1932 if (c == '#' || /* used to mark comments */
1933 c == '\\') /* used to mark continued lines */
1940 /* ------------------------------------------------------------------------- */
1941 /* functions to translate string identifiers to integer or boolean value */
1942 /* ------------------------------------------------------------------------- */
1944 int get_integer_from_string(char *s)
1946 static char *number_text[][3] =
1948 { "0", "zero", "null", },
1949 { "1", "one", "first" },
1950 { "2", "two", "second" },
1951 { "3", "three", "third" },
1952 { "4", "four", "fourth" },
1953 { "5", "five", "fifth" },
1954 { "6", "six", "sixth" },
1955 { "7", "seven", "seventh" },
1956 { "8", "eight", "eighth" },
1957 { "9", "nine", "ninth" },
1958 { "10", "ten", "tenth" },
1959 { "11", "eleven", "eleventh" },
1960 { "12", "twelve", "twelfth" },
1962 { NULL, NULL, NULL },
1966 char *s_lower = getStringToLower(s);
1969 for (i = 0; number_text[i][0] != NULL; i++)
1970 for (j = 0; j < 3; j++)
1971 if (strEqual(s_lower, number_text[i][j]))
1976 if (strEqual(s_lower, "false") ||
1977 strEqual(s_lower, "no") ||
1978 strEqual(s_lower, "off"))
1980 else if (strEqual(s_lower, "true") ||
1981 strEqual(s_lower, "yes") ||
1982 strEqual(s_lower, "on"))
1993 boolean get_boolean_from_string(char *s)
1995 char *s_lower = getStringToLower(s);
1996 boolean result = FALSE;
1998 if (strEqual(s_lower, "true") ||
1999 strEqual(s_lower, "yes") ||
2000 strEqual(s_lower, "on") ||
2001 get_integer_from_string(s) == 1)
2009 int get_switch3_from_string(char *s)
2011 char *s_lower = getStringToLower(s);
2014 if (strEqual(s_lower, "true") ||
2015 strEqual(s_lower, "yes") ||
2016 strEqual(s_lower, "on") ||
2017 get_integer_from_string(s) == 1)
2019 else if (strEqual(s_lower, "auto"))
2028 /* ------------------------------------------------------------------------- */
2029 /* functions for generic lists */
2030 /* ------------------------------------------------------------------------- */
2032 ListNode *newListNode()
2034 return checked_calloc(sizeof(ListNode));
2037 void addNodeToList(ListNode **node_first, char *key, void *content)
2039 ListNode *node_new = newListNode();
2041 node_new->key = getStringCopy(key);
2042 node_new->content = content;
2043 node_new->next = *node_first;
2044 *node_first = node_new;
2047 void deleteNodeFromList(ListNode **node_first, char *key,
2048 void (*destructor_function)(void *))
2050 if (node_first == NULL || *node_first == NULL)
2053 if (strEqual((*node_first)->key, key))
2055 checked_free((*node_first)->key);
2056 if (destructor_function)
2057 destructor_function((*node_first)->content);
2058 *node_first = (*node_first)->next;
2061 deleteNodeFromList(&(*node_first)->next, key, destructor_function);
2064 ListNode *getNodeFromKey(ListNode *node_first, char *key)
2066 if (node_first == NULL)
2069 if (strEqual(node_first->key, key))
2072 return getNodeFromKey(node_first->next, key);
2075 int getNumNodes(ListNode *node_first)
2077 return (node_first ? 1 + getNumNodes(node_first->next) : 0);
2080 void dumpList(ListNode *node_first)
2082 ListNode *node = node_first;
2086 printf("['%s' (%d)]\n", node->key,
2087 ((struct ListNodeInfo *)node->content)->num_references);
2091 printf("[%d nodes]\n", getNumNodes(node_first));
2095 /* ------------------------------------------------------------------------- */
2096 /* functions for file handling */
2097 /* ------------------------------------------------------------------------- */
2099 File *openFile(char *filename, char *mode)
2101 File *file = checked_calloc(sizeof(File));
2103 file->file = fopen(filename, mode);
2105 if (file->file != NULL)
2107 file->filename = getStringCopy(filename);
2112 #if defined(PLATFORM_ANDROID)
2113 file->asset_file = SDL_RWFromFile(filename, mode);
2115 if (file->asset_file != NULL)
2117 file->file_is_asset = TRUE;
2118 file->filename = getStringCopy(filename);
2129 int closeFile(File *file)
2136 #if defined(PLATFORM_ANDROID)
2137 if (file->asset_file)
2138 result = SDL_RWclose(file->asset_file);
2142 result = fclose(file->file);
2144 checked_free(file->filename);
2150 int checkEndOfFile(File *file)
2152 #if defined(PLATFORM_ANDROID)
2153 if (file->file_is_asset)
2154 return file->end_of_file;
2157 return feof(file->file);
2160 size_t readFile(File *file, void *buffer, size_t item_size, size_t num_items)
2162 #if defined(PLATFORM_ANDROID)
2163 if (file->file_is_asset)
2165 if (file->end_of_file)
2168 size_t num_items_read =
2169 SDL_RWread(file->asset_file, buffer, item_size, num_items);
2171 if (num_items_read < num_items)
2172 file->end_of_file = TRUE;
2174 return num_items_read;
2178 return fread(buffer, item_size, num_items, file->file);
2181 int seekFile(File *file, long offset, int whence)
2183 #if defined(PLATFORM_ANDROID)
2184 if (file->file_is_asset)
2186 int sdl_whence = (whence == SEEK_SET ? RW_SEEK_SET :
2187 whence == SEEK_CUR ? RW_SEEK_CUR :
2188 whence == SEEK_END ? RW_SEEK_END : 0);
2190 return (SDL_RWseek(file->asset_file, offset, sdl_whence) == -1 ? -1 : 0);
2194 return fseek(file->file, offset, whence);
2197 int getByteFromFile(File *file)
2199 #if defined(PLATFORM_ANDROID)
2200 if (file->file_is_asset)
2202 if (file->end_of_file)
2206 size_t num_bytes_read = SDL_RWread(file->asset_file, &c, 1, 1);
2208 if (num_bytes_read < 1)
2209 file->end_of_file = TRUE;
2211 return (file->end_of_file ? EOF : (int)c);
2215 return fgetc(file->file);
2218 char *getStringFromFile(File *file, char *line, int size)
2220 #if defined(PLATFORM_ANDROID)
2221 if (file->file_is_asset)
2223 if (file->end_of_file)
2226 char *line_ptr = line;
2227 int num_bytes_read = 0;
2229 while (num_bytes_read < size - 1 &&
2230 SDL_RWread(file->asset_file, line_ptr, 1, 1) == 1 &&
2231 *line_ptr++ != '\n')
2236 if (strlen(line) == 0)
2238 file->end_of_file = TRUE;
2247 return fgets(line, size, file->file);
2251 /* ------------------------------------------------------------------------- */
2252 /* functions for directory handling */
2253 /* ------------------------------------------------------------------------- */
2255 Directory *openDirectory(char *dir_name)
2257 Directory *dir = checked_calloc(sizeof(Directory));
2259 dir->dir = opendir(dir_name);
2261 if (dir->dir != NULL)
2263 dir->filename = getStringCopy(dir_name);
2268 #if defined(PLATFORM_ANDROID)
2269 char *asset_toc_filename = getPath2(dir_name, ASSET_TOC_BASENAME);
2271 dir->asset_toc_file = SDL_RWFromFile(asset_toc_filename, MODE_READ);
2273 checked_free(asset_toc_filename);
2275 if (dir->asset_toc_file != NULL)
2277 dir->directory_is_asset = TRUE;
2278 dir->filename = getStringCopy(dir_name);
2289 int closeDirectory(Directory *dir)
2296 #if defined(PLATFORM_ANDROID)
2297 if (dir->asset_toc_file)
2298 result = SDL_RWclose(dir->asset_toc_file);
2302 result = closedir(dir->dir);
2305 freeDirectoryEntry(dir->dir_entry);
2307 checked_free(dir->filename);
2313 DirectoryEntry *readDirectory(Directory *dir)
2316 freeDirectoryEntry(dir->dir_entry);
2318 dir->dir_entry = NULL;
2320 #if defined(PLATFORM_ANDROID)
2321 if (dir->directory_is_asset)
2323 char line[MAX_LINE_LEN];
2324 char *line_ptr = line;
2325 int num_bytes_read = 0;
2327 while (num_bytes_read < MAX_LINE_LEN - 1 &&
2328 SDL_RWread(dir->asset_toc_file, line_ptr, 1, 1) == 1 &&
2337 if (strlen(line) == 0)
2340 dir->dir_entry = checked_calloc(sizeof(DirectoryEntry));
2342 dir->dir_entry->is_directory = FALSE;
2343 if (line[strlen(line) - 1] == '/')
2345 dir->dir_entry->is_directory = TRUE;
2347 line[strlen(line) - 1] = '\0';
2350 dir->dir_entry->basename = getStringCopy(line);
2351 dir->dir_entry->filename = getPath2(dir->filename, line);
2353 return dir->dir_entry;
2357 struct dirent *dir_entry = readdir(dir->dir);
2359 if (dir_entry == NULL)
2362 dir->dir_entry = checked_calloc(sizeof(DirectoryEntry));
2364 dir->dir_entry->basename = getStringCopy(dir_entry->d_name);
2365 dir->dir_entry->filename = getPath2(dir->filename, dir_entry->d_name);
2367 struct stat file_status;
2369 dir->dir_entry->is_directory =
2370 (stat(dir->dir_entry->filename, &file_status) == 0 &&
2371 (file_status.st_mode & S_IFMT) == S_IFDIR);
2374 Error(ERR_INFO, "::: '%s' is directory: %d",
2375 dir->dir_entry->basename,
2376 dir->dir_entry->is_directory);
2379 return dir->dir_entry;
2382 void freeDirectoryEntry(DirectoryEntry *dir_entry)
2384 if (dir_entry == NULL)
2387 checked_free(dir_entry->basename);
2388 checked_free(dir_entry->filename);
2389 checked_free(dir_entry);
2393 /* ------------------------------------------------------------------------- */
2394 /* functions for checking files and filenames */
2395 /* ------------------------------------------------------------------------- */
2397 boolean directoryExists(char *dir_name)
2399 if (dir_name == NULL)
2402 boolean success = (access(dir_name, F_OK) == 0);
2404 #if defined(PLATFORM_ANDROID)
2407 // this might be an asset directory; check by trying to open toc file
2408 char *asset_toc_filename = getPath2(dir_name, ASSET_TOC_BASENAME);
2409 SDL_RWops *file = SDL_RWFromFile(asset_toc_filename, MODE_READ);
2411 checked_free(asset_toc_filename);
2413 success = (file != NULL);
2423 boolean fileExists(char *filename)
2425 if (filename == NULL)
2428 boolean success = (access(filename, F_OK) == 0);
2430 #if defined(PLATFORM_ANDROID)
2433 // this might be an asset file; check by trying to open it
2434 SDL_RWops *file = SDL_RWFromFile(filename, MODE_READ);
2436 success = (file != NULL);
2446 boolean fileHasPrefix(char *basename, char *prefix)
2448 static char *basename_lower = NULL;
2449 int basename_length, prefix_length;
2451 checked_free(basename_lower);
2453 if (basename == NULL || prefix == NULL)
2456 basename_lower = getStringToLower(basename);
2457 basename_length = strlen(basename_lower);
2458 prefix_length = strlen(prefix);
2460 if (basename_length > prefix_length + 1 &&
2461 basename_lower[prefix_length] == '.' &&
2462 strncmp(basename_lower, prefix, prefix_length) == 0)
2468 boolean fileHasSuffix(char *basename, char *suffix)
2470 static char *basename_lower = NULL;
2471 int basename_length, suffix_length;
2473 checked_free(basename_lower);
2475 if (basename == NULL || suffix == NULL)
2478 basename_lower = getStringToLower(basename);
2479 basename_length = strlen(basename_lower);
2480 suffix_length = strlen(suffix);
2482 if (basename_length > suffix_length + 1 &&
2483 basename_lower[basename_length - suffix_length - 1] == '.' &&
2484 strEqual(&basename_lower[basename_length - suffix_length], suffix))
2490 #if defined(TARGET_SDL)
2491 static boolean FileCouldBeArtwork(char *basename)
2493 return (!strEqual(basename, ".") &&
2494 !strEqual(basename, "..") &&
2495 !fileHasSuffix(basename, "txt") &&
2496 !fileHasSuffix(basename, "conf"));
2500 boolean FileIsGraphic(char *filename)
2502 char *basename = getBaseNamePtr(filename);
2504 #if defined(TARGET_SDL)
2505 return FileCouldBeArtwork(basename);
2507 return fileHasSuffix(basename, "pcx");
2511 boolean FileIsSound(char *filename)
2513 char *basename = getBaseNamePtr(filename);
2515 #if defined(TARGET_SDL)
2516 return FileCouldBeArtwork(basename);
2518 return fileHasSuffix(basename, "wav");
2522 boolean FileIsMusic(char *filename)
2524 char *basename = getBaseNamePtr(filename);
2526 #if defined(TARGET_SDL)
2527 return FileCouldBeArtwork(basename);
2529 if (FileIsSound(basename))
2533 #if defined(TARGET_SDL)
2534 if ((fileHasPrefix(basename, "mod") && !fileHasSuffix(basename, "txt")) ||
2535 fileHasSuffix(basename, "mod") ||
2536 fileHasSuffix(basename, "s3m") ||
2537 fileHasSuffix(basename, "it") ||
2538 fileHasSuffix(basename, "xm") ||
2539 fileHasSuffix(basename, "midi") ||
2540 fileHasSuffix(basename, "mid") ||
2541 fileHasSuffix(basename, "mp3") ||
2542 fileHasSuffix(basename, "ogg"))
2551 boolean FileIsArtworkType(char *basename, int type)
2553 if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(basename)) ||
2554 (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(basename)) ||
2555 (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(basename)))
2561 /* ------------------------------------------------------------------------- */
2562 /* functions for loading artwork configuration information */
2563 /* ------------------------------------------------------------------------- */
2565 char *get_mapped_token(char *token)
2567 /* !!! make this dynamically configurable (init.c:InitArtworkConfig) !!! */
2568 static char *map_token_prefix[][2] =
2570 { "char_procent", "char_percent" },
2575 for (i = 0; map_token_prefix[i][0] != NULL; i++)
2577 int len_token_prefix = strlen(map_token_prefix[i][0]);
2579 if (strncmp(token, map_token_prefix[i][0], len_token_prefix) == 0)
2580 return getStringCat2(map_token_prefix[i][1], &token[len_token_prefix]);
2586 /* This function checks if a string <s> of the format "string1, string2, ..."
2587 exactly contains a string <s_contained>. */
2589 static boolean string_has_parameter(char *s, char *s_contained)
2593 if (s == NULL || s_contained == NULL)
2596 if (strlen(s_contained) > strlen(s))
2599 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
2601 char next_char = s[strlen(s_contained)];
2603 /* check if next character is delimiter or whitespace */
2604 return (next_char == ',' || next_char == '\0' ||
2605 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
2608 /* check if string contains another parameter string after a comma */
2609 substring = strchr(s, ',');
2610 if (substring == NULL) /* string does not contain a comma */
2613 /* advance string pointer to next character after the comma */
2616 /* skip potential whitespaces after the comma */
2617 while (*substring == ' ' || *substring == '\t')
2620 return string_has_parameter(substring, s_contained);
2623 int get_parameter_value(char *value_raw, char *suffix, int type)
2625 char *value = getStringToLower(value_raw);
2626 int result = 0; /* probably a save default value */
2628 if (strEqual(suffix, ".direction"))
2630 result = (strEqual(value, "left") ? MV_LEFT :
2631 strEqual(value, "right") ? MV_RIGHT :
2632 strEqual(value, "up") ? MV_UP :
2633 strEqual(value, "down") ? MV_DOWN : MV_NONE);
2635 else if (strEqual(suffix, ".align"))
2637 result = (strEqual(value, "left") ? ALIGN_LEFT :
2638 strEqual(value, "right") ? ALIGN_RIGHT :
2639 strEqual(value, "center") ? ALIGN_CENTER :
2640 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
2642 else if (strEqual(suffix, ".valign"))
2644 result = (strEqual(value, "top") ? VALIGN_TOP :
2645 strEqual(value, "bottom") ? VALIGN_BOTTOM :
2646 strEqual(value, "middle") ? VALIGN_MIDDLE :
2647 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
2649 else if (strEqual(suffix, ".anim_mode"))
2651 result = (string_has_parameter(value, "none") ? ANIM_NONE :
2652 string_has_parameter(value, "loop") ? ANIM_LOOP :
2653 string_has_parameter(value, "linear") ? ANIM_LINEAR :
2654 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
2655 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
2656 string_has_parameter(value, "random") ? ANIM_RANDOM :
2657 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
2658 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
2659 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
2660 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
2661 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
2662 string_has_parameter(value, "centered") ? ANIM_CENTERED :
2665 if (string_has_parameter(value, "reverse"))
2666 result |= ANIM_REVERSE;
2668 if (string_has_parameter(value, "opaque_player"))
2669 result |= ANIM_OPAQUE_PLAYER;
2671 if (string_has_parameter(value, "static_panel"))
2672 result |= ANIM_STATIC_PANEL;
2674 else if (strEqual(suffix, ".class"))
2676 result = get_hash_from_key(value);
2678 else if (strEqual(suffix, ".style"))
2680 result = STYLE_DEFAULT;
2682 if (string_has_parameter(value, "accurate_borders"))
2683 result |= STYLE_ACCURATE_BORDERS;
2685 if (string_has_parameter(value, "inner_corners"))
2686 result |= STYLE_INNER_CORNERS;
2688 else if (strEqual(suffix, ".fade_mode"))
2690 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
2691 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
2692 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
2693 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
2697 else if (strPrefix(suffix, ".font")) /* (may also be ".font_xyz") */
2699 else if (strEqualN(suffix, ".font", 5)) /* (may also be ".font_xyz") */
2702 result = gfx.get_font_from_token_function(value);
2704 else /* generic parameter of type integer or boolean */
2706 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
2707 type == TYPE_INTEGER ? get_integer_from_string(value) :
2708 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
2709 ARG_UNDEFINED_VALUE);
2717 struct ScreenModeInfo *get_screen_mode_from_string(char *screen_mode_string)
2719 static struct ScreenModeInfo screen_mode;
2720 char *screen_mode_string_x = strchr(screen_mode_string, 'x');
2721 char *screen_mode_string_copy;
2722 char *screen_mode_string_pos_w;
2723 char *screen_mode_string_pos_h;
2725 if (screen_mode_string_x == NULL) /* invalid screen mode format */
2728 screen_mode_string_copy = getStringCopy(screen_mode_string);
2730 screen_mode_string_pos_w = screen_mode_string_copy;
2731 screen_mode_string_pos_h = strchr(screen_mode_string_copy, 'x');
2732 *screen_mode_string_pos_h++ = '\0';
2734 screen_mode.width = atoi(screen_mode_string_pos_w);
2735 screen_mode.height = atoi(screen_mode_string_pos_h);
2737 return &screen_mode;
2740 void get_aspect_ratio_from_screen_mode(struct ScreenModeInfo *screen_mode,
2743 float aspect_ratio = (float)screen_mode->width / (float)screen_mode->height;
2744 float aspect_ratio_new;
2749 *x = i * aspect_ratio + 0.000001;
2752 aspect_ratio_new = (float)*x / (float)*y;
2756 while (aspect_ratio_new != aspect_ratio && *y < screen_mode->height);
2759 static void FreeCustomArtworkList(struct ArtworkListInfo *,
2760 struct ListNodeInfo ***, int *);
2762 struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list,
2763 struct ConfigTypeInfo *suffix_list,
2764 char **ignore_tokens,
2765 int num_file_list_entries)
2767 struct FileInfo *file_list;
2768 int num_file_list_entries_found = 0;
2769 int num_suffix_list_entries = 0;
2773 file_list = checked_calloc(num_file_list_entries * sizeof(struct FileInfo));
2775 for (i = 0; suffix_list[i].token != NULL; i++)
2776 num_suffix_list_entries++;
2778 /* always start with reliable default values */
2779 for (i = 0; i < num_file_list_entries; i++)
2781 file_list[i].token = NULL;
2783 file_list[i].default_filename = NULL;
2784 file_list[i].filename = NULL;
2786 if (num_suffix_list_entries > 0)
2788 int parameter_array_size = num_suffix_list_entries * sizeof(char *);
2790 file_list[i].default_parameter = checked_calloc(parameter_array_size);
2791 file_list[i].parameter = checked_calloc(parameter_array_size);
2793 for (j = 0; j < num_suffix_list_entries; j++)
2795 setString(&file_list[i].default_parameter[j], suffix_list[j].value);
2796 setString(&file_list[i].parameter[j], suffix_list[j].value);
2799 file_list[i].redefined = FALSE;
2800 file_list[i].fallback_to_default = FALSE;
2801 file_list[i].default_is_cloned = FALSE;
2807 for (i = 0; config_list[i].token != NULL; i++)
2809 int len_config_token = strlen(config_list[i].token);
2811 int len_config_value = strlen(config_list[i].value);
2813 boolean is_file_entry = TRUE;
2815 for (j = 0; suffix_list[j].token != NULL; j++)
2817 int len_suffix = strlen(suffix_list[j].token);
2819 if (len_suffix < len_config_token &&
2820 strEqual(&config_list[i].token[len_config_token - len_suffix],
2821 suffix_list[j].token))
2823 setString(&file_list[list_pos].default_parameter[j],
2824 config_list[i].value);
2826 is_file_entry = FALSE;
2832 /* the following tokens are no file definitions, but other config tokens */
2833 for (j = 0; ignore_tokens[j] != NULL; j++)
2834 if (strEqual(config_list[i].token, ignore_tokens[j]))
2835 is_file_entry = FALSE;
2842 if (list_pos >= num_file_list_entries)
2846 /* simple sanity check if this is really a file definition */
2847 if (!strEqual(&config_list[i].value[len_config_value - 4], ".pcx") &&
2848 !strEqual(&config_list[i].value[len_config_value - 4], ".wav") &&
2849 !strEqual(config_list[i].value, UNDEFINED_FILENAME))
2851 Error(ERR_INFO, "Configuration directive '%s' -> '%s':",
2852 config_list[i].token, config_list[i].value);
2853 Error(ERR_EXIT, "This seems to be no valid definition -- please fix");
2857 file_list[list_pos].token = config_list[i].token;
2858 file_list[list_pos].default_filename = config_list[i].value;
2861 printf("::: '%s' => '%s'\n", config_list[i].token, config_list[i].value);
2865 if (strSuffix(config_list[i].token, ".clone_from"))
2866 file_list[list_pos].default_is_cloned = TRUE;
2869 num_file_list_entries_found = list_pos + 1;
2870 if (num_file_list_entries_found != num_file_list_entries)
2872 Error(ERR_INFO_LINE, "-");
2873 Error(ERR_INFO, "inconsistant config list information:");
2874 Error(ERR_INFO, "- should be: %d (according to 'src/conf_xxx.h')",
2875 num_file_list_entries);
2876 Error(ERR_INFO, "- found to be: %d (according to 'src/conf_xxx.c')",
2877 num_file_list_entries_found);
2878 Error(ERR_EXIT, "please fix");
2882 printf("::: ---------- DONE ----------\n");
2888 static boolean token_suffix_match(char *token, char *suffix, int start_pos)
2890 int len_token = strlen(token);
2891 int len_suffix = strlen(suffix);
2893 if (start_pos < 0) /* compare suffix from end of string */
2894 start_pos += len_token;
2896 if (start_pos < 0 || start_pos + len_suffix > len_token)
2899 if (strncmp(&token[start_pos], suffix, len_suffix) != 0)
2902 if (token[start_pos + len_suffix] == '\0')
2905 if (token[start_pos + len_suffix] == '.')
2911 #define KNOWN_TOKEN_VALUE "[KNOWN_TOKEN_VALUE]"
2913 static void read_token_parameters(SetupFileHash *setup_file_hash,
2914 struct ConfigTypeInfo *suffix_list,
2915 struct FileInfo *file_list_entry)
2917 /* check for config token that is the base token without any suffixes */
2918 char *filename = getHashEntry(setup_file_hash, file_list_entry->token);
2919 char *known_token_value = KNOWN_TOKEN_VALUE;
2922 if (filename != NULL)
2924 setString(&file_list_entry->filename, filename);
2926 /* when file definition found, set all parameters to default values */
2927 for (i = 0; suffix_list[i].token != NULL; i++)
2928 setString(&file_list_entry->parameter[i], suffix_list[i].value);
2930 file_list_entry->redefined = TRUE;
2932 /* mark config file token as well known from default config */
2933 setHashEntry(setup_file_hash, file_list_entry->token, known_token_value);
2936 /* check for config tokens that can be build by base token and suffixes */
2937 for (i = 0; suffix_list[i].token != NULL; i++)
2939 char *token = getStringCat2(file_list_entry->token, suffix_list[i].token);
2940 char *value = getHashEntry(setup_file_hash, token);
2944 setString(&file_list_entry->parameter[i], value);
2946 /* mark config file token as well known from default config */
2947 setHashEntry(setup_file_hash, token, known_token_value);
2954 static void add_dynamic_file_list_entry(struct FileInfo **list,
2955 int *num_list_entries,
2956 SetupFileHash *extra_file_hash,
2957 struct ConfigTypeInfo *suffix_list,
2958 int num_suffix_list_entries,
2961 struct FileInfo *new_list_entry;
2962 int parameter_array_size = num_suffix_list_entries * sizeof(char *);
2964 (*num_list_entries)++;
2965 *list = checked_realloc(*list, *num_list_entries * sizeof(struct FileInfo));
2966 new_list_entry = &(*list)[*num_list_entries - 1];
2968 new_list_entry->token = getStringCopy(token);
2969 new_list_entry->default_filename = NULL;
2970 new_list_entry->filename = NULL;
2971 new_list_entry->parameter = checked_calloc(parameter_array_size);
2973 new_list_entry->redefined = FALSE;
2974 new_list_entry->fallback_to_default = FALSE;
2975 new_list_entry->default_is_cloned = FALSE;
2977 read_token_parameters(extra_file_hash, suffix_list, new_list_entry);
2980 static void add_property_mapping(struct PropertyMapping **list,
2981 int *num_list_entries,
2982 int base_index, int ext1_index,
2983 int ext2_index, int ext3_index,
2986 struct PropertyMapping *new_list_entry;
2988 (*num_list_entries)++;
2989 *list = checked_realloc(*list,
2990 *num_list_entries * sizeof(struct PropertyMapping));
2991 new_list_entry = &(*list)[*num_list_entries - 1];
2993 new_list_entry->base_index = base_index;
2994 new_list_entry->ext1_index = ext1_index;
2995 new_list_entry->ext2_index = ext2_index;
2996 new_list_entry->ext3_index = ext3_index;
2998 new_list_entry->artwork_index = artwork_index;
3001 static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
3004 struct FileInfo *file_list = artwork_info->file_list;
3005 struct ConfigTypeInfo *suffix_list = artwork_info->suffix_list;
3006 char **base_prefixes = artwork_info->base_prefixes;
3007 char **ext1_suffixes = artwork_info->ext1_suffixes;
3008 char **ext2_suffixes = artwork_info->ext2_suffixes;
3009 char **ext3_suffixes = artwork_info->ext3_suffixes;
3010 char **ignore_tokens = artwork_info->ignore_tokens;
3011 int num_file_list_entries = artwork_info->num_file_list_entries;
3012 int num_suffix_list_entries = artwork_info->num_suffix_list_entries;
3013 int num_base_prefixes = artwork_info->num_base_prefixes;
3014 int num_ext1_suffixes = artwork_info->num_ext1_suffixes;
3015 int num_ext2_suffixes = artwork_info->num_ext2_suffixes;
3016 int num_ext3_suffixes = artwork_info->num_ext3_suffixes;
3017 int num_ignore_tokens = artwork_info->num_ignore_tokens;
3018 SetupFileHash *setup_file_hash, *valid_file_hash;
3019 SetupFileHash *extra_file_hash, *empty_file_hash;
3020 char *known_token_value = KNOWN_TOKEN_VALUE;
3023 if (filename == NULL)
3027 printf("LoadArtworkConfigFromFilename '%s' ...\n", filename);
3030 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3033 /* separate valid (defined) from empty (undefined) config token values */
3034 valid_file_hash = newSetupFileHash();
3035 empty_file_hash = newSetupFileHash();
3036 BEGIN_HASH_ITERATION(setup_file_hash, itr)
3038 char *value = HASH_ITERATION_VALUE(itr);
3040 setHashEntry(*value ? valid_file_hash : empty_file_hash,
3041 HASH_ITERATION_TOKEN(itr), value);
3043 END_HASH_ITERATION(setup_file_hash, itr)
3045 /* at this point, we do not need the setup file hash anymore -- free it */
3046 freeSetupFileHash(setup_file_hash);
3048 /* map deprecated to current tokens (using prefix match and replace) */
3049 BEGIN_HASH_ITERATION(valid_file_hash, itr)
3051 char *token = HASH_ITERATION_TOKEN(itr);
3052 char *mapped_token = get_mapped_token(token);
3054 if (mapped_token != NULL)
3056 char *value = HASH_ITERATION_VALUE(itr);
3058 /* add mapped token */
3059 setHashEntry(valid_file_hash, mapped_token, value);
3061 /* ignore old token (by setting it to "known" keyword) */
3062 setHashEntry(valid_file_hash, token, known_token_value);
3067 END_HASH_ITERATION(valid_file_hash, itr)
3069 /* read parameters for all known config file tokens */
3070 for (i = 0; i < num_file_list_entries; i++)
3071 read_token_parameters(valid_file_hash, suffix_list, &file_list[i]);
3073 /* set all tokens that can be ignored here to "known" keyword */
3074 for (i = 0; i < num_ignore_tokens; i++)
3075 setHashEntry(valid_file_hash, ignore_tokens[i], known_token_value);
3077 /* copy all unknown config file tokens to extra config hash */
3078 extra_file_hash = newSetupFileHash();
3079 BEGIN_HASH_ITERATION(valid_file_hash, itr)
3081 char *value = HASH_ITERATION_VALUE(itr);
3083 if (!strEqual(value, known_token_value))
3084 setHashEntry(extra_file_hash, HASH_ITERATION_TOKEN(itr), value);
3086 END_HASH_ITERATION(valid_file_hash, itr)
3088 /* at this point, we do not need the valid file hash anymore -- free it */
3089 freeSetupFileHash(valid_file_hash);
3091 /* now try to determine valid, dynamically defined config tokens */
3093 BEGIN_HASH_ITERATION(extra_file_hash, itr)
3095 struct FileInfo **dynamic_file_list =
3096 &artwork_info->dynamic_file_list;
3097 int *num_dynamic_file_list_entries =
3098 &artwork_info->num_dynamic_file_list_entries;
3099 struct PropertyMapping **property_mapping =
3100 &artwork_info->property_mapping;
3101 int *num_property_mapping_entries =
3102 &artwork_info->num_property_mapping_entries;
3103 int current_summarized_file_list_entry =
3104 artwork_info->num_file_list_entries +
3105 artwork_info->num_dynamic_file_list_entries;
3106 char *token = HASH_ITERATION_TOKEN(itr);
3107 int len_token = strlen(token);
3109 boolean base_prefix_found = FALSE;
3110 boolean parameter_suffix_found = FALSE;
3113 printf("::: examining '%s' -> '%s'\n", token, HASH_ITERATION_VALUE(itr));
3116 /* skip all parameter definitions (handled by read_token_parameters()) */
3117 for (i = 0; i < num_suffix_list_entries && !parameter_suffix_found; i++)
3119 int len_suffix = strlen(suffix_list[i].token);
3121 if (token_suffix_match(token, suffix_list[i].token, -len_suffix))
3122 parameter_suffix_found = TRUE;
3125 if (parameter_suffix_found)
3128 /* ---------- step 0: search for matching base prefix ---------- */
3131 for (i = 0; i < num_base_prefixes && !base_prefix_found; i++)
3133 char *base_prefix = base_prefixes[i];
3134 int len_base_prefix = strlen(base_prefix);
3135 boolean ext1_suffix_found = FALSE;
3136 boolean ext2_suffix_found = FALSE;
3137 boolean ext3_suffix_found = FALSE;
3138 boolean exact_match = FALSE;
3139 int base_index = -1;
3140 int ext1_index = -1;
3141 int ext2_index = -1;
3142 int ext3_index = -1;
3144 base_prefix_found = token_suffix_match(token, base_prefix, start_pos);
3146 if (!base_prefix_found)
3152 if (IS_PARENT_PROCESS())
3153 printf("===> MATCH: '%s', '%s'\n", token, base_prefix);
3156 if (start_pos + len_base_prefix == len_token) /* exact match */
3161 if (IS_PARENT_PROCESS())
3162 printf("===> EXACT MATCH: '%s', '%s'\n", token, base_prefix);
3165 add_dynamic_file_list_entry(dynamic_file_list,
3166 num_dynamic_file_list_entries,
3169 num_suffix_list_entries,
3171 add_property_mapping(property_mapping,
3172 num_property_mapping_entries,
3173 base_index, -1, -1, -1,
3174 current_summarized_file_list_entry);
3179 if (IS_PARENT_PROCESS())
3180 printf("---> examining token '%s': search 1st suffix ...\n", token);
3183 /* ---------- step 1: search for matching first suffix ---------- */
3185 start_pos += len_base_prefix;
3186 for (j = 0; j < num_ext1_suffixes && !ext1_suffix_found; j++)
3188 char *ext1_suffix = ext1_suffixes[j];
3189 int len_ext1_suffix = strlen(ext1_suffix);
3191 ext1_suffix_found = token_suffix_match(token, ext1_suffix, start_pos);
3193 if (!ext1_suffix_found)
3199 if (IS_PARENT_PROCESS())
3200 printf("===> MATCH: '%s', '%s'\n", token, ext1_suffix);
3203 if (start_pos + len_ext1_suffix == len_token) /* exact match */
3208 if (IS_PARENT_PROCESS())
3209 printf("===> EXACT MATCH: '%s', '%s'\n", token, ext1_suffix);
3212 add_dynamic_file_list_entry(dynamic_file_list,
3213 num_dynamic_file_list_entries,
3216 num_suffix_list_entries,
3218 add_property_mapping(property_mapping,
3219 num_property_mapping_entries,
3220 base_index, ext1_index, -1, -1,
3221 current_summarized_file_list_entry);
3225 start_pos += len_ext1_suffix;
3232 if (IS_PARENT_PROCESS())
3233 printf("---> examining token '%s': search 2nd suffix ...\n", token);
3236 /* ---------- step 2: search for matching second suffix ---------- */
3238 for (k = 0; k < num_ext2_suffixes && !ext2_suffix_found; k++)
3240 char *ext2_suffix = ext2_suffixes[k];
3241 int len_ext2_suffix = strlen(ext2_suffix);
3243 ext2_suffix_found = token_suffix_match(token, ext2_suffix, start_pos);
3245 if (!ext2_suffix_found)
3251 if (IS_PARENT_PROCESS())
3252 printf("===> MATCH: '%s', '%s'\n", token, ext2_suffix);
3255 if (start_pos + len_ext2_suffix == len_token) /* exact match */
3260 if (IS_PARENT_PROCESS())
3261 printf("===> EXACT MATCH: '%s', '%s'\n", token, ext2_suffix);
3264 add_dynamic_file_list_entry(dynamic_file_list,
3265 num_dynamic_file_list_entries,
3268 num_suffix_list_entries,
3270 add_property_mapping(property_mapping,
3271 num_property_mapping_entries,
3272 base_index, ext1_index, ext2_index, -1,
3273 current_summarized_file_list_entry);
3277 start_pos += len_ext2_suffix;
3284 if (IS_PARENT_PROCESS())
3285 printf("---> examining token '%s': search 3rd suffix ...\n",token);
3288 /* ---------- step 3: search for matching third suffix ---------- */
3290 for (l = 0; l < num_ext3_suffixes && !ext3_suffix_found; l++)
3292 char *ext3_suffix = ext3_suffixes[l];
3293 int len_ext3_suffix = strlen(ext3_suffix);
3295 ext3_suffix_found = token_suffix_match(token, ext3_suffix, start_pos);
3297 if (!ext3_suffix_found)
3303 if (IS_PARENT_PROCESS())
3304 printf("===> MATCH: '%s', '%s'\n", token, ext3_suffix);
3307 if (start_pos + len_ext3_suffix == len_token) /* exact match */
3312 if (IS_PARENT_PROCESS())
3313 printf("===> EXACT MATCH: '%s', '%s'\n", token, ext3_suffix);
3316 add_dynamic_file_list_entry(dynamic_file_list,
3317 num_dynamic_file_list_entries,
3320 num_suffix_list_entries,
3322 add_property_mapping(property_mapping,
3323 num_property_mapping_entries,
3324 base_index, ext1_index, ext2_index, ext3_index,
3325 current_summarized_file_list_entry);
3331 END_HASH_ITERATION(extra_file_hash, itr)
3333 if (artwork_info->num_dynamic_file_list_entries > 0)
3335 artwork_info->dynamic_artwork_list =
3336 checked_calloc(artwork_info->num_dynamic_file_list_entries *
3337 artwork_info->sizeof_artwork_list_entry);
3340 if (options.verbose && IS_PARENT_PROCESS())
3342 SetupFileList *setup_file_list, *list;
3343 boolean dynamic_tokens_found = FALSE;
3344 boolean unknown_tokens_found = FALSE;
3345 boolean undefined_values_found = (hashtable_count(empty_file_hash) != 0);
3347 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3348 Error(ERR_EXIT, "loadSetupFileHash works, but loadSetupFileList fails");
3350 BEGIN_HASH_ITERATION(extra_file_hash, itr)
3352 if (strEqual(HASH_ITERATION_VALUE(itr), known_token_value))
3353 dynamic_tokens_found = TRUE;
3355 unknown_tokens_found = TRUE;
3357 END_HASH_ITERATION(extra_file_hash, itr)
3359 if (options.debug && dynamic_tokens_found)
3361 Error(ERR_INFO_LINE, "-");
3362 Error(ERR_INFO, "dynamic token(s) found in config file:");
3363 Error(ERR_INFO, "- config file: '%s'", filename);
3365 for (list = setup_file_list; list != NULL; list = list->next)
3367 char *value = getHashEntry(extra_file_hash, list->token);
3369 if (value != NULL && strEqual(value, known_token_value))
3370 Error(ERR_INFO, "- dynamic token: '%s'", list->token);
3373 Error(ERR_INFO_LINE, "-");
3376 if (unknown_tokens_found)
3378 Error(ERR_INFO_LINE, "-");
3379 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
3380 Error(ERR_INFO, "- config file: '%s'", filename);
3382 for (list = setup_file_list; list != NULL; list = list->next)
3384 char *value = getHashEntry(extra_file_hash, list->token);
3386 if (value != NULL && !strEqual(value, known_token_value))
3387 Error(ERR_INFO, "- dynamic token: '%s'", list->token);
3390 Error(ERR_INFO_LINE, "-");
3393 if (undefined_values_found)
3395 Error(ERR_INFO_LINE, "-");
3396 Error(ERR_INFO, "warning: undefined values found in config file:");
3397 Error(ERR_INFO, "- config file: '%s'", filename);
3399 for (list = setup_file_list; list != NULL; list = list->next)
3401 char *value = getHashEntry(empty_file_hash, list->token);
3404 Error(ERR_INFO, "- undefined value for token: '%s'", list->token);
3407 Error(ERR_INFO_LINE, "-");
3410 freeSetupFileList(setup_file_list);
3413 freeSetupFileHash(extra_file_hash);
3414 freeSetupFileHash(empty_file_hash);
3417 for (i = 0; i < num_file_list_entries; i++)
3419 printf("'%s' ", file_list[i].token);
3420 if (file_list[i].filename)
3421 printf("-> '%s'\n", file_list[i].filename);
3423 printf("-> UNDEFINED [-> '%s']\n", file_list[i].default_filename);
3428 void LoadArtworkConfig(struct ArtworkListInfo *artwork_info)
3430 struct FileInfo *file_list = artwork_info->file_list;
3431 int num_file_list_entries = artwork_info->num_file_list_entries;
3432 int num_suffix_list_entries = artwork_info->num_suffix_list_entries;
3433 char *filename_base = UNDEFINED_FILENAME, *filename_local;
3436 DrawInitText("Loading artwork config", 120, FC_GREEN);
3437 DrawInitText(ARTWORKINFO_FILENAME(artwork_info->type), 150, FC_YELLOW);
3439 /* always start with reliable default values */
3440 for (i = 0; i < num_file_list_entries; i++)
3442 setString(&file_list[i].filename, file_list[i].default_filename);
3444 for (j = 0; j < num_suffix_list_entries; j++)
3445 setString(&file_list[i].parameter[j], file_list[i].default_parameter[j]);
3447 file_list[i].redefined = FALSE;
3448 file_list[i].fallback_to_default = FALSE;
3451 /* free previous dynamic artwork file array */
3452 if (artwork_info->dynamic_file_list != NULL)
3454 for (i = 0; i < artwork_info->num_dynamic_file_list_entries; i++)
3456 free(artwork_info->dynamic_file_list[i].token);
3457 free(artwork_info->dynamic_file_list[i].filename);
3458 free(artwork_info->dynamic_file_list[i].parameter);
3461 free(artwork_info->dynamic_file_list);
3462 artwork_info->dynamic_file_list = NULL;
3464 FreeCustomArtworkList(artwork_info, &artwork_info->dynamic_artwork_list,
3465 &artwork_info->num_dynamic_file_list_entries);
3468 /* free previous property mapping */
3469 if (artwork_info->property_mapping != NULL)
3471 free(artwork_info->property_mapping);
3473 artwork_info->property_mapping = NULL;
3474 artwork_info->num_property_mapping_entries = 0;
3478 if (!GFX_OVERRIDE_ARTWORK(artwork_info->type))
3480 if (!SETUP_OVERRIDE_ARTWORK(setup, artwork_info->type))
3483 /* first look for special artwork configured in level series config */
3484 filename_base = getCustomArtworkLevelConfigFilename(artwork_info->type);
3487 printf("::: filename_base == '%s' [%s, %s]\n", filename_base,
3488 leveldir_current->graphics_set,
3489 leveldir_current->graphics_path);
3492 if (fileExists(filename_base))
3493 LoadArtworkConfigFromFilename(artwork_info, filename_base);
3496 filename_local = getCustomArtworkConfigFilename(artwork_info->type);
3498 if (filename_local != NULL && !strEqual(filename_base, filename_local))
3499 LoadArtworkConfigFromFilename(artwork_info, filename_local);
3502 static void deleteArtworkListEntry(struct ArtworkListInfo *artwork_info,
3503 struct ListNodeInfo **listnode)
3507 char *filename = (*listnode)->source_filename;
3509 if (--(*listnode)->num_references <= 0)
3510 deleteNodeFromList(&artwork_info->content_list, filename,
3511 artwork_info->free_artwork);
3517 static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
3518 struct ListNodeInfo **listnode,
3519 struct FileInfo *file_list_entry)
3529 char *basename = file_list_entry->filename;
3530 char *filename = getCustomArtworkFilename(basename, artwork_info->type);
3532 if (filename == NULL)
3534 Error(ERR_WARN, "cannot find artwork file '%s'", basename);
3536 basename = file_list_entry->default_filename;
3538 /* fail for cloned default artwork that has no default filename defined */
3539 if (file_list_entry->default_is_cloned &&
3540 strEqual(basename, UNDEFINED_FILENAME))
3542 int error_mode = ERR_WARN;
3544 /* we can get away without sounds and music, but not without graphics */
3545 if (*listnode == NULL && artwork_info->type == ARTWORK_TYPE_GRAPHICS)
3546 error_mode = ERR_EXIT;
3548 Error(error_mode, "token '%s' was cloned and has no default filename",
3549 file_list_entry->token);
3554 /* dynamic artwork has no default filename / skip empty default artwork */
3555 if (basename == NULL || strEqual(basename, UNDEFINED_FILENAME))
3558 file_list_entry->fallback_to_default = TRUE;
3560 Error(ERR_WARN, "trying default artwork file '%s'", basename);
3562 filename = getCustomArtworkFilename(basename, artwork_info->type);
3564 if (filename == NULL)
3566 int error_mode = ERR_WARN;
3568 /* we can get away without sounds and music, but not without graphics */
3569 if (*listnode == NULL && artwork_info->type == ARTWORK_TYPE_GRAPHICS)
3570 error_mode = ERR_EXIT;
3572 Error(error_mode, "cannot find default artwork file '%s'", basename);
3578 /* check if the old and the new artwork file are the same */
3579 if (*listnode && strEqual((*listnode)->source_filename, filename))
3581 /* The old and new artwork are the same (have the same filename and path).
3582 This usually means that this artwork does not exist in this artwork set
3583 and a fallback to the existing artwork is done. */
3586 printf("[artwork '%s' already exists (same list entry)]\n", filename);
3592 /* delete existing artwork file entry */
3593 deleteArtworkListEntry(artwork_info, listnode);
3595 /* check if the new artwork file already exists in the list of artworks */
3596 if ((node = getNodeFromKey(artwork_info->content_list, filename)) != NULL)
3599 printf("[artwork '%s' already exists (other list entry)]\n", filename);
3602 *listnode = (struct ListNodeInfo *)node->content;
3603 (*listnode)->num_references++;
3608 DrawInitText(init_text[artwork_info->type], 120, FC_GREEN);
3609 DrawInitText(basename, 150, FC_YELLOW);
3611 if ((*listnode = artwork_info->load_artwork(filename)) != NULL)
3614 printf("[adding new artwork '%s']\n", filename);
3617 (*listnode)->num_references = 1;
3618 addNodeToList(&artwork_info->content_list, (*listnode)->source_filename,
3623 int error_mode = ERR_WARN;
3625 /* we can get away without sounds and music, but not without graphics */
3626 if (artwork_info->type == ARTWORK_TYPE_GRAPHICS)
3627 error_mode = ERR_EXIT;
3629 Error(error_mode, "cannot load artwork file '%s'", basename);
3635 static void LoadCustomArtwork(struct ArtworkListInfo *artwork_info,
3636 struct ListNodeInfo **listnode,
3637 struct FileInfo *file_list_entry)
3640 printf("GOT CUSTOM ARTWORK FILE '%s'\n", file_list_entry->filename);
3643 if (strEqual(file_list_entry->filename, UNDEFINED_FILENAME))
3645 deleteArtworkListEntry(artwork_info, listnode);
3650 replaceArtworkListEntry(artwork_info, listnode, file_list_entry);
3653 void ReloadCustomArtworkList(struct ArtworkListInfo *artwork_info)
3655 struct FileInfo *file_list = artwork_info->file_list;
3656 struct FileInfo *dynamic_file_list = artwork_info->dynamic_file_list;
3657 int num_file_list_entries = artwork_info->num_file_list_entries;
3658 int num_dynamic_file_list_entries =
3659 artwork_info->num_dynamic_file_list_entries;
3662 print_timestamp_init("ReloadCustomArtworkList");
3664 for (i = 0; i < num_file_list_entries; i++)
3665 LoadCustomArtwork(artwork_info, &artwork_info->artwork_list[i],
3668 for (i = 0; i < num_dynamic_file_list_entries; i++)
3669 LoadCustomArtwork(artwork_info, &artwork_info->dynamic_artwork_list[i],
3670 &dynamic_file_list[i]);
3672 print_timestamp_done("ReloadCustomArtworkList");
3675 dumpList(artwork_info->content_list);
3679 static void FreeCustomArtworkList(struct ArtworkListInfo *artwork_info,
3680 struct ListNodeInfo ***list,
3681 int *num_list_entries)
3688 for (i = 0; i < *num_list_entries; i++)
3689 deleteArtworkListEntry(artwork_info, &(*list)[i]);
3693 *num_list_entries = 0;
3696 void FreeCustomArtworkLists(struct ArtworkListInfo *artwork_info)
3698 if (artwork_info == NULL)
3701 FreeCustomArtworkList(artwork_info, &artwork_info->artwork_list,
3702 &artwork_info->num_file_list_entries);
3704 FreeCustomArtworkList(artwork_info, &artwork_info->dynamic_artwork_list,
3705 &artwork_info->num_dynamic_file_list_entries);
3709 /* ------------------------------------------------------------------------- */
3710 /* functions only needed for non-Unix (non-command-line) systems */
3711 /* (MS-DOS only; SDL/Windows creates files "stdout.txt" and "stderr.txt") */
3712 /* (now also added for Windows, to create files in user data directory) */
3713 /* ------------------------------------------------------------------------- */
3715 char *getErrorFilename(char *basename)
3717 return getPath2(getUserGameDataDir(), basename);
3720 void openErrorFile()
3722 InitUserDataDirectory();
3724 if ((program.error_file = fopen(program.error_filename, MODE_WRITE)) == NULL)
3726 program.error_file = stderr;
3728 Error(ERR_WARN, "cannot open file '%s' for writing: %s",
3729 program.error_filename, strerror(errno));
3733 void closeErrorFile()
3735 if (program.error_file != stderr) /* do not close stream 'stderr' */
3736 fclose(program.error_file);
3739 void dumpErrorFile()
3741 FILE *error_file = fopen(program.error_filename, MODE_READ);
3743 if (error_file != NULL)
3745 while (!feof(error_file))
3746 fputc(fgetc(error_file), stderr);
3752 void NotifyUserAboutErrorFile()
3754 #if defined(PLATFORM_WIN32)
3755 char *title_text = getStringCat2(program.program_title, " Error Message");
3756 char *error_text = getStringCat2("The program was aborted due to an error; "
3757 "for details, see the following error file:"
3758 STRING_NEWLINE, program.error_filename);
3760 MessageBox(NULL, error_text, title_text, MB_OK);
3765 /* ------------------------------------------------------------------------- */
3766 /* the following is only for debugging purpose and normally not used */
3767 /* ------------------------------------------------------------------------- */
3771 #define DEBUG_PRINT_INIT_TIMESTAMPS FALSE
3772 #define DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH 10
3774 #define DEBUG_NUM_TIMESTAMPS 10
3775 #define DEBUG_TIME_IN_MICROSECONDS 0
3777 #if DEBUG_TIME_IN_MICROSECONDS
3778 static double Counter_Microseconds()
3780 static struct timeval base_time = { 0, 0 };
3781 struct timeval current_time;
3784 gettimeofday(¤t_time, NULL);
3786 /* reset base time in case of wrap-around */
3787 if (current_time.tv_sec < base_time.tv_sec)
3788 base_time = current_time;
3791 ((double)(current_time.tv_sec - base_time.tv_sec)) * 1000000 +
3792 ((double)(current_time.tv_usec - base_time.tv_usec));
3794 return counter; /* return microseconds since last init */
3798 char *debug_print_timestamp_get_padding(int padding_size)
3800 static char *padding = NULL;
3801 int max_padding_size = 100;
3803 if (padding == NULL)
3805 padding = checked_calloc(max_padding_size + 1);
3806 memset(padding, ' ', max_padding_size);
3809 return &padding[MAX(0, max_padding_size - padding_size)];
3812 void debug_print_timestamp(int counter_nr, char *message)
3814 int indent_size = 8;
3815 int padding_size = 40;
3816 float timestamp_interval;
3819 Error(ERR_EXIT, "debugging: invalid negative counter");
3820 else if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
3821 Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
3823 #if DEBUG_TIME_IN_MICROSECONDS
3824 static double counter[DEBUG_NUM_TIMESTAMPS][2];
3827 counter[counter_nr][0] = Counter_Microseconds();
3829 static int counter[DEBUG_NUM_TIMESTAMPS][2];
3832 counter[counter_nr][0] = Counter();
3835 timestamp_interval = counter[counter_nr][0] - counter[counter_nr][1];
3836 counter[counter_nr][1] = counter[counter_nr][0];
3840 Error(ERR_DEBUG, "%s%s%s %.3f %s",
3842 printf("%s%s%s %.3f %s\n",
3844 debug_print_timestamp_get_padding(counter_nr * indent_size),
3846 debug_print_timestamp_get_padding(padding_size - strlen(message)),
3847 timestamp_interval / 1000,
3851 void debug_print_parent_only(char *format, ...)
3853 if (!IS_PARENT_PROCESS())
3860 va_start(ap, format);
3861 vprintf(format, ap);
3870 void print_timestamp_ext(char *message, char *mode)
3872 #if DEBUG_PRINT_INIT_TIMESTAMPS
3873 static char *debug_message = NULL;
3874 static char *last_message = NULL;
3875 static int counter_nr = 0;
3876 int max_depth = DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH;
3878 checked_free(debug_message);
3879 debug_message = getStringCat3(mode, " ", message);
3881 if (strEqual(mode, "INIT"))
3883 debug_print_timestamp(counter_nr, NULL);
3885 if (counter_nr + 1 < max_depth)
3886 debug_print_timestamp(counter_nr, debug_message);
3890 debug_print_timestamp(counter_nr, NULL);
3892 else if (strEqual(mode, "DONE"))
3896 if (counter_nr + 1 < max_depth ||
3897 (counter_nr == 0 && max_depth == 1))
3899 last_message = message;
3901 if (counter_nr == 0 && max_depth == 1)
3903 checked_free(debug_message);
3904 debug_message = getStringCat3("TIME", " ", message);
3907 debug_print_timestamp(counter_nr, debug_message);
3910 else if (!strEqual(mode, "TIME") ||
3911 !strEqual(message, last_message))
3913 if (counter_nr < max_depth)
3914 debug_print_timestamp(counter_nr, debug_message);
3919 void print_timestamp_init(char *message)
3921 print_timestamp_ext(message, "INIT");
3924 void print_timestamp_time(char *message)
3926 print_timestamp_ext(message, "TIME");
3929 void print_timestamp_done(char *message)
3931 print_timestamp_ext(message, "DONE");