rnd-20060726-5-src
[rocksndiamonds.git] / src / libgame / misc.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * misc.c                                                   *
12 ***********************************************************/
13
14 #include <time.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <stdarg.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #include "platform.h"
23
24 #if !defined(PLATFORM_WIN32)
25 #include <pwd.h>
26 #include <sys/param.h>
27 #endif
28
29 #include "misc.h"
30 #include "setup.h"
31 #include "random.h"
32 #include "text.h"
33 #include "image.h"
34
35
36 /* ========================================================================= */
37 /* some generic helper functions                                             */
38 /* ========================================================================= */
39
40 /* ------------------------------------------------------------------------- */
41 /* platform independent wrappers for printf() et al. (newline aware)         */
42 /* ------------------------------------------------------------------------- */
43
44 static void vfprintf_newline(FILE *stream, char *format, va_list ap)
45 {
46 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MSDOS)
47   char *newline = "\r\n";
48 #else
49   char *newline = "\n";
50 #endif
51
52   vfprintf(stream, format, ap);
53
54   fprintf(stream, "%s", newline);
55 }
56
57 static void vprintf_error_ext(char *format, va_list ap, boolean print_newline)
58 {
59   FILE *error = stderr;
60
61 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MSDOS)
62   if ((error = openErrorFile()) == NULL)
63     program.exit_function(1);
64 #endif
65
66   if (print_newline)
67     vfprintf_newline(error, format, ap);
68   else
69     vfprintf(error, format, ap);
70
71   if (error != stderr)
72     fclose(error);
73 }
74
75 static void vprintf_error(char *format, va_list ap)
76 {
77   vprintf_error_ext(format, ap, FALSE);
78 }
79
80 static void vprintf_error_newline(char *format, va_list ap)
81 {
82   vprintf_error_ext(format, ap, TRUE);
83 }
84
85 static void printf_error(char *format, ...)
86 {
87   if (format)
88   {
89     va_list ap;
90
91     va_start(ap, format);
92     vprintf_error(format, ap);
93     va_end(ap);
94   }
95 }
96
97 static void printf_error_newline(char *format, ...)
98 {
99   if (format)
100   {
101     va_list ap;
102
103     va_start(ap, format);
104     vprintf_error_newline(format, ap);
105     va_end(ap);
106   }
107 }
108
109 static void fprintf_newline(FILE *stream, char *format, ...)
110 {
111   if (format)
112   {
113     va_list ap;
114
115     va_start(ap, format);
116     vfprintf_newline(stream, format, ap);
117     va_end(ap);
118   }
119 }
120
121 static char *get_line_string(char *line_chars, int line_length)
122 {
123   static char *buffer = NULL;
124   int line_chars_length = strlen(line_chars);
125   int i;
126
127   if (buffer != NULL)
128     checked_free(buffer);
129
130   buffer = checked_malloc(line_chars_length * line_length + 1);
131
132   for (i = 0; i < line_length; i++)
133     strcpy(&buffer[i * line_chars_length], line_chars);
134
135   return buffer;
136 }
137
138 void fprintf_line(FILE *stream, char *line_chars, int line_length)
139 {
140   fprintf_newline(stream, get_line_string(line_chars, line_length));
141 }
142
143 void printf_line(char *line_chars, int line_length)
144 {
145   fprintf_line(stdout, line_chars, line_length);
146 }
147
148 void printf_line_error(char *line_chars, int line_length)
149 {
150   printf_error_newline(get_line_string(line_chars, line_length));
151 }
152
153 void printf_line_with_prefix(char *prefix, char *line_chars, int line_length)
154 {
155   fprintf(stdout, "%s", prefix);
156   fprintf_line(stdout, line_chars, line_length);
157 }
158
159
160 /* ------------------------------------------------------------------------- */
161 /* string functions                                                          */
162 /* ------------------------------------------------------------------------- */
163
164 /* int2str() returns a number converted to a string;
165    the used memory is static, but will be overwritten by later calls,
166    so if you want to save the result, copy it to a private string buffer;
167    there can be 10 local calls of int2str() without buffering the result --
168    the 11th call will then destroy the result from the first call and so on.
169 */
170
171 char *int2str(int number, int size)
172 {
173   static char shift_array[10][40];
174   static int shift_counter = 0;
175   char *s = shift_array[shift_counter];
176
177   shift_counter = (shift_counter + 1) % 10;
178
179   if (size > 20)
180     size = 20;
181
182   if (size)
183   {
184     sprintf(s, "                    %09d", number);
185     return &s[strlen(s) - size];
186   }
187   else
188   {
189     sprintf(s, "%d", number);
190     return s;
191   }
192 }
193
194
195 /* something similar to "int2str()" above, but allocates its own memory
196    and has a different interface; we cannot use "itoa()", because this
197    seems to be already defined when cross-compiling to the win32 target */
198
199 char *i_to_a(unsigned int i)
200 {
201   static char *a = NULL;
202
203   checked_free(a);
204
205   if (i > 2147483647)   /* yes, this is a kludge */
206     i = 2147483647;
207
208   a = checked_malloc(10 + 1);
209
210   sprintf(a, "%d", i);
211
212   return a;
213 }
214
215
216 /* calculate base-2 logarithm of argument (rounded down to integer;
217    this function returns the number of the highest bit set in argument) */
218
219 int log_2(unsigned int x)
220 {
221   int e = 0;
222
223   while ((1 << e) < x)
224   {
225     x -= (1 << e);      /* for rounding down (rounding up: remove this line) */
226     e++;
227   }
228
229   return e;
230 }
231
232
233 /* ------------------------------------------------------------------------- */
234 /* counter functions                                                         */
235 /* ------------------------------------------------------------------------- */
236
237 #if defined(PLATFORM_MSDOS)
238 volatile unsigned long counter = 0;
239
240 void increment_counter()
241 {
242   counter++;
243 }
244
245 END_OF_FUNCTION(increment_counter);
246 #endif
247
248
249 /* maximal allowed length of a command line option */
250 #define MAX_OPTION_LEN          256
251
252 #ifdef TARGET_SDL
253 static unsigned long mainCounter(int mode)
254 {
255   static unsigned long base_ms = 0;
256   unsigned long current_ms;
257   unsigned long counter_ms;
258
259   current_ms = SDL_GetTicks();
260
261   /* reset base time in case of counter initializing or wrap-around */
262   if (mode == INIT_COUNTER || current_ms < base_ms)
263     base_ms = current_ms;
264
265   counter_ms = current_ms - base_ms;
266
267   return counter_ms;            /* return milliseconds since last init */
268 }
269
270 #else /* !TARGET_SDL */
271
272 #if defined(PLATFORM_UNIX)
273 static unsigned long mainCounter(int mode)
274 {
275   static struct timeval base_time = { 0, 0 };
276   struct timeval current_time;
277   unsigned long counter_ms;
278
279   gettimeofday(&current_time, NULL);
280
281   /* reset base time in case of counter initializing or wrap-around */
282   if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
283     base_time = current_time;
284
285   counter_ms = (current_time.tv_sec  - base_time.tv_sec)  * 1000
286              + (current_time.tv_usec - base_time.tv_usec) / 1000;
287
288   return counter_ms;            /* return milliseconds since last init */
289 }
290 #endif /* PLATFORM_UNIX */
291 #endif /* !TARGET_SDL */
292
293 void InitCounter()              /* set counter back to zero */
294 {
295 #if !defined(PLATFORM_MSDOS)
296   mainCounter(INIT_COUNTER);
297 #else
298   LOCK_VARIABLE(counter);
299   LOCK_FUNCTION(increment_counter);
300   install_int_ex(increment_counter, BPS_TO_TIMER(100));
301 #endif
302 }
303
304 unsigned long Counter() /* get milliseconds since last call of InitCounter() */
305 {
306 #if !defined(PLATFORM_MSDOS)
307   return mainCounter(READ_COUNTER);
308 #else
309   return (counter * 10);
310 #endif
311 }
312
313 static void sleep_milliseconds(unsigned long milliseconds_delay)
314 {
315   boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
316
317   if (do_busy_waiting)
318   {
319     /* we want to wait only a few ms -- if we assume that we have a
320        kernel timer resolution of 10 ms, we would wait far to long;
321        therefore it's better to do a short interval of busy waiting
322        to get our sleeping time more accurate */
323
324     unsigned long base_counter = Counter(), actual_counter = Counter();
325
326     while (actual_counter < base_counter + milliseconds_delay &&
327            actual_counter >= base_counter)
328       actual_counter = Counter();
329   }
330   else
331   {
332 #if defined(TARGET_SDL)
333     SDL_Delay(milliseconds_delay);
334 #elif defined(TARGET_ALLEGRO)
335     rest(milliseconds_delay);
336 #else
337     struct timeval delay;
338
339     delay.tv_sec  = milliseconds_delay / 1000;
340     delay.tv_usec = 1000 * (milliseconds_delay % 1000);
341
342     if (select(0, NULL, NULL, NULL, &delay) != 0)
343       Error(ERR_WARN, "sleep_milliseconds(): select() failed");
344 #endif
345   }
346 }
347
348 void Delay(unsigned long delay) /* Sleep specified number of milliseconds */
349 {
350   sleep_milliseconds(delay);
351 }
352
353 boolean FrameReached(unsigned long *frame_counter_var,
354                      unsigned long frame_delay)
355 {
356   unsigned long actual_frame_counter = FrameCounter;
357
358   if (actual_frame_counter >= *frame_counter_var &&
359       actual_frame_counter < *frame_counter_var + frame_delay)
360     return FALSE;
361
362   *frame_counter_var = actual_frame_counter;
363
364   return TRUE;
365 }
366
367 boolean DelayReached(unsigned long *counter_var,
368                      unsigned long delay)
369 {
370   unsigned long actual_counter = Counter();
371
372   if (actual_counter >= *counter_var &&
373       actual_counter < *counter_var + delay)
374     return FALSE;
375
376   *counter_var = actual_counter;
377
378   return TRUE;
379 }
380
381 void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
382 {
383   unsigned long actual_counter;
384
385   while (1)
386   {
387     actual_counter = Counter();
388
389     if (actual_counter >= *counter_var &&
390         actual_counter < *counter_var + delay)
391       sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
392     else
393       break;
394   }
395
396   *counter_var = actual_counter;
397 }
398
399
400 /* ------------------------------------------------------------------------- */
401 /* random generator functions                                                */
402 /* ------------------------------------------------------------------------- */
403
404 unsigned int init_random_number(int nr, long seed)
405 {
406   if (seed == NEW_RANDOMIZE)
407   {
408 #if defined(TARGET_SDL)
409     seed = (long)SDL_GetTicks();
410 #else
411     struct timeval current_time;
412
413     gettimeofday(&current_time, NULL);
414     seed = (long)current_time.tv_usec;
415 #endif
416   }
417
418   srandom_linux_libc(nr, (unsigned int) seed);
419
420   return (unsigned int) seed;
421 }
422
423 unsigned int get_random_number(int nr, int max)
424 {
425   return (max > 0 ? random_linux_libc(nr) % max : 0);
426 }
427
428
429 /* ------------------------------------------------------------------------- */
430 /* system info functions                                                     */
431 /* ------------------------------------------------------------------------- */
432
433 #if !defined(PLATFORM_MSDOS)
434 static char *get_corrected_real_name(char *real_name)
435 {
436   char *real_name_new = checked_malloc(MAX_USERNAME_LEN + 1);
437   char *from_ptr = real_name;
438   char *to_ptr   = real_name_new;
439
440   /* copy the name string, but not more than MAX_USERNAME_LEN characters */
441   while (*from_ptr && (long)(to_ptr - real_name_new) < MAX_USERNAME_LEN - 1)
442   {
443     /* the name field read from "passwd" file may also contain additional
444        user information, separated by commas, which will be removed here */
445     if (*from_ptr == ',')
446       break;
447
448     /* the user's real name may contain 'ß' characters (german sharp s),
449        which have no equivalent in upper case letters (used by our fonts) */
450     if (*from_ptr == 'ß')
451     {
452       from_ptr++;
453       *to_ptr++ = 's';
454       *to_ptr++ = 's';
455     }
456     else
457       *to_ptr++ = *from_ptr++;
458   }
459
460   *to_ptr = '\0';
461
462   return real_name_new;
463 }
464 #endif
465
466 char *getLoginName()
467 {
468   static char *login_name = NULL;
469
470 #if defined(PLATFORM_WIN32)
471   if (login_name == NULL)
472   {
473     unsigned long buffer_size = MAX_USERNAME_LEN + 1;
474     login_name = checked_malloc(buffer_size);
475
476     if (GetUserName(login_name, &buffer_size) == 0)
477       strcpy(login_name, ANONYMOUS_NAME);
478   }
479 #else
480   if (login_name == NULL)
481   {
482     struct passwd *pwd;
483
484     if ((pwd = getpwuid(getuid())) == NULL)
485       login_name = ANONYMOUS_NAME;
486     else
487       login_name = getStringCopy(pwd->pw_name);
488   }
489 #endif
490
491   return login_name;
492 }
493
494 char *getRealName()
495 {
496   static char *real_name = NULL;
497
498 #if defined(PLATFORM_WIN32)
499   if (real_name == NULL)
500   {
501     static char buffer[MAX_USERNAME_LEN + 1];
502     unsigned long buffer_size = MAX_USERNAME_LEN + 1;
503
504     if (GetUserName(buffer, &buffer_size) != 0)
505       real_name = get_corrected_real_name(buffer);
506     else
507       real_name = ANONYMOUS_NAME;
508   }
509 #elif defined(PLATFORM_UNIX)
510   if (real_name == NULL)
511   {
512     struct passwd *pwd;
513
514     if ((pwd = getpwuid(getuid())) != NULL && strlen(pwd->pw_gecos) != 0)
515       real_name = get_corrected_real_name(pwd->pw_gecos);
516     else
517       real_name = ANONYMOUS_NAME;
518   }
519 #else
520   real_name = ANONYMOUS_NAME;
521 #endif
522
523   return real_name;
524 }
525
526 char *getHomeDir()
527 {
528   static char *dir = NULL;
529
530 #if defined(PLATFORM_WIN32)
531   if (dir == NULL)
532   {
533     dir = checked_malloc(MAX_PATH + 1);
534
535     if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir)))
536       strcpy(dir, ".");
537   }
538 #elif defined(PLATFORM_UNIX)
539   if (dir == NULL)
540   {
541     if ((dir = getenv("HOME")) == NULL)
542     {
543       struct passwd *pwd;
544
545       if ((pwd = getpwuid(getuid())) != NULL)
546         dir = getStringCopy(pwd->pw_dir);
547       else
548         dir = ".";
549     }
550   }
551 #else
552   dir = ".";
553 #endif
554
555   return dir;
556 }
557
558
559 /* ------------------------------------------------------------------------- */
560 /* path manipulation functions                                               */
561 /* ------------------------------------------------------------------------- */
562
563 static char *getLastPathSeparatorPtr(char *filename)
564 {
565   char *last_separator = strrchr(filename, '/');
566
567 #if !defined(PLATFORM_UNIX)
568   if (last_separator == NULL)   /* also try DOS/Windows variant */
569     last_separator = strrchr(filename, '\\');
570 #endif
571
572   return last_separator;
573 }
574
575 char *getBaseNamePtr(char *filename)
576 {
577   char *last_separator = getLastPathSeparatorPtr(filename);
578
579   if (last_separator != NULL)
580     return last_separator + 1;  /* separator found: strip base path */
581   else
582     return filename;            /* no separator found: filename has no path */
583 }
584
585 char *getBaseName(char *filename)
586 {
587   return getStringCopy(getBaseNamePtr(filename));
588 }
589
590 char *getBasePath(char *filename)
591 {
592   char *basepath = getStringCopy(filename);
593   char *last_separator = getLastPathSeparatorPtr(basepath);
594
595   if (last_separator != NULL)
596     *last_separator = '\0';     /* separator found: strip basename */
597   else
598     basepath = ".";             /* no separator found: use current path */
599
600   return basepath;
601 }
602
603
604 /* ------------------------------------------------------------------------- */
605 /* various string functions                                                  */
606 /* ------------------------------------------------------------------------- */
607
608 char *getPath2(char *path1, char *path2)
609 {
610   char *complete_path = checked_malloc(strlen(path1) + 1 +
611                                        strlen(path2) + 1);
612
613   sprintf(complete_path, "%s/%s", path1, path2);
614
615   return complete_path;
616 }
617
618 char *getPath3(char *path1, char *path2, char *path3)
619 {
620   char *complete_path = checked_malloc(strlen(path1) + 1 +
621                                        strlen(path2) + 1 +
622                                        strlen(path3) + 1);
623
624   sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
625
626   return complete_path;
627 }
628
629 char *getStringCat2(char *s1, char *s2)
630 {
631   char *complete_string = checked_malloc(strlen(s1) + strlen(s2) + 1);
632
633   sprintf(complete_string, "%s%s", s1, s2);
634
635   return complete_string;
636 }
637
638 char *getStringCopy(char *s)
639 {
640   char *s_copy;
641
642   if (s == NULL)
643     return NULL;
644
645   s_copy = checked_malloc(strlen(s) + 1);
646   strcpy(s_copy, s);
647
648   return s_copy;
649 }
650
651 char *getStringToLower(char *s)
652 {
653   char *s_copy = checked_malloc(strlen(s) + 1);
654   char *s_ptr = s_copy;
655
656   while (*s)
657     *s_ptr++ = tolower(*s++);
658   *s_ptr = '\0';
659
660   return s_copy;
661 }
662
663 void setString(char **old_value, char *new_value)
664 {
665   checked_free(*old_value);
666
667   *old_value = getStringCopy(new_value);
668 }
669
670 boolean strEqual(char *s1, char *s2)
671 {
672   return (s1 == NULL && s2 == NULL ? TRUE  :
673           s1 == NULL && s2 != NULL ? FALSE :
674           s1 != NULL && s2 == NULL ? FALSE :
675           strcmp(s1, s2) == 0);
676 }
677
678
679 /* ------------------------------------------------------------------------- */
680 /* command line option handling functions                                    */
681 /* ------------------------------------------------------------------------- */
682
683 void GetOptions(char *argv[], void (*print_usage_function)(void))
684 {
685   char *ro_base_path = RO_BASE_PATH;
686   char *rw_base_path = RW_BASE_PATH;
687   char **options_left = &argv[1];
688
689 #if !defined(PLATFORM_MACOSX)
690   /* if the program is configured to start from current directory (default),
691      determine program package directory (KDE/Konqueror does not do this by
692      itself and fails otherwise); on Mac OS X, the program binary is stored
693      in an application package directory -- do not try to use this directory
694      as the program data directory (Mac OS X handles this correctly anyway) */
695
696   if (strEqual(ro_base_path, "."))
697     ro_base_path = program.command_basepath;
698   if (strEqual(rw_base_path, "."))
699     rw_base_path = program.command_basepath;
700 #endif
701
702   /* initialize global program options */
703   options.display_name = NULL;
704   options.server_host = NULL;
705   options.server_port = 0;
706   options.ro_base_directory = ro_base_path;
707   options.rw_base_directory = rw_base_path;
708   options.level_directory    = getPath2(ro_base_path, LEVELS_DIRECTORY);
709   options.graphics_directory = getPath2(ro_base_path, GRAPHICS_DIRECTORY);
710   options.sounds_directory   = getPath2(ro_base_path, SOUNDS_DIRECTORY);
711   options.music_directory    = getPath2(ro_base_path, MUSIC_DIRECTORY);
712   options.docs_directory     = getPath2(ro_base_path, DOCS_DIRECTORY);
713   options.execute_command = NULL;
714   options.serveronly = FALSE;
715   options.network = FALSE;
716   options.verbose = FALSE;
717   options.debug = FALSE;
718
719 #if !defined(PLATFORM_UNIX)
720   if (*options_left == NULL)    /* no options given -- enable verbose mode */
721     options.verbose = TRUE;
722 #endif
723
724   while (*options_left)
725   {
726     char option_str[MAX_OPTION_LEN];
727     char *option = options_left[0];
728     char *next_option = options_left[1];
729     char *option_arg = NULL;
730     int option_len = strlen(option);
731
732     if (option_len >= MAX_OPTION_LEN)
733       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
734
735     strcpy(option_str, option);                 /* copy argument into buffer */
736     option = option_str;
737
738     if (strEqual(option, "--"))                 /* stop scanning arguments */
739       break;
740
741     if (strncmp(option, "--", 2) == 0)          /* treat '--' like '-' */
742       option++;
743
744     option_arg = strchr(option, '=');
745     if (option_arg == NULL)                     /* no '=' in option */
746       option_arg = next_option;
747     else
748     {
749       *option_arg++ = '\0';                     /* cut argument from option */
750       if (*option_arg == '\0')                  /* no argument after '=' */
751         Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
752     }
753
754     option_len = strlen(option);
755
756     if (strEqual(option, "-"))
757       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
758     else if (strncmp(option, "-help", option_len) == 0)
759     {
760       print_usage_function();
761
762       exit(0);
763     }
764     else if (strncmp(option, "-display", option_len) == 0)
765     {
766       if (option_arg == NULL)
767         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
768
769       options.display_name = option_arg;
770       if (option_arg == next_option)
771         options_left++;
772     }
773     else if (strncmp(option, "-basepath", option_len) == 0)
774     {
775       if (option_arg == NULL)
776         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
777
778       /* this should be extended to separate options for ro and rw data */
779       options.ro_base_directory = ro_base_path = option_arg;
780       options.rw_base_directory = rw_base_path = option_arg;
781       if (option_arg == next_option)
782         options_left++;
783
784       /* adjust paths for sub-directories in base directory accordingly */
785       options.level_directory    = getPath2(ro_base_path, LEVELS_DIRECTORY);
786       options.graphics_directory = getPath2(ro_base_path, GRAPHICS_DIRECTORY);
787       options.sounds_directory   = getPath2(ro_base_path, SOUNDS_DIRECTORY);
788       options.music_directory    = getPath2(ro_base_path, MUSIC_DIRECTORY);
789       options.docs_directory     = getPath2(ro_base_path, DOCS_DIRECTORY);
790     }
791     else if (strncmp(option, "-levels", option_len) == 0)
792     {
793       if (option_arg == NULL)
794         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
795
796       options.level_directory = option_arg;
797       if (option_arg == next_option)
798         options_left++;
799     }
800     else if (strncmp(option, "-graphics", option_len) == 0)
801     {
802       if (option_arg == NULL)
803         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
804
805       options.graphics_directory = option_arg;
806       if (option_arg == next_option)
807         options_left++;
808     }
809     else if (strncmp(option, "-sounds", option_len) == 0)
810     {
811       if (option_arg == NULL)
812         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
813
814       options.sounds_directory = option_arg;
815       if (option_arg == next_option)
816         options_left++;
817     }
818     else if (strncmp(option, "-music", option_len) == 0)
819     {
820       if (option_arg == NULL)
821         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
822
823       options.music_directory = option_arg;
824       if (option_arg == next_option)
825         options_left++;
826     }
827     else if (strncmp(option, "-network", option_len) == 0)
828     {
829       options.network = TRUE;
830     }
831     else if (strncmp(option, "-serveronly", option_len) == 0)
832     {
833       options.serveronly = TRUE;
834     }
835     else if (strncmp(option, "-verbose", option_len) == 0)
836     {
837       options.verbose = TRUE;
838     }
839     else if (strncmp(option, "-debug", option_len) == 0)
840     {
841       options.debug = TRUE;
842     }
843     else if (strncmp(option, "-execute", option_len) == 0)
844     {
845       if (option_arg == NULL)
846         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
847
848       options.execute_command = option_arg;
849       if (option_arg == next_option)
850         options_left++;
851
852       /* when doing batch processing, always enable verbose mode (warnings) */
853       options.verbose = TRUE;
854     }
855 #if 1
856 #if DEBUG
857 #if defined(TARGET_SDL)
858     else if (strncmp(option, "-SDL_ListModes", option_len) == 0)
859     {
860       SDL_Rect **modes;
861       int i;
862
863       SDL_Init(SDL_INIT_VIDEO);
864
865       /* get available fullscreen/hardware modes */
866       modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
867
868       /* check if there are any modes available */
869       if (modes == (SDL_Rect **)0)
870       {
871         printf("No modes available!\n");
872
873         exit(-1);
874       }
875
876       /* check if our resolution is restricted */
877       if (modes == (SDL_Rect **)-1)
878       {
879         printf("All resolutions available.\n");
880       }
881       else
882       {
883         /* print valid modes */
884         printf("Available Modes:\n");
885         for(i = 0; modes[i]; i++)
886           printf("  %d x %d\n", modes[i]->w, modes[i]->h);
887       }
888
889       exit(0);
890     }
891 #endif
892 #endif
893 #endif
894     else if (*option == '-')
895     {
896       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
897     }
898     else if (options.server_host == NULL)
899     {
900       options.server_host = *options_left;
901     }
902     else if (options.server_port == 0)
903     {
904       options.server_port = atoi(*options_left);
905       if (options.server_port < 1024)
906         Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
907     }
908     else
909       Error(ERR_EXIT_HELP, "too many arguments");
910
911     options_left++;
912   }
913 }
914
915
916 /* ------------------------------------------------------------------------- */
917 /* error handling functions                                                  */
918 /* ------------------------------------------------------------------------- */
919
920 /* used by SetError() and GetError() to store internal error messages */
921 static char internal_error[1024];       /* this is bad */
922
923 void SetError(char *format, ...)
924 {
925   va_list ap;
926
927   va_start(ap, format);
928   vsprintf(internal_error, format, ap);
929   va_end(ap);
930 }
931
932 char *GetError()
933 {
934   return internal_error;
935 }
936
937 #if 1
938
939 void Error(int mode, char *format, ...)
940 {
941   static boolean last_line_was_separator = FALSE;
942   char *process_name = "";
943
944   /* display warnings only when running in verbose mode */
945   if (mode & ERR_WARN && !options.verbose)
946     return;
947
948   if (mode == ERR_RETURN_LINE)
949   {
950     if (!last_line_was_separator)
951       printf_line_error(format, 79);
952
953     last_line_was_separator = TRUE;
954
955     return;
956   }
957
958   last_line_was_separator = FALSE;
959
960   if (mode & ERR_SOUND_SERVER)
961     process_name = " sound server";
962   else if (mode & ERR_NETWORK_SERVER)
963     process_name = " network server";
964   else if (mode & ERR_NETWORK_CLIENT)
965     process_name = " network client **";
966
967   if (format)
968   {
969     va_list ap;
970
971     printf_error("%s%s: ", program.command_basename, process_name);
972
973     if (mode & ERR_WARN)
974       printf_error("warning: ");
975
976     va_start(ap, format);
977     vprintf_error_newline(format, ap);
978     va_end(ap);
979   }
980   
981   if (mode & ERR_HELP)
982     printf_error_newline("%s: Try option '--help' for more information.",
983                          program.command_basename);
984
985   if (mode & ERR_EXIT)
986     printf_error_newline("%s%s: aborting",
987                          program.command_basename, process_name);
988
989   if (mode & ERR_EXIT)
990   {
991     if (mode & ERR_FROM_SERVER)
992       exit(1);                          /* child process: normal exit */
993     else
994       program.exit_function(1);         /* main process: clean up stuff */
995   }
996 }
997
998 #else
999
1000 void Error(int mode, char *format, ...)
1001 {
1002   static boolean last_line_was_separator = FALSE;
1003   char *process_name = "";
1004   FILE *error = stderr;
1005   char *newline = "\n";
1006
1007   /* display warnings only when running in verbose mode */
1008   if (mode & ERR_WARN && !options.verbose)
1009     return;
1010
1011   if (mode == ERR_RETURN_LINE)
1012   {
1013     if (!last_line_was_separator)
1014       fprintf_line(error, format, 79);
1015
1016     last_line_was_separator = TRUE;
1017
1018     return;
1019   }
1020
1021   last_line_was_separator = FALSE;
1022
1023 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MSDOS)
1024   newline = "\r\n";
1025
1026   if ((error = openErrorFile()) == NULL)
1027   {
1028     printf("Cannot write to error output file!%s", newline);
1029
1030     program.exit_function(1);
1031   }
1032 #endif
1033
1034   if (mode & ERR_SOUND_SERVER)
1035     process_name = " sound server";
1036   else if (mode & ERR_NETWORK_SERVER)
1037     process_name = " network server";
1038   else if (mode & ERR_NETWORK_CLIENT)
1039     process_name = " network client **";
1040
1041   if (format)
1042   {
1043     va_list ap;
1044
1045     fprintf(error, "%s%s: ", program.command_basename, process_name);
1046
1047     if (mode & ERR_WARN)
1048       fprintf(error, "warning: ");
1049
1050     va_start(ap, format);
1051     vfprintf(error, format, ap);
1052     va_end(ap);
1053   
1054     fprintf(error, "%s", newline);
1055   }
1056   
1057   if (mode & ERR_HELP)
1058     fprintf(error, "%s: Try option '--help' for more information.%s",
1059             program.command_basename, newline);
1060
1061   if (mode & ERR_EXIT)
1062     fprintf(error, "%s%s: aborting%s",
1063             program.command_basename, process_name, newline);
1064
1065   if (error != stderr)
1066     fclose(error);
1067
1068   if (mode & ERR_EXIT)
1069   {
1070     if (mode & ERR_FROM_SERVER)
1071       exit(1);                          /* child process: normal exit */
1072     else
1073       program.exit_function(1);         /* main process: clean up stuff */
1074   }
1075 }
1076
1077 #endif
1078
1079
1080 /* ------------------------------------------------------------------------- */
1081 /* checked memory allocation and freeing functions                           */
1082 /* ------------------------------------------------------------------------- */
1083
1084 void *checked_malloc(unsigned long size)
1085 {
1086   void *ptr;
1087
1088   ptr = malloc(size);
1089
1090   if (ptr == NULL)
1091     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
1092
1093   return ptr;
1094 }
1095
1096 void *checked_calloc(unsigned long size)
1097 {
1098   void *ptr;
1099
1100   ptr = calloc(1, size);
1101
1102   if (ptr == NULL)
1103     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
1104
1105   return ptr;
1106 }
1107
1108 void *checked_realloc(void *ptr, unsigned long size)
1109 {
1110   ptr = realloc(ptr, size);
1111
1112   if (ptr == NULL)
1113     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
1114
1115   return ptr;
1116 }
1117
1118 void checked_free(void *ptr)
1119 {
1120   if (ptr != NULL)      /* this check should be done by free() anyway */
1121     free(ptr);
1122 }
1123
1124
1125 /* ------------------------------------------------------------------------- */
1126 /* various helper functions                                                  */
1127 /* ------------------------------------------------------------------------- */
1128
1129 inline void swap_numbers(int *i1, int *i2)
1130 {
1131   int help = *i1;
1132
1133   *i1 = *i2;
1134   *i2 = help;
1135 }
1136
1137 inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
1138 {
1139   int help_x = *x1;
1140   int help_y = *y1;
1141
1142   *x1 = *x2;
1143   *x2 = help_x;
1144
1145   *y1 = *y2;
1146   *y2 = help_y;
1147 }
1148
1149 /* the "put" variants of the following file access functions check for the file
1150    pointer being != NULL and return the number of bytes they have or would have
1151    written; this allows for chunk writing functions to first determine the size
1152    of the (not yet written) chunk, write the correct chunk size and finally
1153    write the chunk itself */
1154
1155 int getFile8BitInteger(FILE *file)
1156 {
1157   return fgetc(file);
1158 }
1159
1160 int putFile8BitInteger(FILE *file, int value)
1161 {
1162   if (file != NULL)
1163     fputc(value, file);
1164
1165   return 1;
1166 }
1167
1168 int getFile16BitInteger(FILE *file, int byte_order)
1169 {
1170   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1171     return ((fgetc(file) << 8) |
1172             (fgetc(file) << 0));
1173   else           /* BYTE_ORDER_LITTLE_ENDIAN */
1174     return ((fgetc(file) << 0) |
1175             (fgetc(file) << 8));
1176 }
1177
1178 int putFile16BitInteger(FILE *file, int value, int byte_order)
1179 {
1180   if (file != NULL)
1181   {
1182     if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1183     {
1184       fputc((value >> 8) & 0xff, file);
1185       fputc((value >> 0) & 0xff, file);
1186     }
1187     else           /* BYTE_ORDER_LITTLE_ENDIAN */
1188     {
1189       fputc((value >> 0) & 0xff, file);
1190       fputc((value >> 8) & 0xff, file);
1191     }
1192   }
1193
1194   return 2;
1195 }
1196
1197 int getFile32BitInteger(FILE *file, int byte_order)
1198 {
1199   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1200     return ((fgetc(file) << 24) |
1201             (fgetc(file) << 16) |
1202             (fgetc(file) <<  8) |
1203             (fgetc(file) <<  0));
1204   else           /* BYTE_ORDER_LITTLE_ENDIAN */
1205     return ((fgetc(file) <<  0) |
1206             (fgetc(file) <<  8) |
1207             (fgetc(file) << 16) |
1208             (fgetc(file) << 24));
1209 }
1210
1211 int putFile32BitInteger(FILE *file, int value, int byte_order)
1212 {
1213   if (file != NULL)
1214   {
1215     if (byte_order == BYTE_ORDER_BIG_ENDIAN)
1216     {
1217       fputc((value >> 24) & 0xff, file);
1218       fputc((value >> 16) & 0xff, file);
1219       fputc((value >>  8) & 0xff, file);
1220       fputc((value >>  0) & 0xff, file);
1221     }
1222     else           /* BYTE_ORDER_LITTLE_ENDIAN */
1223     {
1224       fputc((value >>  0) & 0xff, file);
1225       fputc((value >>  8) & 0xff, file);
1226       fputc((value >> 16) & 0xff, file);
1227       fputc((value >> 24) & 0xff, file);
1228     }
1229   }
1230
1231   return 4;
1232 }
1233
1234 boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
1235                      int byte_order)
1236 {
1237   const int chunk_name_length = 4;
1238
1239   /* read chunk name */
1240   fgets(chunk_name, chunk_name_length + 1, file);
1241
1242   if (chunk_size != NULL)
1243   {
1244     /* read chunk size */
1245     *chunk_size = getFile32BitInteger(file, byte_order);
1246   }
1247
1248   return (feof(file) || ferror(file) ? FALSE : TRUE);
1249 }
1250
1251 int putFileChunk(FILE *file, char *chunk_name, int chunk_size,
1252                  int byte_order)
1253 {
1254   int num_bytes = 0;
1255
1256   /* write chunk name */
1257   if (file != NULL)
1258     fputs(chunk_name, file);
1259
1260   num_bytes += strlen(chunk_name);
1261
1262   if (chunk_size >= 0)
1263   {
1264     /* write chunk size */
1265     if (file != NULL)
1266       putFile32BitInteger(file, chunk_size, byte_order);
1267
1268     num_bytes += 4;
1269   }
1270
1271   return num_bytes;
1272 }
1273
1274 int getFileVersion(FILE *file)
1275 {
1276   int version_major = fgetc(file);
1277   int version_minor = fgetc(file);
1278   int version_patch = fgetc(file);
1279   int version_build = fgetc(file);
1280
1281   return VERSION_IDENT(version_major, version_minor, version_patch,
1282                        version_build);
1283 }
1284
1285 int putFileVersion(FILE *file, int version)
1286 {
1287   if (file != NULL)
1288   {
1289     int version_major = VERSION_MAJOR(version);
1290     int version_minor = VERSION_MINOR(version);
1291     int version_patch = VERSION_PATCH(version);
1292     int version_build = VERSION_BUILD(version);
1293
1294     fputc(version_major, file);
1295     fputc(version_minor, file);
1296     fputc(version_patch, file);
1297     fputc(version_build, file);
1298   }
1299
1300   return 4;
1301 }
1302
1303 void ReadBytesFromFile(FILE *file, byte *buffer, unsigned long bytes)
1304 {
1305   int i;
1306
1307   for(i = 0; i < bytes && !feof(file); i++)
1308     buffer[i] = fgetc(file);
1309 }
1310
1311 void WriteBytesToFile(FILE *file, byte *buffer, unsigned long bytes)
1312 {
1313   int i;
1314
1315   for(i = 0; i < bytes; i++)
1316     fputc(buffer[i], file);
1317 }
1318
1319 void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
1320 {
1321   while (bytes-- && !feof(file))
1322     fgetc(file);
1323 }
1324
1325 void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
1326 {
1327   while (bytes--)
1328     fputc(0, file);
1329 }
1330
1331
1332 /* ------------------------------------------------------------------------- */
1333 /* functions to translate key identifiers between different format           */
1334 /* ------------------------------------------------------------------------- */
1335
1336 #define TRANSLATE_KEYSYM_TO_KEYNAME     0
1337 #define TRANSLATE_KEYSYM_TO_X11KEYNAME  1
1338 #define TRANSLATE_KEYNAME_TO_KEYSYM     2
1339 #define TRANSLATE_X11KEYNAME_TO_KEYSYM  3
1340
1341 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
1342 {
1343   static struct
1344   {
1345     Key key;
1346     char *x11name;
1347     char *name;
1348   } translate_key[] =
1349   {
1350     /* normal cursor keys */
1351     { KSYM_Left,        "XK_Left",              "cursor left" },
1352     { KSYM_Right,       "XK_Right",             "cursor right" },
1353     { KSYM_Up,          "XK_Up",                "cursor up" },
1354     { KSYM_Down,        "XK_Down",              "cursor down" },
1355
1356     /* keypad cursor keys */
1357 #ifdef KSYM_KP_Left
1358     { KSYM_KP_Left,     "XK_KP_Left",           "keypad left" },
1359     { KSYM_KP_Right,    "XK_KP_Right",          "keypad right" },
1360     { KSYM_KP_Up,       "XK_KP_Up",             "keypad up" },
1361     { KSYM_KP_Down,     "XK_KP_Down",           "keypad down" },
1362 #endif
1363
1364     /* other keypad keys */
1365 #ifdef KSYM_KP_Enter
1366     { KSYM_KP_Enter,    "XK_KP_Enter",          "keypad enter" },
1367     { KSYM_KP_Add,      "XK_KP_Add",            "keypad +" },
1368     { KSYM_KP_Subtract, "XK_KP_Subtract",       "keypad -" },
1369     { KSYM_KP_Multiply, "XK_KP_Multiply",       "keypad mltply" },
1370     { KSYM_KP_Divide,   "XK_KP_Divide",         "keypad /" },
1371     { KSYM_KP_Separator,"XK_KP_Separator",      "keypad ," },
1372 #endif
1373
1374     /* modifier keys */
1375     { KSYM_Shift_L,     "XK_Shift_L",           "left shift" },
1376     { KSYM_Shift_R,     "XK_Shift_R",           "right shift" },
1377     { KSYM_Control_L,   "XK_Control_L",         "left control" },
1378     { KSYM_Control_R,   "XK_Control_R",         "right control" },
1379     { KSYM_Meta_L,      "XK_Meta_L",            "left meta" },
1380     { KSYM_Meta_R,      "XK_Meta_R",            "right meta" },
1381     { KSYM_Alt_L,       "XK_Alt_L",             "left alt" },
1382     { KSYM_Alt_R,       "XK_Alt_R",             "right alt" },
1383     { KSYM_Super_L,     "XK_Super_L",           "left super" },  /* Win-L */
1384     { KSYM_Super_R,     "XK_Super_R",           "right super" }, /* Win-R */
1385     { KSYM_Mode_switch, "XK_Mode_switch",       "mode switch" }, /* Alt-R */
1386     { KSYM_Multi_key,   "XK_Multi_key",         "multi key" },   /* Ctrl-R */
1387
1388     /* some special keys */
1389     { KSYM_BackSpace,   "XK_BackSpace",         "backspace" },
1390     { KSYM_Delete,      "XK_Delete",            "delete" },
1391     { KSYM_Insert,      "XK_Insert",            "insert" },
1392     { KSYM_Tab,         "XK_Tab",               "tab" },
1393     { KSYM_Home,        "XK_Home",              "home" },
1394     { KSYM_End,         "XK_End",               "end" },
1395     { KSYM_Page_Up,     "XK_Page_Up",           "page up" },
1396     { KSYM_Page_Down,   "XK_Page_Down",         "page down" },
1397     { KSYM_Menu,        "XK_Menu",              "menu" },        /* Win-Menu */
1398
1399     /* ASCII 0x20 to 0x40 keys (except numbers) */
1400     { KSYM_space,       "XK_space",             "space" },
1401     { KSYM_exclam,      "XK_exclam",            "!" },
1402     { KSYM_quotedbl,    "XK_quotedbl",          "\"" },
1403     { KSYM_numbersign,  "XK_numbersign",        "#" },
1404     { KSYM_dollar,      "XK_dollar",            "$" },
1405     { KSYM_percent,     "XK_percent",           "%" },
1406     { KSYM_ampersand,   "XK_ampersand",         "&" },
1407     { KSYM_apostrophe,  "XK_apostrophe",        "'" },
1408     { KSYM_parenleft,   "XK_parenleft",         "(" },
1409     { KSYM_parenright,  "XK_parenright",        ")" },
1410     { KSYM_asterisk,    "XK_asterisk",          "*" },
1411     { KSYM_plus,        "XK_plus",              "+" },
1412     { KSYM_comma,       "XK_comma",             "," },
1413     { KSYM_minus,       "XK_minus",             "-" },
1414     { KSYM_period,      "XK_period",            "." },
1415     { KSYM_slash,       "XK_slash",             "/" },
1416     { KSYM_colon,       "XK_colon",             ":" },
1417     { KSYM_semicolon,   "XK_semicolon",         ";" },
1418     { KSYM_less,        "XK_less",              "<" },
1419     { KSYM_equal,       "XK_equal",             "=" },
1420     { KSYM_greater,     "XK_greater",           ">" },
1421     { KSYM_question,    "XK_question",          "?" },
1422     { KSYM_at,          "XK_at",                "@" },
1423
1424     /* more ASCII keys */
1425     { KSYM_bracketleft, "XK_bracketleft",       "[" },
1426     { KSYM_backslash,   "XK_backslash",         "\\" },
1427     { KSYM_bracketright,"XK_bracketright",      "]" },
1428     { KSYM_asciicircum, "XK_asciicircum",       "^" },
1429     { KSYM_underscore,  "XK_underscore",        "_" },
1430     { KSYM_grave,       "XK_grave",             "grave" },
1431     { KSYM_quoteleft,   "XK_quoteleft",         "quote left" },
1432     { KSYM_braceleft,   "XK_braceleft",         "brace left" },
1433     { KSYM_bar,         "XK_bar",               "bar" },
1434     { KSYM_braceright,  "XK_braceright",        "brace right" },
1435     { KSYM_asciitilde,  "XK_asciitilde",        "~" },
1436
1437     /* special (non-ASCII) keys */
1438     { KSYM_Adiaeresis,  "XK_Adiaeresis",        "Ä" },
1439     { KSYM_Odiaeresis,  "XK_Odiaeresis",        "Ö" },
1440     { KSYM_Udiaeresis,  "XK_Udiaeresis",        "Ãœ" },
1441     { KSYM_adiaeresis,  "XK_adiaeresis",        "ä" },
1442     { KSYM_odiaeresis,  "XK_odiaeresis",        "ö" },
1443     { KSYM_udiaeresis,  "XK_udiaeresis",        "ü" },
1444     { KSYM_ssharp,      "XK_ssharp",            "sharp s" },
1445
1446     /* end-of-array identifier */
1447     { 0,                NULL,                   NULL }
1448   };
1449
1450   int i;
1451
1452   if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
1453   {
1454     static char name_buffer[30];
1455     Key key = *keysym;
1456
1457     if (key >= KSYM_A && key <= KSYM_Z)
1458       sprintf(name_buffer, "%c", 'A' + (char)(key - KSYM_A));
1459     else if (key >= KSYM_a && key <= KSYM_z)
1460       sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
1461     else if (key >= KSYM_0 && key <= KSYM_9)
1462       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
1463     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
1464       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
1465 #if 1
1466     else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
1467       sprintf(name_buffer, "F%d", (int)(key - KSYM_FKEY_FIRST + 1));
1468 #else
1469     else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
1470       sprintf(name_buffer, "function F%d", (int)(key - KSYM_FKEY_FIRST + 1));
1471 #endif
1472     else if (key == KSYM_UNDEFINED)
1473       strcpy(name_buffer, "(undefined)");
1474     else
1475     {
1476       i = 0;
1477
1478       do
1479       {
1480         if (key == translate_key[i].key)
1481         {
1482           strcpy(name_buffer, translate_key[i].name);
1483           break;
1484         }
1485       }
1486       while (translate_key[++i].name);
1487
1488       if (!translate_key[i].name)
1489         strcpy(name_buffer, "(unknown)");
1490     }
1491
1492     *name = name_buffer;
1493   }
1494   else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
1495   {
1496     static char name_buffer[30];
1497     Key key = *keysym;
1498
1499     if (key >= KSYM_A && key <= KSYM_Z)
1500       sprintf(name_buffer, "XK_%c", 'A' + (char)(key - KSYM_A));
1501     else if (key >= KSYM_a && key <= KSYM_z)
1502       sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
1503     else if (key >= KSYM_0 && key <= KSYM_9)
1504       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
1505     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
1506       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
1507     else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
1508       sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_FKEY_FIRST + 1));
1509     else if (key == KSYM_UNDEFINED)
1510       strcpy(name_buffer, "[undefined]");
1511     else
1512     {
1513       i = 0;
1514
1515       do
1516       {
1517         if (key == translate_key[i].key)
1518         {
1519           strcpy(name_buffer, translate_key[i].x11name);
1520           break;
1521         }
1522       }
1523       while (translate_key[++i].x11name);
1524
1525       if (!translate_key[i].x11name)
1526         sprintf(name_buffer, "0x%04lx", (unsigned long)key);
1527     }
1528
1529     *x11name = name_buffer;
1530   }
1531   else if (mode == TRANSLATE_KEYNAME_TO_KEYSYM)
1532   {
1533     Key key = KSYM_UNDEFINED;
1534
1535     i = 0;
1536     do
1537     {
1538       if (strEqual(translate_key[i].name, *name))
1539       {
1540         key = translate_key[i].key;
1541         break;
1542       }
1543     }
1544     while (translate_key[++i].x11name);
1545
1546     if (key == KSYM_UNDEFINED)
1547       Error(ERR_WARN, "getKeyFromKeyName(): not completely implemented");
1548
1549     *keysym = key;
1550   }
1551   else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
1552   {
1553     Key key = KSYM_UNDEFINED;
1554     char *name_ptr = *x11name;
1555
1556     if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
1557     {
1558       char c = name_ptr[3];
1559
1560       if (c >= 'A' && c <= 'Z')
1561         key = KSYM_A + (Key)(c - 'A');
1562       else if (c >= 'a' && c <= 'z')
1563         key = KSYM_a + (Key)(c - 'a');
1564       else if (c >= '0' && c <= '9')
1565         key = KSYM_0 + (Key)(c - '0');
1566     }
1567     else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
1568     {
1569       char c = name_ptr[6];
1570
1571       if (c >= '0' && c <= '9')
1572         key = KSYM_KP_0 + (Key)(c - '0');
1573     }
1574     else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
1575     {
1576       char c1 = name_ptr[4];
1577       char c2 = name_ptr[5];
1578       int d = 0;
1579
1580       if ((c1 >= '0' && c1 <= '9') &&
1581           ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
1582         d = atoi(&name_ptr[4]);
1583
1584       if (d >= 1 && d <= KSYM_NUM_FKEYS)
1585         key = KSYM_F1 + (Key)(d - 1);
1586     }
1587     else if (strncmp(name_ptr, "XK_", 3) == 0)
1588     {
1589       i = 0;
1590
1591       do
1592       {
1593         if (strEqual(name_ptr, translate_key[i].x11name))
1594         {
1595           key = translate_key[i].key;
1596           break;
1597         }
1598       }
1599       while (translate_key[++i].x11name);
1600     }
1601     else if (strncmp(name_ptr, "0x", 2) == 0)
1602     {
1603       unsigned long value = 0;
1604
1605       name_ptr += 2;
1606
1607       while (name_ptr)
1608       {
1609         char c = *name_ptr++;
1610         int d = -1;
1611
1612         if (c >= '0' && c <= '9')
1613           d = (int)(c - '0');
1614         else if (c >= 'a' && c <= 'f')
1615           d = (int)(c - 'a' + 10);
1616         else if (c >= 'A' && c <= 'F')
1617           d = (int)(c - 'A' + 10);
1618
1619         if (d == -1)
1620         {
1621           value = -1;
1622           break;
1623         }
1624
1625         value = value * 16 + d;
1626       }
1627
1628       if (value != -1)
1629         key = (Key)value;
1630     }
1631
1632     *keysym = key;
1633   }
1634 }
1635
1636 char *getKeyNameFromKey(Key key)
1637 {
1638   char *name;
1639
1640   translate_keyname(&key, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
1641   return name;
1642 }
1643
1644 char *getX11KeyNameFromKey(Key key)
1645 {
1646   char *x11name;
1647
1648   translate_keyname(&key, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
1649   return x11name;
1650 }
1651
1652 Key getKeyFromKeyName(char *name)
1653 {
1654   Key key;
1655
1656   translate_keyname(&key, NULL, &name, TRANSLATE_KEYNAME_TO_KEYSYM);
1657   return key;
1658 }
1659
1660 Key getKeyFromX11KeyName(char *x11name)
1661 {
1662   Key key;
1663
1664   translate_keyname(&key, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
1665   return key;
1666 }
1667
1668 char getCharFromKey(Key key)
1669 {
1670   char *keyname = getKeyNameFromKey(key);
1671   char letter = 0;
1672
1673   if (strlen(keyname) == 1)
1674     letter = keyname[0];
1675   else if (strEqual(keyname, "space"))
1676     letter = ' ';
1677   else if (strEqual(keyname, "circumflex"))
1678     letter = '^';
1679
1680   return letter;
1681 }
1682
1683
1684 /* ------------------------------------------------------------------------- */
1685 /* functions to translate string identifiers to integer or boolean value     */
1686 /* ------------------------------------------------------------------------- */
1687
1688 int get_integer_from_string(char *s)
1689 {
1690   static char *number_text[][3] =
1691   {
1692     { "0",      "zero",         "null",         },
1693     { "1",      "one",          "first"         },
1694     { "2",      "two",          "second"        },
1695     { "3",      "three",        "third"         },
1696     { "4",      "four",         "fourth"        },
1697     { "5",      "five",         "fifth"         },
1698     { "6",      "six",          "sixth"         },
1699     { "7",      "seven",        "seventh"       },
1700     { "8",      "eight",        "eighth"        },
1701     { "9",      "nine",         "ninth"         },
1702     { "10",     "ten",          "tenth"         },
1703     { "11",     "eleven",       "eleventh"      },
1704     { "12",     "twelve",       "twelfth"       },
1705
1706     { NULL,     NULL,           NULL            },
1707   };
1708
1709   int i, j;
1710   char *s_lower = getStringToLower(s);
1711   int result = -1;
1712
1713   for (i = 0; number_text[i][0] != NULL; i++)
1714     for (j = 0; j < 3; j++)
1715       if (strEqual(s_lower, number_text[i][j]))
1716         result = i;
1717
1718   if (result == -1)
1719   {
1720     if (strEqual(s_lower, "false"))
1721       result = 0;
1722     else if (strEqual(s_lower, "true"))
1723       result = 1;
1724     else
1725       result = atoi(s);
1726   }
1727
1728   free(s_lower);
1729
1730   return result;
1731 }
1732
1733 boolean get_boolean_from_string(char *s)
1734 {
1735   char *s_lower = getStringToLower(s);
1736   boolean result = FALSE;
1737
1738   if (strEqual(s_lower, "true") ||
1739       strEqual(s_lower, "yes") ||
1740       strEqual(s_lower, "on") ||
1741       get_integer_from_string(s) == 1)
1742     result = TRUE;
1743
1744   free(s_lower);
1745
1746   return result;
1747 }
1748
1749
1750 /* ------------------------------------------------------------------------- */
1751 /* functions for generic lists                                               */
1752 /* ------------------------------------------------------------------------- */
1753
1754 ListNode *newListNode()
1755 {
1756   return checked_calloc(sizeof(ListNode));
1757 }
1758
1759 void addNodeToList(ListNode **node_first, char *key, void *content)
1760 {
1761   ListNode *node_new = newListNode();
1762
1763   node_new->key = getStringCopy(key);
1764   node_new->content = content;
1765   node_new->next = *node_first;
1766   *node_first = node_new;
1767 }
1768
1769 void deleteNodeFromList(ListNode **node_first, char *key,
1770                         void (*destructor_function)(void *))
1771 {
1772   if (node_first == NULL || *node_first == NULL)
1773     return;
1774
1775   if (strEqual((*node_first)->key, key))
1776   {
1777     free((*node_first)->key);
1778     if (destructor_function)
1779       destructor_function((*node_first)->content);
1780     *node_first = (*node_first)->next;
1781   }
1782   else
1783     deleteNodeFromList(&(*node_first)->next, key, destructor_function);
1784 }
1785
1786 ListNode *getNodeFromKey(ListNode *node_first, char *key)
1787 {
1788   if (node_first == NULL)
1789     return NULL;
1790
1791   if (strEqual(node_first->key, key))
1792     return node_first;
1793   else
1794     return getNodeFromKey(node_first->next, key);
1795 }
1796
1797 int getNumNodes(ListNode *node_first)
1798 {
1799   return (node_first ? 1 + getNumNodes(node_first->next) : 0);
1800 }
1801
1802 void dumpList(ListNode *node_first)
1803 {
1804   ListNode *node = node_first;
1805
1806   while (node)
1807   {
1808     printf("['%s' (%d)]\n", node->key,
1809            ((struct ListNodeInfo *)node->content)->num_references);
1810     node = node->next;
1811   }
1812
1813   printf("[%d nodes]\n", getNumNodes(node_first));
1814 }
1815
1816
1817 /* ------------------------------------------------------------------------- */
1818 /* functions for checking files and filenames                                */
1819 /* ------------------------------------------------------------------------- */
1820
1821 boolean fileExists(char *filename)
1822 {
1823   if (filename == NULL)
1824     return FALSE;
1825
1826   return (access(filename, F_OK) == 0);
1827 }
1828
1829 boolean fileHasPrefix(char *basename, char *prefix)
1830 {
1831   static char *basename_lower = NULL;
1832   int basename_length, prefix_length;
1833
1834   checked_free(basename_lower);
1835
1836   if (basename == NULL || prefix == NULL)
1837     return FALSE;
1838
1839   basename_lower = getStringToLower(basename);
1840   basename_length = strlen(basename_lower);
1841   prefix_length = strlen(prefix);
1842
1843   if (basename_length > prefix_length + 1 &&
1844       basename_lower[prefix_length] == '.' &&
1845       strncmp(basename_lower, prefix, prefix_length) == 0)
1846     return TRUE;
1847
1848   return FALSE;
1849 }
1850
1851 boolean fileHasSuffix(char *basename, char *suffix)
1852 {
1853   static char *basename_lower = NULL;
1854   int basename_length, suffix_length;
1855
1856   checked_free(basename_lower);
1857
1858   if (basename == NULL || suffix == NULL)
1859     return FALSE;
1860
1861   basename_lower = getStringToLower(basename);
1862   basename_length = strlen(basename_lower);
1863   suffix_length = strlen(suffix);
1864
1865   if (basename_length > suffix_length + 1 &&
1866       basename_lower[basename_length - suffix_length - 1] == '.' &&
1867       strEqual(&basename_lower[basename_length - suffix_length], suffix))
1868     return TRUE;
1869
1870   return FALSE;
1871 }
1872
1873 boolean FileIsGraphic(char *filename)
1874 {
1875   char *basename = getBaseNamePtr(filename);
1876
1877   return fileHasSuffix(basename, "pcx");
1878 }
1879
1880 boolean FileIsSound(char *filename)
1881 {
1882   char *basename = getBaseNamePtr(filename);
1883
1884   return fileHasSuffix(basename, "wav");
1885 }
1886
1887 boolean FileIsMusic(char *filename)
1888 {
1889   char *basename = getBaseNamePtr(filename);
1890
1891   if (FileIsSound(basename))
1892     return TRUE;
1893
1894 #if defined(TARGET_SDL)
1895   if (fileHasPrefix(basename, "mod") ||
1896       fileHasSuffix(basename, "mod") ||
1897       fileHasSuffix(basename, "s3m") ||
1898       fileHasSuffix(basename, "it") ||
1899       fileHasSuffix(basename, "xm") ||
1900       fileHasSuffix(basename, "midi") ||
1901       fileHasSuffix(basename, "mid") ||
1902       fileHasSuffix(basename, "mp3") ||
1903       fileHasSuffix(basename, "ogg"))
1904     return TRUE;
1905 #endif
1906
1907   return FALSE;
1908 }
1909
1910 boolean FileIsArtworkType(char *basename, int type)
1911 {
1912   if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(basename)) ||
1913       (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(basename)) ||
1914       (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(basename)))
1915     return TRUE;
1916
1917   return FALSE;
1918 }
1919
1920 /* ------------------------------------------------------------------------- */
1921 /* functions for loading artwork configuration information                   */
1922 /* ------------------------------------------------------------------------- */
1923
1924 char *get_mapped_token(char *token)
1925 {
1926   /* !!! make this dynamically configurable (init.c:InitArtworkConfig) !!! */
1927   static char *map_token_prefix[][2] =
1928   {
1929     { "char_procent",           "char_percent"  },
1930     { NULL,                                     }
1931   };
1932   int i;
1933
1934   for (i = 0; map_token_prefix[i][0] != NULL; i++)
1935   {
1936     int len_token_prefix = strlen(map_token_prefix[i][0]);
1937
1938     if (strncmp(token, map_token_prefix[i][0], len_token_prefix) == 0)
1939       return getStringCat2(map_token_prefix[i][1], &token[len_token_prefix]);
1940   }
1941
1942   return NULL;
1943 }
1944
1945 /* This function checks if a string <s> of the format "string1, string2, ..."
1946    exactly contains a string <s_contained>. */
1947
1948 static boolean string_has_parameter(char *s, char *s_contained)
1949 {
1950   char *substring;
1951
1952   if (s == NULL || s_contained == NULL)
1953     return FALSE;
1954
1955   if (strlen(s_contained) > strlen(s))
1956     return FALSE;
1957
1958   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
1959   {
1960     char next_char = s[strlen(s_contained)];
1961
1962     /* check if next character is delimiter or whitespace */
1963     return (next_char == ',' || next_char == '\0' ||
1964             next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
1965   }
1966
1967   /* check if string contains another parameter string after a comma */
1968   substring = strchr(s, ',');
1969   if (substring == NULL)        /* string does not contain a comma */
1970     return FALSE;
1971
1972   /* advance string pointer to next character after the comma */
1973   substring++;
1974
1975   /* skip potential whitespaces after the comma */
1976   while (*substring == ' ' || *substring == '\t')
1977     substring++;
1978
1979   return string_has_parameter(substring, s_contained);
1980 }
1981
1982 int get_parameter_value(char *value_raw, char *suffix, int type)
1983 {
1984   char *value = getStringToLower(value_raw);
1985   int result = 0;       /* probably a save default value */
1986
1987   if (strEqual(suffix, ".direction"))
1988   {
1989     result = (strEqual(value, "left")  ? MV_LEFT :
1990               strEqual(value, "right") ? MV_RIGHT :
1991               strEqual(value, "up")    ? MV_UP :
1992               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
1993   }
1994   else if (strEqual(suffix, ".anim_mode"))
1995   {
1996     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
1997               string_has_parameter(value, "loop")       ? ANIM_LOOP :
1998               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
1999               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
2000               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
2001               string_has_parameter(value, "random")     ? ANIM_RANDOM :
2002               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
2003               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
2004               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
2005               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
2006               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
2007               ANIM_DEFAULT);
2008
2009     if (string_has_parameter(value, "reverse"))
2010       result |= ANIM_REVERSE;
2011
2012     if (string_has_parameter(value, "opaque_player"))
2013       result |= ANIM_OPAQUE_PLAYER;
2014
2015     if (string_has_parameter(value, "static_panel"))
2016       result |= ANIM_STATIC_PANEL;
2017   }
2018   else          /* generic parameter of type integer or boolean */
2019   {
2020     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
2021               type == TYPE_INTEGER ? get_integer_from_string(value) :
2022               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
2023               ARG_UNDEFINED_VALUE);
2024   }
2025
2026   free(value);
2027
2028   return result;
2029 }
2030
2031 int get_auto_parameter_value(char *token, char *value_raw)
2032 {
2033   char *suffix;
2034
2035   if (token == NULL || value_raw == NULL)
2036     return ARG_UNDEFINED_VALUE;
2037
2038   suffix = strrchr(token, '.');
2039   if (suffix == NULL)
2040     suffix = token;
2041
2042   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
2043 }
2044
2045 static void FreeCustomArtworkList(struct ArtworkListInfo *,
2046                                   struct ListNodeInfo ***, int *);
2047
2048 struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list,
2049                                            struct ConfigTypeInfo *suffix_list,
2050                                            char **ignore_tokens,
2051                                            int num_file_list_entries)
2052 {
2053   struct FileInfo *file_list;
2054   int num_file_list_entries_found = 0;
2055   int num_suffix_list_entries = 0;
2056   int list_pos;
2057   int i, j;
2058
2059   file_list = checked_calloc(num_file_list_entries * sizeof(struct FileInfo));
2060
2061   for (i = 0; suffix_list[i].token != NULL; i++)
2062     num_suffix_list_entries++;
2063
2064   /* always start with reliable default values */
2065   for (i = 0; i < num_file_list_entries; i++)
2066   {
2067     file_list[i].token = NULL;
2068
2069     file_list[i].default_filename = NULL;
2070     file_list[i].filename = NULL;
2071
2072     if (num_suffix_list_entries > 0)
2073     {
2074       int parameter_array_size = num_suffix_list_entries * sizeof(char *);
2075
2076       file_list[i].default_parameter = checked_calloc(parameter_array_size);
2077       file_list[i].parameter = checked_calloc(parameter_array_size);
2078
2079       for (j = 0; j < num_suffix_list_entries; j++)
2080       {
2081         setString(&file_list[i].default_parameter[j], suffix_list[j].value);
2082         setString(&file_list[i].parameter[j], suffix_list[j].value);
2083       }
2084
2085       file_list[i].redefined = FALSE;
2086       file_list[i].fallback_to_default = FALSE;
2087     }
2088   }
2089
2090   list_pos = 0;
2091   for (i = 0; config_list[i].token != NULL; i++)
2092   {
2093     int len_config_token = strlen(config_list[i].token);
2094     int len_config_value = strlen(config_list[i].value);
2095     boolean is_file_entry = TRUE;
2096
2097     for (j = 0; suffix_list[j].token != NULL; j++)
2098     {
2099       int len_suffix = strlen(suffix_list[j].token);
2100
2101       if (len_suffix < len_config_token &&
2102           strEqual(&config_list[i].token[len_config_token - len_suffix],
2103                    suffix_list[j].token))
2104       {
2105         setString(&file_list[list_pos].default_parameter[j],
2106                   config_list[i].value);
2107
2108         is_file_entry = FALSE;
2109         break;
2110       }
2111     }
2112
2113     /* the following tokens are no file definitions, but other config tokens */
2114     for (j = 0; ignore_tokens[j] != NULL; j++)
2115       if (strEqual(config_list[i].token, ignore_tokens[j]))
2116         is_file_entry = FALSE;
2117
2118     if (is_file_entry)
2119     {
2120       if (i > 0)
2121         list_pos++;
2122
2123       if (list_pos >= num_file_list_entries)
2124         break;
2125
2126       /* simple sanity check if this is really a file definition */
2127       if (!strEqual(&config_list[i].value[len_config_value - 4], ".pcx") &&
2128           !strEqual(&config_list[i].value[len_config_value - 4], ".wav") &&
2129           !strEqual(config_list[i].value, UNDEFINED_FILENAME))
2130       {
2131         Error(ERR_RETURN, "Configuration directive '%s' -> '%s':",
2132               config_list[i].token, config_list[i].value);
2133         Error(ERR_EXIT, "This seems to be no valid definition -- please fix");
2134       }
2135
2136       file_list[list_pos].token = config_list[i].token;
2137       file_list[list_pos].default_filename = config_list[i].value;
2138     }
2139   }
2140
2141   num_file_list_entries_found = list_pos + 1;
2142   if (num_file_list_entries_found != num_file_list_entries)
2143   {
2144     Error(ERR_RETURN_LINE, "-");
2145     Error(ERR_RETURN, "inconsistant config list information:");
2146     Error(ERR_RETURN, "- should be:   %d (according to 'src/conf_gfx.h')",
2147           num_file_list_entries);
2148     Error(ERR_RETURN, "- found to be: %d (according to 'src/conf_gfx.c')",
2149           num_file_list_entries_found);
2150     Error(ERR_EXIT,   "please fix");
2151   }
2152
2153   return file_list;
2154 }
2155
2156 static boolean token_suffix_match(char *token, char *suffix, int start_pos)
2157 {
2158   int len_token = strlen(token);
2159   int len_suffix = strlen(suffix);
2160
2161   if (start_pos < 0)    /* compare suffix from end of string */
2162     start_pos += len_token;
2163
2164   if (start_pos < 0 || start_pos + len_suffix > len_token)
2165     return FALSE;
2166
2167   if (strncmp(&token[start_pos], suffix, len_suffix) != 0)
2168     return FALSE;
2169
2170   if (token[start_pos + len_suffix] == '\0')
2171     return TRUE;
2172
2173   if (token[start_pos + len_suffix] == '.')
2174     return TRUE;
2175
2176   return FALSE;
2177 }
2178
2179 #define KNOWN_TOKEN_VALUE       "[KNOWN_TOKEN_VALUE]"
2180
2181 static void read_token_parameters(SetupFileHash *setup_file_hash,
2182                                   struct ConfigTypeInfo *suffix_list,
2183                                   struct FileInfo *file_list_entry)
2184 {
2185   /* check for config token that is the base token without any suffixes */
2186   char *filename = getHashEntry(setup_file_hash, file_list_entry->token);
2187   char *known_token_value = KNOWN_TOKEN_VALUE;
2188   int i;
2189
2190   if (filename != NULL)
2191   {
2192     setString(&file_list_entry->filename, filename);
2193
2194     /* when file definition found, set all parameters to default values */
2195     for (i = 0; suffix_list[i].token != NULL; i++)
2196       setString(&file_list_entry->parameter[i], suffix_list[i].value);
2197
2198     file_list_entry->redefined = TRUE;
2199
2200     /* mark config file token as well known from default config */
2201     setHashEntry(setup_file_hash, file_list_entry->token, known_token_value);
2202   }
2203
2204   /* check for config tokens that can be build by base token and suffixes */
2205   for (i = 0; suffix_list[i].token != NULL; i++)
2206   {
2207     char *token = getStringCat2(file_list_entry->token, suffix_list[i].token);
2208     char *value = getHashEntry(setup_file_hash, token);
2209
2210     if (value != NULL)
2211     {
2212       setString(&file_list_entry->parameter[i], value);
2213
2214       /* mark config file token as well known from default config */
2215       setHashEntry(setup_file_hash, token, known_token_value);
2216     }
2217
2218     free(token);
2219   }
2220 }
2221
2222 static void add_dynamic_file_list_entry(struct FileInfo **list,
2223                                         int *num_list_entries,
2224                                         SetupFileHash *extra_file_hash,
2225                                         struct ConfigTypeInfo *suffix_list,
2226                                         int num_suffix_list_entries,
2227                                         char *token)
2228 {
2229   struct FileInfo *new_list_entry;
2230   int parameter_array_size = num_suffix_list_entries * sizeof(char *);
2231
2232   (*num_list_entries)++;
2233   *list = checked_realloc(*list, *num_list_entries * sizeof(struct FileInfo));
2234   new_list_entry = &(*list)[*num_list_entries - 1];
2235
2236   new_list_entry->token = getStringCopy(token);
2237   new_list_entry->default_filename = NULL;
2238   new_list_entry->filename = NULL;
2239   new_list_entry->parameter = checked_calloc(parameter_array_size);
2240
2241   new_list_entry->redefined = FALSE;
2242   new_list_entry->fallback_to_default = FALSE;
2243
2244   read_token_parameters(extra_file_hash, suffix_list, new_list_entry);
2245 }
2246
2247 static void add_property_mapping(struct PropertyMapping **list,
2248                                  int *num_list_entries,
2249                                  int base_index, int ext1_index,
2250                                  int ext2_index, int ext3_index,
2251                                  int artwork_index)
2252 {
2253   struct PropertyMapping *new_list_entry;
2254
2255   (*num_list_entries)++;
2256   *list = checked_realloc(*list,
2257                           *num_list_entries * sizeof(struct PropertyMapping));
2258   new_list_entry = &(*list)[*num_list_entries - 1];
2259
2260   new_list_entry->base_index = base_index;
2261   new_list_entry->ext1_index = ext1_index;
2262   new_list_entry->ext2_index = ext2_index;
2263   new_list_entry->ext3_index = ext3_index;
2264
2265   new_list_entry->artwork_index = artwork_index;
2266 }
2267
2268 static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
2269                                           char *filename)
2270 {
2271   struct FileInfo *file_list = artwork_info->file_list;
2272   struct ConfigTypeInfo *suffix_list = artwork_info->suffix_list;
2273   char **base_prefixes = artwork_info->base_prefixes;
2274   char **ext1_suffixes = artwork_info->ext1_suffixes;
2275   char **ext2_suffixes = artwork_info->ext2_suffixes;
2276   char **ext3_suffixes = artwork_info->ext3_suffixes;
2277   char **ignore_tokens = artwork_info->ignore_tokens;
2278   int num_file_list_entries = artwork_info->num_file_list_entries;
2279   int num_suffix_list_entries = artwork_info->num_suffix_list_entries;
2280   int num_base_prefixes = artwork_info->num_base_prefixes;
2281   int num_ext1_suffixes = artwork_info->num_ext1_suffixes;
2282   int num_ext2_suffixes = artwork_info->num_ext2_suffixes;
2283   int num_ext3_suffixes = artwork_info->num_ext3_suffixes;
2284   int num_ignore_tokens = artwork_info->num_ignore_tokens;
2285   SetupFileHash *setup_file_hash, *valid_file_hash;
2286   SetupFileHash *extra_file_hash, *empty_file_hash;
2287   char *known_token_value = KNOWN_TOKEN_VALUE;
2288   int i, j, k, l;
2289
2290   if (filename == NULL)
2291     return;
2292
2293 #if 0
2294   printf("LoadArtworkConfigFromFilename '%s' ...\n", filename);
2295 #endif
2296
2297   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2298     return;
2299
2300   /* separate valid (defined) from empty (undefined) config token values */
2301   valid_file_hash = newSetupFileHash();
2302   empty_file_hash = newSetupFileHash();
2303   BEGIN_HASH_ITERATION(setup_file_hash, itr)
2304   {
2305     char *value = HASH_ITERATION_VALUE(itr);
2306
2307     setHashEntry(*value ? valid_file_hash : empty_file_hash,
2308                  HASH_ITERATION_TOKEN(itr), value);
2309   }
2310   END_HASH_ITERATION(setup_file_hash, itr)
2311
2312   /* at this point, we do not need the setup file hash anymore -- free it */
2313   freeSetupFileHash(setup_file_hash);
2314
2315   /* map deprecated to current tokens (using prefix match and replace) */
2316   BEGIN_HASH_ITERATION(valid_file_hash, itr)
2317   {
2318     char *token = HASH_ITERATION_TOKEN(itr);
2319     char *mapped_token = get_mapped_token(token);
2320
2321     if (mapped_token != NULL)
2322     {
2323       char *value = HASH_ITERATION_VALUE(itr);
2324
2325       /* add mapped token */
2326       setHashEntry(valid_file_hash, mapped_token, value);
2327
2328       /* ignore old token (by setting it to "known" keyword) */
2329       setHashEntry(valid_file_hash, token, known_token_value);
2330
2331       free(mapped_token);
2332     }
2333   }
2334   END_HASH_ITERATION(valid_file_hash, itr)
2335
2336   /* read parameters for all known config file tokens */
2337   for (i = 0; i < num_file_list_entries; i++)
2338     read_token_parameters(valid_file_hash, suffix_list, &file_list[i]);
2339
2340   /* set all tokens that can be ignored here to "known" keyword */
2341   for (i = 0; i < num_ignore_tokens; i++)
2342     setHashEntry(valid_file_hash, ignore_tokens[i], known_token_value);
2343
2344   /* copy all unknown config file tokens to extra config hash */
2345   extra_file_hash = newSetupFileHash();
2346   BEGIN_HASH_ITERATION(valid_file_hash, itr)
2347   {
2348     char *value = HASH_ITERATION_VALUE(itr);
2349
2350     if (!strEqual(value, known_token_value))
2351       setHashEntry(extra_file_hash, HASH_ITERATION_TOKEN(itr), value);
2352   }
2353   END_HASH_ITERATION(valid_file_hash, itr)
2354
2355   /* at this point, we do not need the valid file hash anymore -- free it */
2356   freeSetupFileHash(valid_file_hash);
2357
2358   /* now try to determine valid, dynamically defined config tokens */
2359
2360   BEGIN_HASH_ITERATION(extra_file_hash, itr)
2361   {
2362     struct FileInfo **dynamic_file_list =
2363       &artwork_info->dynamic_file_list;
2364     int *num_dynamic_file_list_entries =
2365       &artwork_info->num_dynamic_file_list_entries;
2366     struct PropertyMapping **property_mapping =
2367       &artwork_info->property_mapping;
2368     int *num_property_mapping_entries =
2369       &artwork_info->num_property_mapping_entries;
2370     int current_summarized_file_list_entry =
2371       artwork_info->num_file_list_entries +
2372       artwork_info->num_dynamic_file_list_entries;
2373     char *token = HASH_ITERATION_TOKEN(itr);
2374     int len_token = strlen(token);
2375     int start_pos;
2376     boolean base_prefix_found = FALSE;
2377     boolean parameter_suffix_found = FALSE;
2378
2379 #if 0
2380     printf("::: examining '%s' -> '%s'\n", token, HASH_ITERATION_VALUE(itr));
2381 #endif
2382
2383     /* skip all parameter definitions (handled by read_token_parameters()) */
2384     for (i = 0; i < num_suffix_list_entries && !parameter_suffix_found; i++)
2385     {
2386       int len_suffix = strlen(suffix_list[i].token);
2387
2388       if (token_suffix_match(token, suffix_list[i].token, -len_suffix))
2389         parameter_suffix_found = TRUE;
2390     }
2391
2392     if (parameter_suffix_found)
2393       continue;
2394
2395     /* ---------- step 0: search for matching base prefix ---------- */
2396
2397     start_pos = 0;
2398     for (i = 0; i < num_base_prefixes && !base_prefix_found; i++)
2399     {
2400       char *base_prefix = base_prefixes[i];
2401       int len_base_prefix = strlen(base_prefix);
2402       boolean ext1_suffix_found = FALSE;
2403       boolean ext2_suffix_found = FALSE;
2404       boolean ext3_suffix_found = FALSE;
2405       boolean exact_match = FALSE;
2406       int base_index = -1;
2407       int ext1_index = -1;
2408       int ext2_index = -1;
2409       int ext3_index = -1;
2410
2411       base_prefix_found = token_suffix_match(token, base_prefix, start_pos);
2412
2413       if (!base_prefix_found)
2414         continue;
2415
2416       base_index = i;
2417
2418       if (start_pos + len_base_prefix == len_token)     /* exact match */
2419       {
2420         exact_match = TRUE;
2421
2422         add_dynamic_file_list_entry(dynamic_file_list,
2423                                     num_dynamic_file_list_entries,
2424                                     extra_file_hash,
2425                                     suffix_list,
2426                                     num_suffix_list_entries,
2427                                     token);
2428         add_property_mapping(property_mapping,
2429                              num_property_mapping_entries,
2430                              base_index, -1, -1, -1,
2431                              current_summarized_file_list_entry);
2432         continue;
2433       }
2434
2435 #if 0
2436       if (IS_PARENT_PROCESS())
2437         printf("---> examining token '%s': search 1st suffix ...\n", token);
2438 #endif
2439
2440       /* ---------- step 1: search for matching first suffix ---------- */
2441
2442       start_pos += len_base_prefix;
2443       for (j = 0; j < num_ext1_suffixes && !ext1_suffix_found; j++)
2444       {
2445         char *ext1_suffix = ext1_suffixes[j];
2446         int len_ext1_suffix = strlen(ext1_suffix);
2447
2448         ext1_suffix_found = token_suffix_match(token, ext1_suffix, start_pos);
2449
2450         if (!ext1_suffix_found)
2451           continue;
2452
2453         ext1_index = j;
2454
2455         if (start_pos + len_ext1_suffix == len_token)   /* exact match */
2456         {
2457           exact_match = TRUE;
2458
2459           add_dynamic_file_list_entry(dynamic_file_list,
2460                                       num_dynamic_file_list_entries,
2461                                       extra_file_hash,
2462                                       suffix_list,
2463                                       num_suffix_list_entries,
2464                                       token);
2465           add_property_mapping(property_mapping,
2466                                num_property_mapping_entries,
2467                                base_index, ext1_index, -1, -1,
2468                                current_summarized_file_list_entry);
2469           continue;
2470         }
2471
2472         start_pos += len_ext1_suffix;
2473       }
2474
2475       if (exact_match)
2476         break;
2477
2478 #if 0
2479       if (IS_PARENT_PROCESS())
2480         printf("---> examining token '%s': search 2nd suffix ...\n", token);
2481 #endif
2482
2483       /* ---------- step 2: search for matching second suffix ---------- */
2484
2485       for (k = 0; k < num_ext2_suffixes && !ext2_suffix_found; k++)
2486       {
2487         char *ext2_suffix = ext2_suffixes[k];
2488         int len_ext2_suffix = strlen(ext2_suffix);
2489
2490         ext2_suffix_found = token_suffix_match(token, ext2_suffix, start_pos);
2491
2492         if (!ext2_suffix_found)
2493           continue;
2494
2495         ext2_index = k;
2496
2497         if (start_pos + len_ext2_suffix == len_token)   /* exact match */
2498         {
2499           exact_match = TRUE;
2500
2501           add_dynamic_file_list_entry(dynamic_file_list,
2502                                       num_dynamic_file_list_entries,
2503                                       extra_file_hash,
2504                                       suffix_list,
2505                                       num_suffix_list_entries,
2506                                       token);
2507           add_property_mapping(property_mapping,
2508                                num_property_mapping_entries,
2509                                base_index, ext1_index, ext2_index, -1,
2510                                current_summarized_file_list_entry);
2511           continue;
2512         }
2513
2514         start_pos += len_ext2_suffix;
2515       }
2516
2517       if (exact_match)
2518         break;
2519
2520 #if 0
2521       if (IS_PARENT_PROCESS())
2522         printf("---> examining token '%s': search 3rd suffix ...\n",token);
2523 #endif
2524
2525       /* ---------- step 3: search for matching third suffix ---------- */
2526
2527       for (l = 0; l < num_ext3_suffixes && !ext3_suffix_found; l++)
2528       {
2529         char *ext3_suffix = ext3_suffixes[l];
2530         int len_ext3_suffix = strlen(ext3_suffix);
2531
2532         ext3_suffix_found = token_suffix_match(token, ext3_suffix, start_pos);
2533
2534         if (!ext3_suffix_found)
2535           continue;
2536
2537         ext3_index = l;
2538
2539         if (start_pos + len_ext3_suffix == len_token) /* exact match */
2540         {
2541           exact_match = TRUE;
2542
2543           add_dynamic_file_list_entry(dynamic_file_list,
2544                                       num_dynamic_file_list_entries,
2545                                       extra_file_hash,
2546                                       suffix_list,
2547                                       num_suffix_list_entries,
2548                                       token);
2549           add_property_mapping(property_mapping,
2550                                num_property_mapping_entries,
2551                                base_index, ext1_index, ext2_index, ext3_index,
2552                                current_summarized_file_list_entry);
2553           continue;
2554         }
2555       }
2556     }
2557   }
2558   END_HASH_ITERATION(extra_file_hash, itr)
2559
2560   if (artwork_info->num_dynamic_file_list_entries > 0)
2561   {
2562     artwork_info->dynamic_artwork_list =
2563       checked_calloc(artwork_info->num_dynamic_file_list_entries *
2564                      artwork_info->sizeof_artwork_list_entry);
2565   }
2566
2567   if (options.verbose && IS_PARENT_PROCESS())
2568   {
2569     SetupFileList *setup_file_list, *list;
2570     boolean dynamic_tokens_found = FALSE;
2571     boolean unknown_tokens_found = FALSE;
2572     boolean undefined_values_found = (hashtable_count(empty_file_hash) != 0);
2573
2574     if ((setup_file_list = loadSetupFileList(filename)) == NULL)
2575       Error(ERR_EXIT, "loadSetupFileHash works, but loadSetupFileList fails");
2576
2577     BEGIN_HASH_ITERATION(extra_file_hash, itr)
2578     {
2579       if (strEqual(HASH_ITERATION_VALUE(itr), known_token_value))
2580         dynamic_tokens_found = TRUE;
2581       else
2582         unknown_tokens_found = TRUE;
2583     }
2584     END_HASH_ITERATION(extra_file_hash, itr)
2585
2586     if (options.debug && dynamic_tokens_found)
2587     {
2588       Error(ERR_RETURN_LINE, "-");
2589       Error(ERR_RETURN, "dynamic token(s) found in config file:");
2590       Error(ERR_RETURN, "- config file: '%s'", filename);
2591
2592       for (list = setup_file_list; list != NULL; list = list->next)
2593       {
2594         char *value = getHashEntry(extra_file_hash, list->token);
2595
2596         if (value != NULL && strEqual(value, known_token_value))
2597           Error(ERR_RETURN, "- dynamic token: '%s'", list->token);
2598       }
2599
2600       Error(ERR_RETURN_LINE, "-");
2601     }
2602
2603     if (unknown_tokens_found)
2604     {
2605       Error(ERR_RETURN_LINE, "-");
2606       Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
2607       Error(ERR_RETURN, "- config file: '%s'", filename);
2608
2609       for (list = setup_file_list; list != NULL; list = list->next)
2610       {
2611         char *value = getHashEntry(extra_file_hash, list->token);
2612
2613         if (value != NULL && !strEqual(value, known_token_value))
2614           Error(ERR_RETURN, "- dynamic token: '%s'", list->token);
2615       }
2616
2617       Error(ERR_RETURN_LINE, "-");
2618     }
2619
2620     if (undefined_values_found)
2621     {
2622       Error(ERR_RETURN_LINE, "-");
2623       Error(ERR_RETURN, "warning: undefined values found in config file:");
2624       Error(ERR_RETURN, "- config file: '%s'", filename);
2625
2626       for (list = setup_file_list; list != NULL; list = list->next)
2627       {
2628         char *value = getHashEntry(empty_file_hash, list->token);
2629
2630         if (value != NULL)
2631           Error(ERR_RETURN, "- undefined value for token: '%s'", list->token);
2632       }
2633
2634       Error(ERR_RETURN_LINE, "-");
2635     }
2636
2637     freeSetupFileList(setup_file_list);
2638   }
2639
2640   freeSetupFileHash(extra_file_hash);
2641   freeSetupFileHash(empty_file_hash);
2642
2643 #if 0
2644   for (i = 0; i < num_file_list_entries; i++)
2645   {
2646     printf("'%s' ", file_list[i].token);
2647     if (file_list[i].filename)
2648       printf("-> '%s'\n", file_list[i].filename);
2649     else
2650       printf("-> UNDEFINED [-> '%s']\n", file_list[i].default_filename);
2651   }
2652 #endif
2653 }
2654
2655 void LoadArtworkConfig(struct ArtworkListInfo *artwork_info)
2656 {
2657   struct FileInfo *file_list = artwork_info->file_list;
2658   int num_file_list_entries = artwork_info->num_file_list_entries;
2659   int num_suffix_list_entries = artwork_info->num_suffix_list_entries;
2660   char *filename_base = UNDEFINED_FILENAME, *filename_local;
2661   int i, j;
2662
2663   DrawInitText("Loading artwork config:", 120, FC_GREEN);
2664   DrawInitText(ARTWORKINFO_FILENAME(artwork_info->type), 150, FC_YELLOW);
2665
2666   /* always start with reliable default values */
2667   for (i = 0; i < num_file_list_entries; i++)
2668   {
2669     setString(&file_list[i].filename, file_list[i].default_filename);
2670
2671     for (j = 0; j < num_suffix_list_entries; j++)
2672       setString(&file_list[i].parameter[j], file_list[i].default_parameter[j]);
2673
2674     file_list[i].redefined = FALSE;
2675     file_list[i].fallback_to_default = FALSE;
2676   }
2677
2678   /* free previous dynamic artwork file array */
2679   if (artwork_info->dynamic_file_list != NULL)
2680   {
2681     for (i = 0; i < artwork_info->num_dynamic_file_list_entries; i++)
2682     {
2683       free(artwork_info->dynamic_file_list[i].token);
2684       free(artwork_info->dynamic_file_list[i].filename);
2685       free(artwork_info->dynamic_file_list[i].parameter);
2686     }
2687
2688     free(artwork_info->dynamic_file_list);
2689     artwork_info->dynamic_file_list = NULL;
2690
2691     FreeCustomArtworkList(artwork_info, &artwork_info->dynamic_artwork_list,
2692                           &artwork_info->num_dynamic_file_list_entries);
2693   }
2694
2695   /* free previous property mapping */
2696   if (artwork_info->property_mapping != NULL)
2697   {
2698     free(artwork_info->property_mapping);
2699
2700     artwork_info->property_mapping = NULL;
2701     artwork_info->num_property_mapping_entries = 0;
2702   }
2703
2704   if (!SETUP_OVERRIDE_ARTWORK(setup, artwork_info->type))
2705   {
2706     /* first look for special artwork configured in level series config */
2707     filename_base = getCustomArtworkLevelConfigFilename(artwork_info->type);
2708
2709     if (fileExists(filename_base))
2710       LoadArtworkConfigFromFilename(artwork_info, filename_base);
2711   }
2712
2713   filename_local = getCustomArtworkConfigFilename(artwork_info->type);
2714
2715   if (filename_local != NULL && !strEqual(filename_base, filename_local))
2716     LoadArtworkConfigFromFilename(artwork_info, filename_local);
2717 }
2718
2719 static void deleteArtworkListEntry(struct ArtworkListInfo *artwork_info,
2720                                    struct ListNodeInfo **listnode)
2721 {
2722   if (*listnode)
2723   {
2724     char *filename = (*listnode)->source_filename;
2725
2726     if (--(*listnode)->num_references <= 0)
2727       deleteNodeFromList(&artwork_info->content_list, filename,
2728                          artwork_info->free_artwork);
2729
2730     *listnode = NULL;
2731   }
2732 }
2733
2734 static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
2735                                     struct ListNodeInfo **listnode,
2736                                     struct FileInfo *file_list_entry)
2737 {
2738   char *init_text[] =
2739   {
2740     "Loading graphics:",
2741     "Loading sounds:",
2742     "Loading music:"
2743   };
2744
2745   ListNode *node;
2746   char *basename = file_list_entry->filename;
2747   char *filename = getCustomArtworkFilename(basename, artwork_info->type);
2748
2749   if (filename == NULL)
2750   {
2751     Error(ERR_WARN, "cannot find artwork file '%s'", basename);
2752
2753     basename = file_list_entry->default_filename;
2754
2755     /* dynamic artwork has no default filename / skip empty default artwork */
2756     if (basename == NULL || strEqual(basename, UNDEFINED_FILENAME))
2757       return;
2758
2759     file_list_entry->fallback_to_default = TRUE;
2760
2761     Error(ERR_WARN, "trying default artwork file '%s'", basename);
2762
2763     filename = getCustomArtworkFilename(basename, artwork_info->type);
2764
2765     if (filename == NULL)
2766     {
2767       int error_mode = ERR_WARN;
2768
2769       /* we can get away without sounds and music, but not without graphics */
2770       if (*listnode == NULL && artwork_info->type == ARTWORK_TYPE_GRAPHICS)
2771         error_mode = ERR_EXIT;
2772
2773       Error(error_mode, "cannot find default artwork file '%s'", basename);
2774
2775       return;
2776     }
2777   }
2778
2779   /* check if the old and the new artwork file are the same */
2780   if (*listnode && strEqual((*listnode)->source_filename, filename))
2781   {
2782     /* The old and new artwork are the same (have the same filename and path).
2783        This usually means that this artwork does not exist in this artwork set
2784        and a fallback to the existing artwork is done. */
2785
2786 #if 0
2787     printf("[artwork '%s' already exists (same list entry)]\n", filename);
2788 #endif
2789
2790     return;
2791   }
2792
2793   /* delete existing artwork file entry */
2794   deleteArtworkListEntry(artwork_info, listnode);
2795
2796   /* check if the new artwork file already exists in the list of artworks */
2797   if ((node = getNodeFromKey(artwork_info->content_list, filename)) != NULL)
2798   {
2799 #if 0
2800       printf("[artwork '%s' already exists (other list entry)]\n", filename);
2801 #endif
2802
2803       *listnode = (struct ListNodeInfo *)node->content;
2804       (*listnode)->num_references++;
2805
2806       return;
2807   }
2808
2809   DrawInitText(init_text[artwork_info->type], 120, FC_GREEN);
2810   DrawInitText(basename, 150, FC_YELLOW);
2811
2812   if ((*listnode = artwork_info->load_artwork(filename)) != NULL)
2813   {
2814 #if 0
2815       printf("[adding new artwork '%s']\n", filename);
2816 #endif
2817
2818     (*listnode)->num_references = 1;
2819     addNodeToList(&artwork_info->content_list, (*listnode)->source_filename,
2820                   *listnode);
2821   }
2822   else
2823   {
2824     int error_mode = ERR_WARN;
2825
2826     /* we can get away without sounds and music, but not without graphics */
2827     if (artwork_info->type == ARTWORK_TYPE_GRAPHICS)
2828       error_mode = ERR_EXIT;
2829
2830     Error(error_mode, "cannot load artwork file '%s'", basename);
2831     return;
2832   }
2833 }
2834
2835 static void LoadCustomArtwork(struct ArtworkListInfo *artwork_info,
2836                               struct ListNodeInfo **listnode,
2837                               struct FileInfo *file_list_entry)
2838 {
2839 #if 0
2840   printf("GOT CUSTOM ARTWORK FILE '%s'\n", filename);
2841 #endif
2842
2843   if (strEqual(file_list_entry->filename, UNDEFINED_FILENAME))
2844   {
2845     deleteArtworkListEntry(artwork_info, listnode);
2846     return;
2847   }
2848
2849   replaceArtworkListEntry(artwork_info, listnode, file_list_entry);
2850 }
2851
2852 void ReloadCustomArtworkList(struct ArtworkListInfo *artwork_info)
2853 {
2854   struct FileInfo *file_list = artwork_info->file_list;
2855   struct FileInfo *dynamic_file_list = artwork_info->dynamic_file_list;
2856   int num_file_list_entries = artwork_info->num_file_list_entries;
2857   int num_dynamic_file_list_entries =
2858     artwork_info->num_dynamic_file_list_entries;
2859   int i;
2860
2861   for (i = 0; i < num_file_list_entries; i++)
2862     LoadCustomArtwork(artwork_info, &artwork_info->artwork_list[i],
2863                       &file_list[i]);
2864
2865   for (i = 0; i < num_dynamic_file_list_entries; i++)
2866     LoadCustomArtwork(artwork_info, &artwork_info->dynamic_artwork_list[i],
2867                       &dynamic_file_list[i]);
2868
2869 #if 0
2870   dumpList(artwork_info->content_list);
2871 #endif
2872 }
2873
2874 static void FreeCustomArtworkList(struct ArtworkListInfo *artwork_info,
2875                                   struct ListNodeInfo ***list,
2876                                   int *num_list_entries)
2877 {
2878   int i;
2879
2880   if (*list == NULL)
2881     return;
2882
2883   for (i = 0; i < *num_list_entries; i++)
2884     deleteArtworkListEntry(artwork_info, &(*list)[i]);
2885   free(*list);
2886
2887   *list = NULL;
2888   *num_list_entries = 0;
2889 }
2890
2891 void FreeCustomArtworkLists(struct ArtworkListInfo *artwork_info)
2892 {
2893   if (artwork_info == NULL)
2894     return;
2895
2896   FreeCustomArtworkList(artwork_info, &artwork_info->artwork_list,
2897                         &artwork_info->num_file_list_entries);
2898
2899   FreeCustomArtworkList(artwork_info, &artwork_info->dynamic_artwork_list,
2900                         &artwork_info->num_dynamic_file_list_entries);
2901 }
2902
2903
2904 /* ------------------------------------------------------------------------- */
2905 /* functions only needed for non-Unix (non-command-line) systems             */
2906 /* (MS-DOS only; SDL/Windows creates files "stdout.txt" and "stderr.txt")    */
2907 /* (now also added for Windows, to create files in user data directory)      */
2908 /* ------------------------------------------------------------------------- */
2909
2910 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MSDOS)
2911
2912 #define ERROR_BASENAME          "stderr.txt"
2913
2914 static char *error_filename = NULL;
2915
2916 #if defined(PLATFORM_WIN32)
2917
2918 void initErrorFile()
2919 {
2920   if (error_filename == NULL)
2921     error_filename = getPath2(getUserDataDir(), ERROR_BASENAME);
2922
2923   unlink(error_filename);
2924 }
2925
2926 #elif defined(PLATFORM_MSDOS)
2927
2928 void initErrorFile()
2929 {
2930   if (error_filename == NULL)
2931     error_filename = ERROR_BASENAME;
2932
2933   unlink(error_filename);
2934 }
2935
2936 #endif  /* PLATFORM_MSDOS */
2937
2938 FILE *openErrorFile()
2939 {
2940   FILE *error_file = fopen(error_filename, MODE_APPEND);
2941
2942   if (error_file == NULL)
2943     fprintf_newline(stderr, "ERROR: cannot open file '%s' for appending!",
2944                     error_filename);
2945
2946   return error_file;
2947 }
2948
2949 void dumpErrorFile()
2950 {
2951   FILE *error_file = fopen(error_filename, MODE_READ);
2952
2953   if (error_file != NULL)
2954   {
2955     while (!feof(error_file))
2956       fputc(fgetc(error_file), stderr);
2957
2958     fclose(error_file);
2959   }
2960 }
2961
2962 #endif  /* PLATFORM_WIN32 || PLATFORM_MSDOS */
2963
2964
2965 /* ------------------------------------------------------------------------- */
2966 /* the following is only for debugging purpose and normally not used         */
2967 /* ------------------------------------------------------------------------- */
2968
2969 #define DEBUG_NUM_TIMESTAMPS    3
2970
2971 void debug_print_timestamp(int counter_nr, char *message)
2972 {
2973   static long counter[DEBUG_NUM_TIMESTAMPS][2];
2974
2975   if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
2976     Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
2977
2978   counter[counter_nr][0] = Counter();
2979
2980   if (message)
2981     printf("%s %.2f seconds\n", message,
2982            (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
2983
2984   counter[counter_nr][1] = Counter();
2985 }
2986
2987 void debug_print_parent_only(char *format, ...)
2988 {
2989   if (!IS_PARENT_PROCESS())
2990     return;
2991
2992   if (format)
2993   {
2994     va_list ap;
2995
2996     va_start(ap, format);
2997     vprintf(format, ap);
2998     va_end(ap);
2999
3000     printf("\n");
3001   }
3002 }