rnd-20020519-2-src
[rocksndiamonds.git] / src / libgame / misc.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 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
33
34 #if defined(PLATFORM_MSDOS)
35 volatile unsigned long counter = 0;
36
37 void increment_counter()
38 {
39   counter++;
40 }
41
42 END_OF_FUNCTION(increment_counter);
43 #endif
44
45
46 /* maximal allowed length of a command line option */
47 #define MAX_OPTION_LEN          256
48
49 #ifdef TARGET_SDL
50 static unsigned long mainCounter(int mode)
51 {
52   static unsigned long base_ms = 0;
53   unsigned long current_ms;
54   unsigned long counter_ms;
55
56   current_ms = SDL_GetTicks();
57
58   /* reset base time in case of counter initializing or wrap-around */
59   if (mode == INIT_COUNTER || current_ms < base_ms)
60     base_ms = current_ms;
61
62   counter_ms = current_ms - base_ms;
63
64   return counter_ms;            /* return milliseconds since last init */
65 }
66
67 #else /* !TARGET_SDL */
68
69 #if defined(PLATFORM_UNIX)
70 static unsigned long mainCounter(int mode)
71 {
72   static struct timeval base_time = { 0, 0 };
73   struct timeval current_time;
74   unsigned long counter_ms;
75
76   gettimeofday(&current_time, NULL);
77
78   /* reset base time in case of counter initializing or wrap-around */
79   if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
80     base_time = current_time;
81
82   counter_ms = (current_time.tv_sec  - base_time.tv_sec)  * 1000
83              + (current_time.tv_usec - base_time.tv_usec) / 1000;
84
85   return counter_ms;            /* return milliseconds since last init */
86 }
87 #endif /* PLATFORM_UNIX */
88 #endif /* !TARGET_SDL */
89
90 void InitCounter()              /* set counter back to zero */
91 {
92 #if !defined(PLATFORM_MSDOS)
93   mainCounter(INIT_COUNTER);
94 #else
95   LOCK_VARIABLE(counter);
96   LOCK_FUNCTION(increment_counter);
97   install_int_ex(increment_counter, BPS_TO_TIMER(100));
98 #endif
99 }
100
101 unsigned long Counter() /* get milliseconds since last call of InitCounter() */
102 {
103 #if !defined(PLATFORM_MSDOS)
104   return mainCounter(READ_COUNTER);
105 #else
106   return (counter * 10);
107 #endif
108 }
109
110 static void sleep_milliseconds(unsigned long milliseconds_delay)
111 {
112   boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
113
114 #if defined(PLATFORM_MSDOS)
115   /* don't use select() to perform waiting operations under DOS
116      environment; always use a busy loop for waiting instead */
117   do_busy_waiting = TRUE;
118 #endif
119
120   if (do_busy_waiting)
121   {
122     /* we want to wait only a few ms -- if we assume that we have a
123        kernel timer resolution of 10 ms, we would wait far to long;
124        therefore it's better to do a short interval of busy waiting
125        to get our sleeping time more accurate */
126
127     unsigned long base_counter = Counter(), actual_counter = Counter();
128
129     while (actual_counter < base_counter + milliseconds_delay &&
130            actual_counter >= base_counter)
131       actual_counter = Counter();
132   }
133   else
134   {
135 #if defined(TARGET_SDL)
136     SDL_Delay(milliseconds_delay);
137 #else
138     struct timeval delay;
139
140     delay.tv_sec  = milliseconds_delay / 1000;
141     delay.tv_usec = 1000 * (milliseconds_delay % 1000);
142
143     if (select(0, NULL, NULL, NULL, &delay) != 0)
144       Error(ERR_WARN, "sleep_milliseconds(): select() failed");
145 #endif
146   }
147 }
148
149 void Delay(unsigned long delay) /* Sleep specified number of milliseconds */
150 {
151   sleep_milliseconds(delay);
152 }
153
154 boolean FrameReached(unsigned long *frame_counter_var,
155                      unsigned long frame_delay)
156 {
157   unsigned long actual_frame_counter = FrameCounter;
158
159   if (actual_frame_counter < *frame_counter_var + frame_delay &&
160       actual_frame_counter >= *frame_counter_var)
161     return FALSE;
162
163   *frame_counter_var = actual_frame_counter;
164
165   return TRUE;
166 }
167
168 boolean DelayReached(unsigned long *counter_var,
169                      unsigned long delay)
170 {
171   unsigned long actual_counter = Counter();
172
173   if (actual_counter < *counter_var + delay &&
174       actual_counter >= *counter_var)
175     return FALSE;
176
177   *counter_var = actual_counter;
178
179   return TRUE;
180 }
181
182 void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
183 {
184   unsigned long actual_counter;
185
186   while(1)
187   {
188     actual_counter = Counter();
189
190     if (actual_counter < *counter_var + delay &&
191         actual_counter >= *counter_var)
192       sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
193     else
194       break;
195   }
196
197   *counter_var = actual_counter;
198 }
199
200 /* int2str() returns a number converted to a string;
201    the used memory is static, but will be overwritten by later calls,
202    so if you want to save the result, copy it to a private string buffer;
203    there can be 10 local calls of int2str() without buffering the result --
204    the 11th call will then destroy the result from the first call and so on.
205 */
206
207 char *int2str(int number, int size)
208 {
209   static char shift_array[10][40];
210   static int shift_counter = 0;
211   char *s = shift_array[shift_counter];
212
213   shift_counter = (shift_counter + 1) % 10;
214
215   if (size > 20)
216     size = 20;
217
218   if (size)
219   {
220     sprintf(s, "                    %09d", number);
221     return &s[strlen(s) - size];
222   }
223   else
224   {
225     sprintf(s, "%d", number);
226     return s;
227   }
228 }
229
230 unsigned int SimpleRND(unsigned int max)
231 {
232 #if defined(TARGET_SDL)
233   static unsigned long root = 654321;
234   unsigned long current_ms;
235
236   current_ms = SDL_GetTicks();
237   root = root * 4253261 + current_ms;
238   return (root % max);
239 #else
240   static unsigned long root = 654321;
241   struct timeval current_time;
242
243   gettimeofday(&current_time, NULL);
244   root = root * 4253261 + current_time.tv_sec + current_time.tv_usec;
245   return (root % max);
246 #endif
247 }
248
249 #ifdef DEBUG
250 static unsigned int last_RND_value = 0;
251
252 unsigned int last_RND()
253 {
254   return last_RND_value;
255 }
256 #endif
257
258 unsigned int RND(unsigned int max)
259 {
260 #ifdef DEBUG
261   return (last_RND_value = random_linux_libc() % max);
262 #else
263   return (random_linux_libc() % max);
264 #endif
265 }
266
267 unsigned int InitRND(long seed)
268 {
269 #if defined(TARGET_SDL)
270   unsigned long current_ms;
271
272   if (seed == NEW_RANDOMIZE)
273   {
274     current_ms = SDL_GetTicks();
275     srandom_linux_libc((unsigned int) current_ms);
276     return (unsigned int) current_ms;
277   }
278   else
279   {
280     srandom_linux_libc((unsigned int) seed);
281     return (unsigned int) seed;
282   }
283 #else
284   struct timeval current_time;
285
286   if (seed == NEW_RANDOMIZE)
287   {
288     gettimeofday(&current_time, NULL);
289     srandom_linux_libc((unsigned int) current_time.tv_usec);
290     return (unsigned int) current_time.tv_usec;
291   }
292   else
293   {
294     srandom_linux_libc((unsigned int) seed);
295     return (unsigned int) seed;
296   }
297 #endif
298 }
299
300 char *getLoginName()
301 {
302 #if defined(PLATFORM_WIN32)
303   return ANONYMOUS_NAME;
304 #else
305   struct passwd *pwd;
306
307   if ((pwd = getpwuid(getuid())) == NULL)
308     return ANONYMOUS_NAME;
309   else
310     return pwd->pw_name;
311 #endif
312 }
313
314 char *getRealName()
315 {
316 #if defined(PLATFORM_UNIX)
317   struct passwd *pwd;
318
319   if ((pwd = getpwuid(getuid())) == NULL || strlen(pwd->pw_gecos) == 0)
320     return ANONYMOUS_NAME;
321   else
322   {
323     static char real_name[1024];
324     char *from_ptr = pwd->pw_gecos, *to_ptr = real_name;
325
326     if (strchr(pwd->pw_gecos, 'ß') == NULL)
327       return pwd->pw_gecos;
328
329     /* the user's real name contains a 'ß' character (german sharp s),
330        which has no equivalent in upper case letters (which our fonts use) */
331     while (*from_ptr != '\0' && (long)(to_ptr - real_name) < 1024 - 2)
332     {
333       if (*from_ptr != 'ß')
334         *to_ptr++ = *from_ptr++;
335       else
336       {
337         from_ptr++;
338         *to_ptr++ = 's';
339         *to_ptr++ = 's';
340       }
341     }
342     *to_ptr = '\0';
343
344     return real_name;
345   }
346 #else /* !PLATFORM_UNIX */
347   return ANONYMOUS_NAME;
348 #endif
349 }
350
351 char *getHomeDir()
352 {
353 #if defined(PLATFORM_UNIX)
354   static char *home_dir = NULL;
355
356   if (!home_dir)
357   {
358     if (!(home_dir = getenv("HOME")))
359     {
360       struct passwd *pwd;
361
362       if ((pwd = getpwuid(getuid())))
363         home_dir = pwd->pw_dir;
364       else
365         home_dir = ".";
366     }
367   }
368
369   return home_dir;
370 #else
371   return ".";
372 #endif
373 }
374
375 char *getPath2(char *path1, char *path2)
376 {
377   char *complete_path = checked_malloc(strlen(path1) + 1 +
378                                        strlen(path2) + 1);
379
380   sprintf(complete_path, "%s/%s", path1, path2);
381   return complete_path;
382 }
383
384 char *getPath3(char *path1, char *path2, char *path3)
385 {
386   char *complete_path = checked_malloc(strlen(path1) + 1 +
387                                        strlen(path2) + 1 +
388                                        strlen(path3) + 1);
389
390   sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
391   return complete_path;
392 }
393
394 char *getStringCopy(char *s)
395 {
396   char *s_copy;
397
398   if (s == NULL)
399     return NULL;
400
401   s_copy = checked_malloc(strlen(s) + 1);
402
403   strcpy(s_copy, s);
404   return s_copy;
405 }
406
407 char *getStringToLower(char *s)
408 {
409   char *s_copy = checked_malloc(strlen(s) + 1);
410   char *s_ptr = s_copy;
411
412   while (*s)
413     *s_ptr++ = tolower(*s++);
414   *s_ptr = '\0';
415
416   return s_copy;
417 }
418
419 void GetOptions(char *argv[])
420 {
421   char **options_left = &argv[1];
422
423   /* initialize global program options */
424   options.display_name = NULL;
425   options.server_host = NULL;
426   options.server_port = 0;
427   options.ro_base_directory = RO_BASE_PATH;
428   options.rw_base_directory = RW_BASE_PATH;
429   options.level_directory = RO_BASE_PATH "/" LEVELS_DIRECTORY;
430   options.graphics_directory = RO_BASE_PATH "/" GRAPHICS_DIRECTORY;
431   options.sounds_directory = RO_BASE_PATH "/" SOUNDS_DIRECTORY;
432   options.music_directory = RO_BASE_PATH "/" MUSIC_DIRECTORY;
433   options.serveronly = FALSE;
434   options.network = FALSE;
435   options.verbose = FALSE;
436   options.debug = FALSE;
437   options.debug_command = NULL;
438
439   while (*options_left)
440   {
441     char option_str[MAX_OPTION_LEN];
442     char *option = options_left[0];
443     char *next_option = options_left[1];
444     char *option_arg = NULL;
445     int option_len = strlen(option);
446
447     if (option_len >= MAX_OPTION_LEN)
448       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
449
450     strcpy(option_str, option);                 /* copy argument into buffer */
451     option = option_str;
452
453     if (strcmp(option, "--") == 0)              /* stop scanning arguments */
454       break;
455
456     if (strncmp(option, "--", 2) == 0)          /* treat '--' like '-' */
457       option++;
458
459     option_arg = strchr(option, '=');
460     if (option_arg == NULL)                     /* no '=' in option */
461       option_arg = next_option;
462     else
463     {
464       *option_arg++ = '\0';                     /* cut argument from option */
465       if (*option_arg == '\0')                  /* no argument after '=' */
466         Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
467     }
468
469     option_len = strlen(option);
470
471     if (strcmp(option, "-") == 0)
472       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
473     else if (strncmp(option, "-help", option_len) == 0)
474     {
475       printf("Usage: %s [options] [<server host> [<server port>]]\n"
476              "Options:\n"
477              "  -d, --display <host>[:<scr>]  X server display\n"
478              "  -b, --basepath <directory>    alternative base directory\n"
479              "  -l, --level <directory>       alternative level directory\n"
480              "  -g, --graphics <directory>    alternative graphics directory\n"
481              "  -s, --sounds <directory>      alternative sounds directory\n"
482              "  -m, --music <directory>       alternative music directory\n"
483              "  -n, --network                 network multiplayer game\n"
484              "      --serveronly              only start network server\n"
485              "  -v, --verbose                 verbose mode\n"
486              "      --debug                   display debugging information\n",
487              program.command_basename);
488
489       if (options.debug)
490         printf("      --debug-command <command> execute special command\n");
491
492       exit(0);
493     }
494     else if (strncmp(option, "-display", option_len) == 0)
495     {
496       if (option_arg == NULL)
497         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
498
499       options.display_name = option_arg;
500       if (option_arg == next_option)
501         options_left++;
502     }
503     else if (strncmp(option, "-basepath", option_len) == 0)
504     {
505       if (option_arg == NULL)
506         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
507
508       /* this should be extended to separate options for ro and rw data */
509       options.ro_base_directory = option_arg;
510       options.rw_base_directory = option_arg;
511       if (option_arg == next_option)
512         options_left++;
513
514       /* adjust path for level directory accordingly */
515       options.level_directory =
516         getPath2(options.ro_base_directory, LEVELS_DIRECTORY);
517     }
518     else if (strncmp(option, "-levels", option_len) == 0)
519     {
520       if (option_arg == NULL)
521         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
522
523       options.level_directory = option_arg;
524       if (option_arg == next_option)
525         options_left++;
526     }
527     else if (strncmp(option, "-graphics", option_len) == 0)
528     {
529       if (option_arg == NULL)
530         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
531
532       options.graphics_directory = option_arg;
533       if (option_arg == next_option)
534         options_left++;
535     }
536     else if (strncmp(option, "-sounds", option_len) == 0)
537     {
538       if (option_arg == NULL)
539         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
540
541       options.sounds_directory = option_arg;
542       if (option_arg == next_option)
543         options_left++;
544     }
545     else if (strncmp(option, "-music", option_len) == 0)
546     {
547       if (option_arg == NULL)
548         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
549
550       options.music_directory = option_arg;
551       if (option_arg == next_option)
552         options_left++;
553     }
554     else if (strncmp(option, "-network", option_len) == 0)
555     {
556       options.network = TRUE;
557     }
558     else if (strncmp(option, "-serveronly", option_len) == 0)
559     {
560       options.serveronly = TRUE;
561     }
562     else if (strncmp(option, "-verbose", option_len) == 0)
563     {
564       options.verbose = TRUE;
565     }
566     else if (strncmp(option, "-debug", option_len) == 0)
567     {
568       options.debug = TRUE;
569     }
570     else if (strncmp(option, "-debug-command", option_len) == 0)
571     {
572       if (option_arg == NULL)
573         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
574
575       options.debug_command = option_arg;
576       if (option_arg == next_option)
577         options_left++;
578     }
579     else if (*option == '-')
580     {
581       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
582     }
583     else if (options.server_host == NULL)
584     {
585       options.server_host = *options_left;
586     }
587     else if (options.server_port == 0)
588     {
589       options.server_port = atoi(*options_left);
590       if (options.server_port < 1024)
591         Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
592     }
593     else
594       Error(ERR_EXIT_HELP, "too many arguments");
595
596     options_left++;
597   }
598 }
599
600 /* used by SetError() and GetError() to store internal error messages */
601 static char internal_error[1024];       /* this is bad */
602
603 void SetError(char *format, ...)
604 {
605   va_list ap;
606
607   va_start(ap, format);
608   vsprintf(internal_error, format, ap);
609   va_end(ap);
610 }
611
612 char *GetError()
613 {
614   return internal_error;
615 }
616
617 void Error(int mode, char *format, ...)
618 {
619   char *process_name = "";
620   FILE *error = stderr;
621   char *newline = "\n";
622
623   /* display warnings only when running in verbose mode */
624   if (mode & ERR_WARN && !options.verbose)
625     return;
626
627 #if !defined(PLATFORM_UNIX)
628   newline = "\r\n";
629
630   if ((error = openErrorFile()) == NULL)
631   {
632     printf("Cannot write to error output file!%s", newline);
633     program.exit_function(1);
634   }
635 #endif
636
637   if (mode & ERR_SOUND_SERVER)
638     process_name = " sound server";
639   else if (mode & ERR_NETWORK_SERVER)
640     process_name = " network server";
641   else if (mode & ERR_NETWORK_CLIENT)
642     process_name = " network client **";
643
644   if (format)
645   {
646     va_list ap;
647
648     fprintf(error, "%s%s: ", program.command_basename, process_name);
649
650     if (mode & ERR_WARN)
651       fprintf(error, "warning: ");
652
653     va_start(ap, format);
654     vfprintf(error, format, ap);
655     va_end(ap);
656   
657     fprintf(error, "%s", newline);
658   }
659   
660   if (mode & ERR_HELP)
661     fprintf(error, "%s: Try option '--help' for more information.%s",
662             program.command_basename, newline);
663
664   if (mode & ERR_EXIT)
665     fprintf(error, "%s%s: aborting%s",
666             program.command_basename, process_name, newline);
667
668   if (error != stderr)
669     fclose(error);
670
671   if (mode & ERR_EXIT)
672   {
673     if (mode & ERR_FROM_SERVER)
674       exit(1);                          /* child process: normal exit */
675     else
676       program.exit_function(1);         /* main process: clean up stuff */
677   }
678 }
679
680 void *checked_malloc(unsigned long size)
681 {
682   void *ptr;
683
684   ptr = malloc(size);
685
686   if (ptr == NULL)
687     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
688
689   return ptr;
690 }
691
692 void *checked_calloc(unsigned long size)
693 {
694   void *ptr;
695
696   ptr = calloc(1, size);
697
698   if (ptr == NULL)
699     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
700
701   return ptr;
702 }
703
704 void *checked_realloc(void *ptr, unsigned long size)
705 {
706   ptr = realloc(ptr, size);
707
708   if (ptr == NULL)
709     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
710
711   return ptr;
712 }
713
714 inline void swap_numbers(int *i1, int *i2)
715 {
716   int help = *i1;
717
718   *i1 = *i2;
719   *i2 = help;
720 }
721
722 inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
723 {
724   int help_x = *x1;
725   int help_y = *y1;
726
727   *x1 = *x2;
728   *x2 = help_x;
729
730   *y1 = *y2;
731   *y2 = help_y;
732 }
733
734 short getFile16BitInteger(FILE *file, int byte_order)
735 {
736   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
737     return ((fgetc(file) <<  8) |
738             (fgetc(file) <<  0));
739   else           /* BYTE_ORDER_LITTLE_ENDIAN */
740     return ((fgetc(file) <<  0) |
741             (fgetc(file) <<  8));
742 }
743
744 void putFile16BitInteger(FILE *file, short value, int byte_order)
745 {
746   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
747   {
748     fputc((value >>  8) & 0xff, file);
749     fputc((value >>  0) & 0xff, file);
750   }
751   else           /* BYTE_ORDER_LITTLE_ENDIAN */
752   {
753     fputc((value >>  0) & 0xff, file);
754     fputc((value >>  8) & 0xff, file);
755   }
756 }
757
758 int getFile32BitInteger(FILE *file, int byte_order)
759 {
760   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
761     return ((fgetc(file) << 24) |
762             (fgetc(file) << 16) |
763             (fgetc(file) <<  8) |
764             (fgetc(file) <<  0));
765   else           /* BYTE_ORDER_LITTLE_ENDIAN */
766     return ((fgetc(file) <<  0) |
767             (fgetc(file) <<  8) |
768             (fgetc(file) << 16) |
769             (fgetc(file) << 24));
770 }
771
772 void putFile32BitInteger(FILE *file, int value, int byte_order)
773 {
774   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
775   {
776     fputc((value >> 24) & 0xff, file);
777     fputc((value >> 16) & 0xff, file);
778     fputc((value >>  8) & 0xff, file);
779     fputc((value >>  0) & 0xff, file);
780   }
781   else           /* BYTE_ORDER_LITTLE_ENDIAN */
782   {
783     fputc((value >>  0) & 0xff, file);
784     fputc((value >>  8) & 0xff, file);
785     fputc((value >> 16) & 0xff, file);
786     fputc((value >> 24) & 0xff, file);
787   }
788 }
789
790 boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
791                      int byte_order)
792 {
793   const int chunk_name_length = 4;
794
795   /* read chunk name */
796   fgets(chunk_name, chunk_name_length + 1, file);
797
798   if (chunk_size != NULL)
799   {
800     /* read chunk size */
801     *chunk_size = getFile32BitInteger(file, byte_order);
802   }
803
804   return (feof(file) || ferror(file) ? FALSE : TRUE);
805 }
806
807 void putFileChunk(FILE *file, char *chunk_name, int chunk_size,
808                   int byte_order)
809 {
810   /* write chunk name */
811   fputs(chunk_name, file);
812
813   if (chunk_size >= 0)
814   {
815     /* write chunk size */
816     putFile32BitInteger(file, chunk_size, byte_order);
817   }
818 }
819
820 void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
821 {
822   while (bytes-- && !feof(file))
823     fgetc(file);
824 }
825
826 void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
827 {
828   while (bytes--)
829     fputc(0, file);
830 }
831
832 #define TRANSLATE_KEYSYM_TO_KEYNAME     0
833 #define TRANSLATE_KEYSYM_TO_X11KEYNAME  1
834 #define TRANSLATE_KEYNAME_TO_KEYSYM     2
835 #define TRANSLATE_X11KEYNAME_TO_KEYSYM  3
836
837 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
838 {
839   static struct
840   {
841     Key key;
842     char *x11name;
843     char *name;
844   } translate_key[] =
845   {
846     /* normal cursor keys */
847     { KSYM_Left,        "XK_Left",              "cursor left" },
848     { KSYM_Right,       "XK_Right",             "cursor right" },
849     { KSYM_Up,          "XK_Up",                "cursor up" },
850     { KSYM_Down,        "XK_Down",              "cursor down" },
851
852     /* keypad cursor keys */
853 #ifdef KSYM_KP_Left
854     { KSYM_KP_Left,     "XK_KP_Left",           "keypad left" },
855     { KSYM_KP_Right,    "XK_KP_Right",          "keypad right" },
856     { KSYM_KP_Up,       "XK_KP_Up",             "keypad up" },
857     { KSYM_KP_Down,     "XK_KP_Down",           "keypad down" },
858 #endif
859
860     /* other keypad keys */
861 #ifdef KSYM_KP_Enter
862     { KSYM_KP_Enter,    "XK_KP_Enter",          "keypad enter" },
863     { KSYM_KP_Add,      "XK_KP_Add",            "keypad +" },
864     { KSYM_KP_Subtract, "XK_KP_Subtract",       "keypad -" },
865     { KSYM_KP_Multiply, "XK_KP_Multiply",       "keypad mltply" },
866     { KSYM_KP_Divide,   "XK_KP_Divide",         "keypad /" },
867     { KSYM_KP_Separator,"XK_KP_Separator",      "keypad ," },
868 #endif
869
870     /* modifier keys */
871     { KSYM_Shift_L,     "XK_Shift_L",           "left shift" },
872     { KSYM_Shift_R,     "XK_Shift_R",           "right shift" },
873     { KSYM_Control_L,   "XK_Control_L",         "left control" },
874     { KSYM_Control_R,   "XK_Control_R",         "right control" },
875     { KSYM_Meta_L,      "XK_Meta_L",            "left meta" },
876     { KSYM_Meta_R,      "XK_Meta_R",            "right meta" },
877     { KSYM_Alt_L,       "XK_Alt_L",             "left alt" },
878     { KSYM_Alt_R,       "XK_Alt_R",             "right alt" },
879     { KSYM_Super_L,     "XK_Super_L",           "left super" },  /* Win-L */
880     { KSYM_Super_R,     "XK_Super_R",           "right super" }, /* Win-R */
881     { KSYM_Mode_switch, "XK_Mode_switch",       "mode switch" }, /* Alt-R */
882     { KSYM_Multi_key,   "XK_Multi_key",         "multi key" },   /* Ctrl-R */
883
884     /* some special keys */
885     { KSYM_BackSpace,   "XK_BackSpace",         "backspace" },
886     { KSYM_Delete,      "XK_Delete",            "delete" },
887     { KSYM_Insert,      "XK_Insert",            "insert" },
888     { KSYM_Tab,         "XK_Tab",               "tab" },
889     { KSYM_Home,        "XK_Home",              "home" },
890     { KSYM_End,         "XK_End",               "end" },
891     { KSYM_Page_Up,     "XK_Page_Up",           "page up" },
892     { KSYM_Page_Down,   "XK_Page_Down",         "page down" },
893     { KSYM_Menu,        "XK_Menu",              "menu" },        /* Win-Menu */
894
895     /* ASCII 0x20 to 0x40 keys (except numbers) */
896     { KSYM_space,       "XK_space",             "space" },
897     { KSYM_exclam,      "XK_exclam",            "!" },
898     { KSYM_quotedbl,    "XK_quotedbl",          "\"" },
899     { KSYM_numbersign,  "XK_numbersign",        "#" },
900     { KSYM_dollar,      "XK_dollar",            "$" },
901     { KSYM_percent,     "XK_percent",           "%" },
902     { KSYM_ampersand,   "XK_ampersand",         "&" },
903     { KSYM_apostrophe,  "XK_apostrophe",        "'" },
904     { KSYM_parenleft,   "XK_parenleft",         "(" },
905     { KSYM_parenright,  "XK_parenright",        ")" },
906     { KSYM_asterisk,    "XK_asterisk",          "*" },
907     { KSYM_plus,        "XK_plus",              "+" },
908     { KSYM_comma,       "XK_comma",             "," },
909     { KSYM_minus,       "XK_minus",             "-" },
910     { KSYM_period,      "XK_period",            "." },
911     { KSYM_slash,       "XK_slash",             "/" },
912     { KSYM_colon,       "XK_colon",             ":" },
913     { KSYM_semicolon,   "XK_semicolon",         ";" },
914     { KSYM_less,        "XK_less",              "<" },
915     { KSYM_equal,       "XK_equal",             "=" },
916     { KSYM_greater,     "XK_greater",           ">" },
917     { KSYM_question,    "XK_question",          "?" },
918     { KSYM_at,          "XK_at",                "@" },
919
920     /* more ASCII keys */
921     { KSYM_bracketleft, "XK_bracketleft",       "[" },
922     { KSYM_backslash,   "XK_backslash",         "backslash" },
923     { KSYM_bracketright,"XK_bracketright",      "]" },
924     { KSYM_asciicircum, "XK_asciicircum",       "circumflex" },
925     { KSYM_underscore,  "XK_underscore",        "_" },
926     { KSYM_grave,       "XK_grave",             "grave" },
927     { KSYM_quoteleft,   "XK_quoteleft",         "quote left" },
928     { KSYM_braceleft,   "XK_braceleft",         "brace left" },
929     { KSYM_bar,         "XK_bar",               "bar" },
930     { KSYM_braceright,  "XK_braceright",        "brace right" },
931     { KSYM_asciitilde,  "XK_asciitilde",        "ascii tilde" },
932
933     /* special (non-ASCII) keys */
934     { KSYM_Adiaeresis,  "XK_Adiaeresis",        "Ä" },
935     { KSYM_Odiaeresis,  "XK_Odiaeresis",        "Ö" },
936     { KSYM_Udiaeresis,  "XK_Udiaeresis",        "Ãœ" },
937     { KSYM_adiaeresis,  "XK_adiaeresis",        "ä" },
938     { KSYM_odiaeresis,  "XK_odiaeresis",        "ö" },
939     { KSYM_udiaeresis,  "XK_udiaeresis",        "ü" },
940     { KSYM_ssharp,      "XK_ssharp",            "sharp s" },
941
942     /* end-of-array identifier */
943     { 0,                NULL,                   NULL }
944   };
945
946   int i;
947
948   if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
949   {
950     static char name_buffer[30];
951     Key key = *keysym;
952
953     if (key >= KSYM_A && key <= KSYM_Z)
954       sprintf(name_buffer, "%c", 'A' + (char)(key - KSYM_A));
955     else if (key >= KSYM_a && key <= KSYM_z)
956       sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
957     else if (key >= KSYM_0 && key <= KSYM_9)
958       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
959     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
960       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
961     else if (key >= KSYM_F1 && key <= KSYM_F24)
962       sprintf(name_buffer, "function F%d", (int)(key - KSYM_F1 + 1));
963     else if (key == KSYM_UNDEFINED)
964       strcpy(name_buffer, "(undefined)");
965     else
966     {
967       i = 0;
968
969       do
970       {
971         if (key == translate_key[i].key)
972         {
973           strcpy(name_buffer, translate_key[i].name);
974           break;
975         }
976       }
977       while (translate_key[++i].name);
978
979       if (!translate_key[i].name)
980         strcpy(name_buffer, "(unknown)");
981     }
982
983     *name = name_buffer;
984   }
985   else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
986   {
987     static char name_buffer[30];
988     Key key = *keysym;
989
990     if (key >= KSYM_A && key <= KSYM_Z)
991       sprintf(name_buffer, "XK_%c", 'A' + (char)(key - KSYM_A));
992     else if (key >= KSYM_a && key <= KSYM_z)
993       sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
994     else if (key >= KSYM_0 && key <= KSYM_9)
995       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
996     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
997       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
998     else if (key >= KSYM_F1 && key <= KSYM_F24)
999       sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_F1 + 1));
1000     else if (key == KSYM_UNDEFINED)
1001       strcpy(name_buffer, "[undefined]");
1002     else
1003     {
1004       i = 0;
1005
1006       do
1007       {
1008         if (key == translate_key[i].key)
1009         {
1010           strcpy(name_buffer, translate_key[i].x11name);
1011           break;
1012         }
1013       }
1014       while (translate_key[++i].x11name);
1015
1016       if (!translate_key[i].x11name)
1017         sprintf(name_buffer, "0x%04lx", (unsigned long)key);
1018     }
1019
1020     *x11name = name_buffer;
1021   }
1022   else if (mode == TRANSLATE_KEYNAME_TO_KEYSYM)
1023   {
1024     Key key = KSYM_UNDEFINED;
1025
1026     i = 0;
1027     do
1028     {
1029       if (strcmp(translate_key[i].name, *name) == 0)
1030       {
1031         key = translate_key[i].key;
1032         break;
1033       }
1034     }
1035     while (translate_key[++i].x11name);
1036
1037     if (key == KSYM_UNDEFINED)
1038       Error(ERR_WARN, "getKeyFromKeyName(): not completely implemented");
1039
1040     *keysym = key;
1041   }
1042   else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
1043   {
1044     Key key = KSYM_UNDEFINED;
1045     char *name_ptr = *x11name;
1046
1047     if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
1048     {
1049       char c = name_ptr[3];
1050
1051       if (c >= 'A' && c <= 'Z')
1052         key = KSYM_A + (Key)(c - 'A');
1053       else if (c >= 'a' && c <= 'z')
1054         key = KSYM_a + (Key)(c - 'a');
1055       else if (c >= '0' && c <= '9')
1056         key = KSYM_0 + (Key)(c - '0');
1057     }
1058     else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
1059     {
1060       char c = name_ptr[6];
1061
1062       if (c >= '0' && c <= '9')
1063         key = KSYM_0 + (Key)(c - '0');
1064     }
1065     else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
1066     {
1067       char c1 = name_ptr[4];
1068       char c2 = name_ptr[5];
1069       int d = 0;
1070
1071       if ((c1 >= '0' && c1 <= '9') &&
1072           ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
1073         d = atoi(&name_ptr[4]);
1074
1075       if (d >=1 && d <= 24)
1076         key = KSYM_F1 + (Key)(d - 1);
1077     }
1078     else if (strncmp(name_ptr, "XK_", 3) == 0)
1079     {
1080       i = 0;
1081
1082       do
1083       {
1084         if (strcmp(name_ptr, translate_key[i].x11name) == 0)
1085         {
1086           key = translate_key[i].key;
1087           break;
1088         }
1089       }
1090       while (translate_key[++i].x11name);
1091     }
1092     else if (strncmp(name_ptr, "0x", 2) == 0)
1093     {
1094       unsigned long value = 0;
1095
1096       name_ptr += 2;
1097
1098       while (name_ptr)
1099       {
1100         char c = *name_ptr++;
1101         int d = -1;
1102
1103         if (c >= '0' && c <= '9')
1104           d = (int)(c - '0');
1105         else if (c >= 'a' && c <= 'f')
1106           d = (int)(c - 'a' + 10);
1107         else if (c >= 'A' && c <= 'F')
1108           d = (int)(c - 'A' + 10);
1109
1110         if (d == -1)
1111         {
1112           value = -1;
1113           break;
1114         }
1115
1116         value = value * 16 + d;
1117       }
1118
1119       if (value != -1)
1120         key = (Key)value;
1121     }
1122
1123     *keysym = key;
1124   }
1125 }
1126
1127 char *getKeyNameFromKey(Key key)
1128 {
1129   char *name;
1130
1131   translate_keyname(&key, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
1132   return name;
1133 }
1134
1135 char *getX11KeyNameFromKey(Key key)
1136 {
1137   char *x11name;
1138
1139   translate_keyname(&key, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
1140   return x11name;
1141 }
1142
1143 Key getKeyFromKeyName(char *name)
1144 {
1145   Key key;
1146
1147   translate_keyname(&key, NULL, &name, TRANSLATE_KEYNAME_TO_KEYSYM);
1148   return key;
1149 }
1150
1151 Key getKeyFromX11KeyName(char *x11name)
1152 {
1153   Key key;
1154
1155   translate_keyname(&key, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
1156   return key;
1157 }
1158
1159 char getCharFromKey(Key key)
1160 {
1161   char *keyname = getKeyNameFromKey(key);
1162   char letter = 0;
1163
1164   if (strlen(keyname) == 1)
1165     letter = keyname[0];
1166   else if (strcmp(keyname, "space") == 0)
1167     letter = ' ';
1168   else if (strcmp(keyname, "circumflex") == 0)
1169     letter = '^';
1170
1171   return letter;
1172 }
1173
1174
1175 /* ========================================================================= */
1176 /* functions for checking filenames                                          */
1177 /* ========================================================================= */
1178
1179 boolean FileIsGraphic(char *filename)
1180 {
1181   if (strlen(filename) > 4 &&
1182       strcmp(&filename[strlen(filename) - 4], ".pcx") == 0)
1183     return TRUE;
1184
1185   return FALSE;
1186 }
1187
1188 boolean FileIsSound(char *basename)
1189 {
1190   if (strlen(basename) > 4 &&
1191       strcmp(&basename[strlen(basename) - 4], ".wav") == 0)
1192     return TRUE;
1193
1194   return FALSE;
1195 }
1196
1197 boolean FileIsMusic(char *basename)
1198 {
1199   if (strlen(basename) > 4 &&
1200       (strcmp(&basename[strlen(basename) - 4], ".mod") == 0 ||
1201        strcmp(&basename[strlen(basename) - 4], ".MOD") == 0 ||
1202        strncmp(basename, "mod.", 4) == 0 ||
1203        strncmp(basename, "MOD.", 4) == 0))
1204     return TRUE;
1205
1206   return FALSE;
1207 }
1208
1209 /* ========================================================================= */
1210 /* functions only needed for non-Unix (non-command-line) systems */
1211 /* ========================================================================= */
1212
1213 #if !defined(PLATFORM_UNIX)
1214
1215 #define ERROR_FILENAME          "error.out"
1216
1217 void initErrorFile()
1218 {
1219   char *filename;
1220
1221   InitUserDataDirectory();
1222
1223   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1224   unlink(filename);
1225   free(filename);
1226 }
1227
1228 FILE *openErrorFile()
1229 {
1230   char *filename;
1231   FILE *error_file;
1232
1233   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1234   error_file = fopen(filename, MODE_APPEND);
1235   free(filename);
1236
1237   return error_file;
1238 }
1239
1240 void dumpErrorFile()
1241 {
1242   char *filename;
1243   FILE *error_file;
1244
1245   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1246   error_file = fopen(filename, MODE_READ);
1247   free(filename);
1248
1249   if (error_file != NULL)
1250   {
1251     while (!feof(error_file))
1252       fputc(fgetc(error_file), stderr);
1253
1254     fclose(error_file);
1255   }
1256 }
1257 #endif
1258
1259
1260 /* ========================================================================= */
1261 /* the following is only for debugging purpose and normally not used         */
1262 /* ========================================================================= */
1263
1264 #define DEBUG_NUM_TIMESTAMPS    3
1265
1266 void debug_print_timestamp(int counter_nr, char *message)
1267 {
1268   static long counter[DEBUG_NUM_TIMESTAMPS][2];
1269
1270   if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
1271     Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
1272
1273   counter[counter_nr][0] = Counter();
1274
1275   if (message)
1276     printf("%s %.2f seconds\n", message,
1277            (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
1278
1279   counter[counter_nr][1] = Counter();
1280 }