190901ced6e6166f8d8851ddb161ddfd98b05273
[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 <sys/stat.h>
18 #include <stdarg.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include "platform.h"
24
25 #if !defined(PLATFORM_WIN32)
26 #include <pwd.h>
27 #include <sys/param.h>
28 #endif
29
30 #include "misc.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/Windows
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   return(TRUE);
165 }
166
167 boolean DelayReached(unsigned long *counter_var,
168                      unsigned long delay)
169 {
170   unsigned long actual_counter = Counter();
171
172   if (actual_counter < *counter_var + delay &&
173       actual_counter >= *counter_var)
174     return(FALSE);
175
176   *counter_var = actual_counter;
177   return(TRUE);
178 }
179
180 void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
181 {
182   unsigned long actual_counter;
183
184   while(1)
185   {
186     actual_counter = Counter();
187
188     if (actual_counter < *counter_var + delay &&
189         actual_counter >= *counter_var)
190       sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
191     else
192       break;
193   }
194
195   *counter_var = actual_counter;
196 }
197
198 /* int2str() returns a number converted to a string;
199    the used memory is static, but will be overwritten by later calls,
200    so if you want to save the result, copy it to a private string buffer;
201    there can be 10 local calls of int2str() without buffering the result --
202    the 11th call will then destroy the result from the first call and so on.
203 */
204
205 char *int2str(int number, int size)
206 {
207   static char shift_array[10][40];
208   static int shift_counter = 0;
209   char *s = shift_array[shift_counter];
210
211   shift_counter = (shift_counter + 1) % 10;
212
213   if (size > 20)
214     size = 20;
215
216   if (size)
217   {
218     sprintf(s, "                    %09d", number);
219     return &s[strlen(s) - size];
220   }
221   else
222   {
223     sprintf(s, "%d", number);
224     return s;
225   }
226 }
227
228 unsigned int SimpleRND(unsigned int max)
229 {
230 #if defined(TARGET_SDL)
231   static unsigned long root = 654321;
232   unsigned long current_ms;
233
234   current_ms = SDL_GetTicks();
235   root = root * 4253261 + current_ms;
236   return (root % max);
237 #else
238   static unsigned long root = 654321;
239   struct timeval current_time;
240
241   gettimeofday(&current_time, NULL);
242   root = root * 4253261 + current_time.tv_sec + current_time.tv_usec;
243   return (root % max);
244 #endif
245 }
246
247 #ifdef DEBUG
248 static unsigned int last_RND_value = 0;
249
250 unsigned int last_RND()
251 {
252   return last_RND_value;
253 }
254 #endif
255
256 unsigned int RND(unsigned int max)
257 {
258 #ifdef DEBUG
259   return (last_RND_value = random_linux_libc() % max);
260 #else
261   return (random_linux_libc() % max);
262 #endif
263 }
264
265 unsigned int InitRND(long seed)
266 {
267 #if defined(TARGET_SDL)
268   unsigned long current_ms;
269
270   if (seed == NEW_RANDOMIZE)
271   {
272     current_ms = SDL_GetTicks();
273     srandom_linux_libc((unsigned int) current_ms);
274     return (unsigned int) current_ms;
275   }
276   else
277   {
278     srandom_linux_libc((unsigned int) seed);
279     return (unsigned int) seed;
280   }
281 #else
282   struct timeval current_time;
283
284   if (seed == NEW_RANDOMIZE)
285   {
286     gettimeofday(&current_time, NULL);
287     srandom_linux_libc((unsigned int) current_time.tv_usec);
288     return (unsigned int) current_time.tv_usec;
289   }
290   else
291   {
292     srandom_linux_libc((unsigned int) seed);
293     return (unsigned int) seed;
294   }
295 #endif
296 }
297
298 char *getLoginName()
299 {
300 #if defined(PLATFORM_WIN32)
301   return ANONYMOUS_NAME;
302 #else
303   struct passwd *pwd;
304
305   if ((pwd = getpwuid(getuid())) == NULL)
306     return ANONYMOUS_NAME;
307   else
308     return pwd->pw_name;
309 #endif
310 }
311
312 char *getRealName()
313 {
314 #if defined(PLATFORM_UNIX)
315   struct passwd *pwd;
316
317   if ((pwd = getpwuid(getuid())) == NULL || strlen(pwd->pw_gecos) == 0)
318     return ANONYMOUS_NAME;
319   else
320   {
321     static char real_name[1024];
322     char *from_ptr = pwd->pw_gecos, *to_ptr = real_name;
323
324     if (strchr(pwd->pw_gecos, 'ß') == NULL)
325       return pwd->pw_gecos;
326
327     /* the user's real name contains a 'ß' character (german sharp s),
328        which has no equivalent in upper case letters (which our fonts use) */
329     while (*from_ptr != '\0' && (long)(to_ptr - real_name) < 1024 - 2)
330     {
331       if (*from_ptr != 'ß')
332         *to_ptr++ = *from_ptr++;
333       else
334       {
335         from_ptr++;
336         *to_ptr++ = 's';
337         *to_ptr++ = 's';
338       }
339     }
340     *to_ptr = '\0';
341
342     return real_name;
343   }
344 #else /* !PLATFORM_UNIX */
345   return ANONYMOUS_NAME;
346 #endif
347 }
348
349 char *getHomeDir()
350 {
351 #if defined(PLATFORM_UNIX)
352   static char *home_dir = NULL;
353
354   if (!home_dir)
355   {
356     if (!(home_dir = getenv("HOME")))
357     {
358       struct passwd *pwd;
359
360       if ((pwd = getpwuid(getuid())))
361         home_dir = pwd->pw_dir;
362       else
363         home_dir = ".";
364     }
365   }
366
367   return home_dir;
368 #else
369   return ".";
370 #endif
371 }
372
373 char *getPath2(char *path1, char *path2)
374 {
375   char *complete_path = checked_malloc(strlen(path1) + 1 +
376                                        strlen(path2) + 1);
377
378   sprintf(complete_path, "%s/%s", path1, path2);
379   return complete_path;
380 }
381
382 char *getPath3(char *path1, char *path2, char *path3)
383 {
384   char *complete_path = checked_malloc(strlen(path1) + 1 +
385                                        strlen(path2) + 1 +
386                                        strlen(path3) + 1);
387
388   sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
389   return complete_path;
390 }
391
392 char *getStringCopy(char *s)
393 {
394   char *s_copy;
395
396   if (s == NULL)
397     return NULL;
398
399   s_copy = checked_malloc(strlen(s) + 1);
400
401   strcpy(s_copy, s);
402   return s_copy;
403 }
404
405 char *getStringToLower(char *s)
406 {
407   char *s_copy = checked_malloc(strlen(s) + 1);
408   char *s_ptr = s_copy;
409
410   while (*s)
411     *s_ptr++ = tolower(*s++);
412   *s_ptr = '\0';
413
414   return s_copy;
415 }
416
417 void GetOptions(char *argv[])
418 {
419   char **options_left = &argv[1];
420
421   /* initialize global program options */
422   options.display_name = NULL;
423   options.server_host = NULL;
424   options.server_port = 0;
425   options.ro_base_directory = RO_BASE_PATH;
426   options.rw_base_directory = RW_BASE_PATH;
427   options.level_directory = RO_BASE_PATH "/" LEVELS_DIRECTORY;
428   options.serveronly = FALSE;
429   options.network = FALSE;
430   options.verbose = FALSE;
431   options.debug = FALSE;
432
433   while (*options_left)
434   {
435     char option_str[MAX_OPTION_LEN];
436     char *option = options_left[0];
437     char *next_option = options_left[1];
438     char *option_arg = NULL;
439     int option_len = strlen(option);
440
441     if (option_len >= MAX_OPTION_LEN)
442       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
443
444     strcpy(option_str, option);                 /* copy argument into buffer */
445     option = option_str;
446
447     if (strcmp(option, "--") == 0)              /* stop scanning arguments */
448       break;
449
450     if (strncmp(option, "--", 2) == 0)          /* treat '--' like '-' */
451       option++;
452
453     option_arg = strchr(option, '=');
454     if (option_arg == NULL)                     /* no '=' in option */
455       option_arg = next_option;
456     else
457     {
458       *option_arg++ = '\0';                     /* cut argument from option */
459       if (*option_arg == '\0')                  /* no argument after '=' */
460         Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
461     }
462
463     option_len = strlen(option);
464
465     if (strcmp(option, "-") == 0)
466       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
467     else if (strncmp(option, "-help", option_len) == 0)
468     {
469       printf("Usage: %s [options] [server.name [port]]\n"
470              "Options:\n"
471              "  -d, --display machine:0       X server display\n"
472              "  -b, --basepath directory      alternative base directory\n"
473              "  -l, --level directory         alternative level directory\n"
474              "  -s, --serveronly              only start network server\n"
475              "  -n, --network                 network multiplayer game\n"
476              "  -v, --verbose                 verbose mode\n"
477              "      --debug                   display debugging information\n",
478              program.command_basename);
479       exit(0);
480     }
481     else if (strncmp(option, "-display", option_len) == 0)
482     {
483       if (option_arg == NULL)
484         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
485
486       options.display_name = option_arg;
487       if (option_arg == next_option)
488         options_left++;
489     }
490     else if (strncmp(option, "-basepath", option_len) == 0)
491     {
492       if (option_arg == NULL)
493         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
494
495       /* this should be extended to separate options for ro and rw data */
496       options.ro_base_directory = option_arg;
497       options.rw_base_directory = option_arg;
498       if (option_arg == next_option)
499         options_left++;
500
501       /* adjust path for level directory accordingly */
502       options.level_directory =
503         getPath2(options.ro_base_directory, LEVELS_DIRECTORY);
504     }
505     else if (strncmp(option, "-levels", option_len) == 0)
506     {
507       if (option_arg == NULL)
508         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
509
510       options.level_directory = option_arg;
511       if (option_arg == next_option)
512         options_left++;
513     }
514     else if (strncmp(option, "-network", option_len) == 0)
515     {
516       options.network = TRUE;
517     }
518     else if (strncmp(option, "-serveronly", option_len) == 0)
519     {
520       options.serveronly = TRUE;
521     }
522     else if (strncmp(option, "-verbose", option_len) == 0)
523     {
524       options.verbose = TRUE;
525     }
526     else if (strncmp(option, "-debug", option_len) == 0)
527     {
528       options.debug = TRUE;
529     }
530     else if (*option == '-')
531     {
532       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
533     }
534     else if (options.server_host == NULL)
535     {
536       options.server_host = *options_left;
537     }
538     else if (options.server_port == 0)
539     {
540       options.server_port = atoi(*options_left);
541       if (options.server_port < 1024)
542         Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
543     }
544     else
545       Error(ERR_EXIT_HELP, "too many arguments");
546
547     options_left++;
548   }
549 }
550
551 void Error(int mode, char *format, ...)
552 {
553   char *process_name = "";
554   FILE *error = stderr;
555   char *newline = "\n";
556
557   /* display warnings only when running in verbose mode */
558   if (mode & ERR_WARN && !options.verbose)
559     return;
560
561 #if !defined(PLATFORM_UNIX)
562   newline = "\r\n";
563
564   if ((error = openErrorFile()) == NULL)
565   {
566     printf("Cannot write to error output file!%s", newline);
567     program.exit_function(1);
568   }
569 #endif
570
571   if (mode & ERR_SOUND_SERVER)
572     process_name = " sound server";
573   else if (mode & ERR_NETWORK_SERVER)
574     process_name = " network server";
575   else if (mode & ERR_NETWORK_CLIENT)
576     process_name = " network client **";
577
578   if (format)
579   {
580     va_list ap;
581
582     fprintf(error, "%s%s: ", program.command_basename, process_name);
583
584     if (mode & ERR_WARN)
585       fprintf(error, "warning: ");
586
587     va_start(ap, format);
588     vfprintf(error, format, ap);
589     va_end(ap);
590   
591     fprintf(error, "%s", newline);
592   }
593   
594   if (mode & ERR_HELP)
595     fprintf(error, "%s: Try option '--help' for more information.%s",
596             program.command_basename, newline);
597
598   if (mode & ERR_EXIT)
599     fprintf(error, "%s%s: aborting%s",
600             program.command_basename, process_name, newline);
601
602   if (error != stderr)
603     fclose(error);
604
605   if (mode & ERR_EXIT)
606   {
607     if (mode & ERR_FROM_SERVER)
608       exit(1);                          /* child process: normal exit */
609     else
610       program.exit_function(1);         /* main process: clean up stuff */
611   }
612 }
613
614 void *checked_malloc(unsigned long size)
615 {
616   void *ptr;
617
618   ptr = malloc(size);
619
620   if (ptr == NULL)
621     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
622
623   return ptr;
624 }
625
626 void *checked_calloc(unsigned long size)
627 {
628   void *ptr;
629
630   ptr = calloc(1, size);
631
632   if (ptr == NULL)
633     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
634
635   return ptr;
636 }
637
638 void *checked_realloc(void *ptr, unsigned long size)
639 {
640   ptr = realloc(ptr, size);
641
642   if (ptr == NULL)
643     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
644
645   return ptr;
646 }
647
648 short getFile16BitInteger(FILE *file, int byte_order)
649 {
650   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
651     return ((fgetc(file) <<  8) |
652             (fgetc(file) <<  0));
653   else           /* BYTE_ORDER_LITTLE_ENDIAN */
654     return ((fgetc(file) <<  0) |
655             (fgetc(file) <<  8));
656 }
657
658 void putFile16BitInteger(FILE *file, short value, int byte_order)
659 {
660   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
661   {
662     fputc((value >>  8) & 0xff, file);
663     fputc((value >>  0) & 0xff, file);
664   }
665   else           /* BYTE_ORDER_LITTLE_ENDIAN */
666   {
667     fputc((value >>  0) & 0xff, file);
668     fputc((value >>  8) & 0xff, file);
669   }
670 }
671
672 int getFile32BitInteger(FILE *file, int byte_order)
673 {
674   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
675     return ((fgetc(file) << 24) |
676             (fgetc(file) << 16) |
677             (fgetc(file) <<  8) |
678             (fgetc(file) <<  0));
679   else           /* BYTE_ORDER_LITTLE_ENDIAN */
680     return ((fgetc(file) <<  0) |
681             (fgetc(file) <<  8) |
682             (fgetc(file) << 16) |
683             (fgetc(file) << 24));
684 }
685
686 void putFile32BitInteger(FILE *file, int value, int byte_order)
687 {
688   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
689   {
690     fputc((value >> 24) & 0xff, file);
691     fputc((value >> 16) & 0xff, file);
692     fputc((value >>  8) & 0xff, file);
693     fputc((value >>  0) & 0xff, file);
694   }
695   else           /* BYTE_ORDER_LITTLE_ENDIAN */
696   {
697     fputc((value >>  0) & 0xff, file);
698     fputc((value >>  8) & 0xff, file);
699     fputc((value >> 16) & 0xff, file);
700     fputc((value >> 24) & 0xff, file);
701   }
702 }
703
704 boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
705                      int byte_order)
706 {
707   const int chunk_name_length = 4;
708
709   /* read chunk name */
710   fgets(chunk_name, chunk_name_length + 1, file);
711
712   if (chunk_size != NULL)
713   {
714     /* read chunk size */
715     *chunk_size = getFile32BitInteger(file, byte_order);
716   }
717
718   return (feof(file) || ferror(file) ? FALSE : TRUE);
719 }
720
721 void putFileChunk(FILE *file, char *chunk_name, int chunk_size,
722                   int byte_order)
723 {
724   /* write chunk name */
725   fputs(chunk_name, file);
726
727   if (chunk_size >= 0)
728   {
729     /* write chunk size */
730     putFile32BitInteger(file, chunk_size, byte_order);
731   }
732 }
733
734 #define TRANSLATE_KEYSYM_TO_KEYNAME     0
735 #define TRANSLATE_KEYSYM_TO_X11KEYNAME  1
736 #define TRANSLATE_X11KEYNAME_TO_KEYSYM  2
737
738 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
739 {
740   static struct
741   {
742     Key key;
743     char *x11name;
744     char *name;
745   } translate_key[] =
746   {
747     /* normal cursor keys */
748     { KSYM_Left,        "XK_Left",              "cursor left" },
749     { KSYM_Right,       "XK_Right",             "cursor right" },
750     { KSYM_Up,          "XK_Up",                "cursor up" },
751     { KSYM_Down,        "XK_Down",              "cursor down" },
752
753     /* keypad cursor keys */
754 #ifdef KSYM_KP_Left
755     { KSYM_KP_Left,     "XK_KP_Left",           "keypad left" },
756     { KSYM_KP_Right,    "XK_KP_Right",          "keypad right" },
757     { KSYM_KP_Up,       "XK_KP_Up",             "keypad up" },
758     { KSYM_KP_Down,     "XK_KP_Down",           "keypad down" },
759 #endif
760
761     /* other keypad keys */
762 #ifdef KSYM_KP_Enter
763     { KSYM_KP_Enter,    "XK_KP_Enter",          "keypad enter" },
764     { KSYM_KP_Add,      "XK_KP_Add",            "keypad +" },
765     { KSYM_KP_Subtract, "XK_KP_Subtract",       "keypad -" },
766     { KSYM_KP_Multiply, "XK_KP_Multiply",       "keypad mltply" },
767     { KSYM_KP_Divide,   "XK_KP_Divide",         "keypad /" },
768     { KSYM_KP_Separator,"XK_KP_Separator",      "keypad ," },
769 #endif
770
771     /* modifier keys */
772     { KSYM_Shift_L,     "XK_Shift_L",           "left shift" },
773     { KSYM_Shift_R,     "XK_Shift_R",           "right shift" },
774     { KSYM_Control_L,   "XK_Control_L",         "left control" },
775     { KSYM_Control_R,   "XK_Control_R",         "right control" },
776     { KSYM_Meta_L,      "XK_Meta_L",            "left meta" },
777     { KSYM_Meta_R,      "XK_Meta_R",            "right meta" },
778     { KSYM_Alt_L,       "XK_Alt_L",             "left alt" },
779     { KSYM_Alt_R,       "XK_Alt_R",             "right alt" },
780     { KSYM_Super_L,     "XK_Super_L",           "left super" },  /* Win-L */
781     { KSYM_Super_R,     "XK_Super_R",           "right super" }, /* Win-R */
782     { KSYM_Mode_switch, "XK_Mode_switch",       "mode switch" }, /* Alt-R */
783     { KSYM_Multi_key,   "XK_Multi_key",         "multi key" },   /* Ctrl-R */
784
785     /* some special keys */
786     { KSYM_BackSpace,   "XK_BackSpace",         "backspace" },
787     { KSYM_Delete,      "XK_Delete",            "delete" },
788     { KSYM_Insert,      "XK_Insert",            "insert" },
789     { KSYM_Tab,         "XK_Tab",               "tab" },
790     { KSYM_Home,        "XK_Home",              "home" },
791     { KSYM_End,         "XK_End",               "end" },
792     { KSYM_Page_Up,     "XK_Page_Up",           "page up" },
793     { KSYM_Page_Down,   "XK_Page_Down",         "page down" },
794     { KSYM_Menu,        "XK_Menu",              "menu" },        /* Win-Menu */
795
796     /* ASCII 0x20 to 0x40 keys (except numbers) */
797     { KSYM_space,       "XK_space",             "space" },
798     { KSYM_exclam,      "XK_exclam",            "!" },
799     { KSYM_quotedbl,    "XK_quotedbl",          "\"" },
800     { KSYM_numbersign,  "XK_numbersign",        "#" },
801     { KSYM_dollar,      "XK_dollar",            "$" },
802     { KSYM_percent,     "XK_percent",           "%" },
803     { KSYM_ampersand,   "XK_ampersand",         "&" },
804     { KSYM_apostrophe,  "XK_apostrophe",        "'" },
805     { KSYM_parenleft,   "XK_parenleft",         "(" },
806     { KSYM_parenright,  "XK_parenright",        ")" },
807     { KSYM_asterisk,    "XK_asterisk",          "*" },
808     { KSYM_plus,        "XK_plus",              "+" },
809     { KSYM_comma,       "XK_comma",             "," },
810     { KSYM_minus,       "XK_minus",             "-" },
811     { KSYM_period,      "XK_period",            "." },
812     { KSYM_slash,       "XK_slash",             "/" },
813     { KSYM_colon,       "XK_colon",             ":" },
814     { KSYM_semicolon,   "XK_semicolon",         ";" },
815     { KSYM_less,        "XK_less",              "<" },
816     { KSYM_equal,       "XK_equal",             "=" },
817     { KSYM_greater,     "XK_greater",           ">" },
818     { KSYM_question,    "XK_question",          "?" },
819     { KSYM_at,          "XK_at",                "@" },
820
821     /* more ASCII keys */
822     { KSYM_bracketleft, "XK_bracketleft",       "[" },
823     { KSYM_backslash,   "XK_backslash",         "backslash" },
824     { KSYM_bracketright,"XK_bracketright",      "]" },
825     { KSYM_asciicircum, "XK_asciicircum",       "circumflex" },
826     { KSYM_underscore,  "XK_underscore",        "_" },
827     { KSYM_grave,       "XK_grave",             "grave" },
828     { KSYM_quoteleft,   "XK_quoteleft",         "quote left" },
829     { KSYM_braceleft,   "XK_braceleft",         "brace left" },
830     { KSYM_bar,         "XK_bar",               "bar" },
831     { KSYM_braceright,  "XK_braceright",        "brace right" },
832     { KSYM_asciitilde,  "XK_asciitilde",        "ascii tilde" },
833
834     /* special (non-ASCII) keys */
835     { KSYM_Adiaeresis,  "XK_Adiaeresis",        "Ä" },
836     { KSYM_Odiaeresis,  "XK_Odiaeresis",        "Ö" },
837     { KSYM_Udiaeresis,  "XK_Udiaeresis",        "Ãœ" },
838     { KSYM_adiaeresis,  "XK_adiaeresis",        "ä" },
839     { KSYM_odiaeresis,  "XK_odiaeresis",        "ö" },
840     { KSYM_udiaeresis,  "XK_udiaeresis",        "ü" },
841     { KSYM_ssharp,      "XK_ssharp",            "sharp s" },
842
843     /* end-of-array identifier */
844     { 0,                NULL,                   NULL }
845   };
846
847   int i;
848
849   if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
850   {
851     static char name_buffer[30];
852     Key key = *keysym;
853
854     if (key >= KSYM_A && key <= KSYM_Z)
855       sprintf(name_buffer, "%c", 'A' + (char)(key - KSYM_A));
856     else if (key >= KSYM_a && key <= KSYM_z)
857       sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
858     else if (key >= KSYM_0 && key <= KSYM_9)
859       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
860     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
861       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
862     else if (key >= KSYM_F1 && key <= KSYM_F24)
863       sprintf(name_buffer, "function F%d", (int)(key - KSYM_F1 + 1));
864     else if (key == KSYM_UNDEFINED)
865       strcpy(name_buffer, "(undefined)");
866     else
867     {
868       i = 0;
869
870       do
871       {
872         if (key == translate_key[i].key)
873         {
874           strcpy(name_buffer, translate_key[i].name);
875           break;
876         }
877       }
878       while (translate_key[++i].name);
879
880       if (!translate_key[i].name)
881         strcpy(name_buffer, "(unknown)");
882     }
883
884     *name = name_buffer;
885   }
886   else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
887   {
888     static char name_buffer[30];
889     Key key = *keysym;
890
891     if (key >= KSYM_A && key <= KSYM_Z)
892       sprintf(name_buffer, "XK_%c", 'A' + (char)(key - KSYM_A));
893     else if (key >= KSYM_a && key <= KSYM_z)
894       sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
895     else if (key >= KSYM_0 && key <= KSYM_9)
896       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
897     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
898       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
899     else if (key >= KSYM_F1 && key <= KSYM_F24)
900       sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_F1 + 1));
901     else if (key == KSYM_UNDEFINED)
902       strcpy(name_buffer, "[undefined]");
903     else
904     {
905       i = 0;
906
907       do
908       {
909         if (key == translate_key[i].key)
910         {
911           strcpy(name_buffer, translate_key[i].x11name);
912           break;
913         }
914       }
915       while (translate_key[++i].x11name);
916
917       if (!translate_key[i].x11name)
918         sprintf(name_buffer, "0x%04lx", (unsigned long)key);
919     }
920
921     *x11name = name_buffer;
922   }
923   else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
924   {
925     Key key = KSYM_UNDEFINED;
926     char *name_ptr = *x11name;
927
928     if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
929     {
930       char c = name_ptr[3];
931
932       if (c >= 'A' && c <= 'Z')
933         key = KSYM_A + (Key)(c - 'A');
934       else if (c >= 'a' && c <= 'z')
935         key = KSYM_a + (Key)(c - 'a');
936       else if (c >= '0' && c <= '9')
937         key = KSYM_0 + (Key)(c - '0');
938     }
939     else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
940     {
941       char c = name_ptr[6];
942
943       if (c >= '0' && c <= '9')
944         key = KSYM_0 + (Key)(c - '0');
945     }
946     else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
947     {
948       char c1 = name_ptr[4];
949       char c2 = name_ptr[5];
950       int d = 0;
951
952       if ((c1 >= '0' && c1 <= '9') &&
953           ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
954         d = atoi(&name_ptr[4]);
955
956       if (d >=1 && d <= 24)
957         key = KSYM_F1 + (Key)(d - 1);
958     }
959     else if (strncmp(name_ptr, "XK_", 3) == 0)
960     {
961       i = 0;
962
963       do
964       {
965         if (strcmp(name_ptr, translate_key[i].x11name) == 0)
966         {
967           key = translate_key[i].key;
968           break;
969         }
970       }
971       while (translate_key[++i].x11name);
972     }
973     else if (strncmp(name_ptr, "0x", 2) == 0)
974     {
975       unsigned long value = 0;
976
977       name_ptr += 2;
978
979       while (name_ptr)
980       {
981         char c = *name_ptr++;
982         int d = -1;
983
984         if (c >= '0' && c <= '9')
985           d = (int)(c - '0');
986         else if (c >= 'a' && c <= 'f')
987           d = (int)(c - 'a' + 10);
988         else if (c >= 'A' && c <= 'F')
989           d = (int)(c - 'A' + 10);
990
991         if (d == -1)
992         {
993           value = -1;
994           break;
995         }
996
997         value = value * 16 + d;
998       }
999
1000       if (value != -1)
1001         key = (Key)value;
1002     }
1003
1004     *keysym = key;
1005   }
1006 }
1007
1008 char *getKeyNameFromKey(Key key)
1009 {
1010   char *name;
1011
1012   translate_keyname(&key, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
1013   return name;
1014 }
1015
1016 char *getX11KeyNameFromKey(Key key)
1017 {
1018   char *x11name;
1019
1020   translate_keyname(&key, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
1021   return x11name;
1022 }
1023
1024 Key getKeyFromX11KeyName(char *x11name)
1025 {
1026   Key key;
1027
1028   translate_keyname(&key, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
1029   return key;
1030 }
1031
1032 char getCharFromKey(Key key)
1033 {
1034   char *keyname = getKeyNameFromKey(key);
1035   char letter = 0;
1036
1037   if (strlen(keyname) == 1)
1038     letter = keyname[0];
1039   else if (strcmp(keyname, "space") == 0)
1040     letter = ' ';
1041   else if (strcmp(keyname, "circumflex") == 0)
1042     letter = '^';
1043
1044   return letter;
1045 }
1046
1047 /* ------------------------------------------------------------------------- */
1048 /* some functions to handle lists of level directories                       */
1049 /* ------------------------------------------------------------------------- */
1050
1051 struct LevelDirInfo *newLevelDirInfo()
1052 {
1053   return checked_calloc(sizeof(struct LevelDirInfo));
1054 }
1055
1056 void pushLevelDirInfo(struct LevelDirInfo **node_first,
1057                       struct LevelDirInfo *node_new)
1058 {
1059   node_new->next = *node_first;
1060   *node_first = node_new;
1061 }
1062
1063 int numLevelDirInfo(struct LevelDirInfo *node)
1064 {
1065   int num = 0;
1066
1067   while (node)
1068   {
1069     num++;
1070     node = node->next;
1071   }
1072
1073   return num;
1074 }
1075
1076 boolean validLevelSeries(struct LevelDirInfo *node)
1077 {
1078   return (node != NULL && !node->node_group && !node->parent_link);
1079 }
1080
1081 struct LevelDirInfo *getFirstValidLevelSeries(struct LevelDirInfo *node)
1082 {
1083   if (node == NULL)
1084   {
1085     if (leveldir_first)         /* start with first level directory entry */
1086       return getFirstValidLevelSeries(leveldir_first);
1087     else
1088       return NULL;
1089   }
1090   else if (node->node_group)    /* enter level group (step down into tree) */
1091     return getFirstValidLevelSeries(node->node_group);
1092   else if (node->parent_link)   /* skip start entry of level group */
1093   {
1094     if (node->next)             /* get first real level series entry */
1095       return getFirstValidLevelSeries(node->next);
1096     else                        /* leave empty level group and go on */
1097       return getFirstValidLevelSeries(node->node_parent->next);
1098   }
1099   else                          /* this seems to be a regular level series */
1100     return node;
1101 }
1102
1103 struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *node)
1104 {
1105   if (node == NULL)
1106     return NULL;
1107
1108   if (node->node_parent == NULL)                /* top level group */
1109     return leveldir_first;
1110   else                                          /* sub level group */
1111     return node->node_parent->node_group;
1112 }
1113
1114 int numLevelDirInfoInGroup(struct LevelDirInfo *node)
1115 {
1116   return numLevelDirInfo(getLevelDirInfoFirstGroupEntry(node));
1117 }
1118
1119 int posLevelDirInfo(struct LevelDirInfo *node)
1120 {
1121   struct LevelDirInfo *node_cmp = getLevelDirInfoFirstGroupEntry(node);
1122   int pos = 0;
1123
1124   while (node_cmp)
1125   {
1126     if (node_cmp == node)
1127       return pos;
1128
1129     pos++;
1130     node_cmp = node_cmp->next;
1131   }
1132
1133   return 0;
1134 }
1135
1136 struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *node, int pos)
1137 {
1138   struct LevelDirInfo *node_default = node;
1139   int pos_cmp = 0;
1140
1141   while (node)
1142   {
1143     if (pos_cmp == pos)
1144       return node;
1145
1146     pos_cmp++;
1147     node = node->next;
1148   }
1149
1150   return node_default;
1151 }
1152
1153 struct LevelDirInfo *getLevelDirInfoFromFilenameExt(struct LevelDirInfo *node,
1154                                                     char *filename)
1155 {
1156   if (filename == NULL)
1157     return NULL;
1158
1159   while (node)
1160   {
1161     if (node->node_group)
1162     {
1163       struct LevelDirInfo *node_group;
1164
1165       node_group = getLevelDirInfoFromFilenameExt(node->node_group, filename);
1166
1167       if (node_group)
1168         return node_group;
1169     }
1170     else if (!node->parent_link)
1171     {
1172       if (strcmp(filename, node->filename) == 0)
1173         return node;
1174     }
1175
1176     node = node->next;
1177   }
1178
1179   return NULL;
1180 }
1181
1182 struct LevelDirInfo *getLevelDirInfoFromFilename(char *filename)
1183 {
1184   return getLevelDirInfoFromFilenameExt(leveldir_first, filename);
1185 }
1186
1187 void dumpLevelDirInfo(struct LevelDirInfo *node, int depth)
1188 {
1189   int i;
1190
1191   while (node)
1192   {
1193     for (i=0; i<depth * 3; i++)
1194       printf(" ");
1195
1196     printf("filename == '%s'\n", node->filename);
1197
1198     if (node->node_group != NULL)
1199       dumpLevelDirInfo(node->node_group, depth + 1);
1200
1201     node = node->next;
1202   }
1203 }
1204
1205 void sortLevelDirInfo(struct LevelDirInfo **node_first,
1206                       int (*compare_function)(const void *, const void *))
1207 {
1208   int num_nodes = numLevelDirInfo(*node_first);
1209   struct LevelDirInfo **sort_array;
1210   struct LevelDirInfo *node = *node_first;
1211   int i = 0;
1212
1213   if (num_nodes == 0)
1214     return;
1215
1216   /* allocate array for sorting structure pointers */
1217   sort_array = checked_calloc(num_nodes * sizeof(struct LevelDirInfo *));
1218
1219   /* writing structure pointers to sorting array */
1220   while (i < num_nodes && node)         /* double boundary check... */
1221   {
1222     sort_array[i] = node;
1223
1224     i++;
1225     node = node->next;
1226   }
1227
1228   /* sorting the structure pointers in the sorting array */
1229   qsort(sort_array, num_nodes, sizeof(struct LevelDirInfo *),
1230         compare_function);
1231
1232   /* update the linkage of list elements with the sorted node array */
1233   for (i=0; i<num_nodes - 1; i++)
1234     sort_array[i]->next = sort_array[i + 1];
1235   sort_array[num_nodes - 1]->next = NULL;
1236
1237   /* update the linkage of the main list anchor pointer */
1238   *node_first = sort_array[0];
1239
1240   free(sort_array);
1241
1242   /* now recursively sort the level group structures */
1243   node = *node_first;
1244   while (node)
1245   {
1246     if (node->node_group != NULL)
1247       sortLevelDirInfo(&node->node_group, compare_function);
1248
1249     node = node->next;
1250   }
1251 }
1252
1253 inline void swap_numbers(int *i1, int *i2)
1254 {
1255   int help = *i1;
1256
1257   *i1 = *i2;
1258   *i2 = help;
1259 }
1260
1261 inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
1262 {
1263   int help_x = *x1;
1264   int help_y = *y1;
1265
1266   *x1 = *x2;
1267   *x2 = help_x;
1268
1269   *y1 = *y2;
1270   *y2 = help_y;
1271 }
1272
1273
1274 /* ========================================================================= */
1275 /* some stuff from "files.c"                                                 */
1276 /* ========================================================================= */
1277
1278 #if defined(PLATFORM_WIN32)
1279 #ifndef S_IRGRP
1280 #define S_IRGRP S_IRUSR
1281 #endif
1282 #ifndef S_IROTH
1283 #define S_IROTH S_IRUSR
1284 #endif
1285 #ifndef S_IWGRP
1286 #define S_IWGRP S_IWUSR
1287 #endif
1288 #ifndef S_IWOTH
1289 #define S_IWOTH S_IWUSR
1290 #endif
1291 #ifndef S_IXGRP
1292 #define S_IXGRP S_IXUSR
1293 #endif
1294 #ifndef S_IXOTH
1295 #define S_IXOTH S_IXUSR
1296 #endif
1297 #endif  /* PLATFORM_WIN32 */
1298
1299 /* file permissions for newly written files */
1300 #define MODE_R_ALL              (S_IRUSR | S_IRGRP | S_IROTH)
1301 #define MODE_W_ALL              (S_IWUSR | S_IWGRP | S_IWOTH)
1302 #define MODE_X_ALL              (S_IXUSR | S_IXGRP | S_IXOTH)
1303 #define MODE_W_ALL              (S_IWUSR | S_IWGRP | S_IWOTH)
1304
1305 #define MODE_W_USR_GRP          (S_IWUSR | S_IWGRP)
1306 #define MODE_W_NEW_DIR          (S_ISGID | MODE_W_USR_GRP)
1307
1308 #define USERDATA_DIR_MODE       (MODE_R_ALL | MODE_X_ALL | MODE_W_NEW_DIR)
1309
1310 #define LEVEL_PERMS             (MODE_R_ALL | MODE_W_USR_GRP)
1311 #define SCORE_PERMS             LEVEL_PERMS
1312 #define TAPE_PERMS              LEVEL_PERMS
1313 #define SETUP_PERMS             LEVEL_PERMS
1314
1315 char *getUserDataDir(void)
1316 {
1317   static char *userdata_dir = NULL;
1318
1319   if (!userdata_dir)
1320   {
1321     char *home_dir = getHomeDir();
1322     char *data_dir = program.userdata_directory;
1323
1324     userdata_dir = getPath2(home_dir, data_dir);
1325   }
1326
1327   return userdata_dir;
1328 }
1329
1330 void createDirectory(char *dir, char *text)
1331 {
1332 #if defined(PLATFORM_UNIX)
1333   /* leave "other" permissions in umask untouched, but ensure group parts
1334      of USERDATA_DIR_MODE are not masked */
1335   mode_t normal_umask = umask(0);
1336   mode_t group_umask = ~(USERDATA_DIR_MODE & S_IRWXG);
1337   umask(normal_umask & group_umask);
1338 #endif
1339
1340   if (access(dir, F_OK) != 0)
1341 #if defined(PLATFORM_WIN32)
1342     if (mkdir(dir) != 0)
1343 #else
1344     if (mkdir(dir, USERDATA_DIR_MODE) != 0)
1345 #endif
1346       Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1347
1348 #if defined(PLATFORM_UNIX)
1349   umask(normal_umask);          /* reset normal umask */
1350 #endif
1351 }
1352
1353 void InitUserDataDirectory()
1354 {
1355   createDirectory(getUserDataDir(), "user data");
1356 }
1357
1358 void SetFilePermissions_Level(char *filename)
1359 {
1360   chmod(filename, LEVEL_PERMS);
1361 }
1362
1363 void SetFilePermissions_Tape(char *filename)
1364 {
1365   chmod(filename, TAPE_PERMS);
1366 }
1367
1368 void SetFilePermissions_Score(char *filename)
1369 {
1370   chmod(filename, SCORE_PERMS);
1371 }
1372
1373 void SetFilePermissions_Setup(char *filename)
1374 {
1375   chmod(filename, SETUP_PERMS);
1376 }
1377
1378
1379 /* ========================================================================= */
1380 /* functions only needed for non-Unix (non-command-line) systems */
1381 /* ========================================================================= */
1382
1383 #if !defined(PLATFORM_UNIX)
1384
1385 #define ERROR_FILENAME          "error.out"
1386
1387 void initErrorFile()
1388 {
1389   char *filename;
1390
1391   InitUserDataDirectory();
1392
1393   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1394   unlink(filename);
1395   free(filename);
1396 }
1397
1398 FILE *openErrorFile()
1399 {
1400   char *filename;
1401   FILE *error_file;
1402
1403   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1404   error_file = fopen(filename, MODE_APPEND);
1405   free(filename);
1406
1407   return error_file;
1408 }
1409
1410 void dumpErrorFile()
1411 {
1412   char *filename;
1413   FILE *error_file;
1414
1415   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1416   error_file = fopen(filename, MODE_READ);
1417   free(filename);
1418
1419   if (error_file != NULL)
1420   {
1421     while (!feof(error_file))
1422       fputc(fgetc(error_file), stderr);
1423
1424     fclose(error_file);
1425   }
1426 }
1427 #endif
1428
1429
1430 /* ========================================================================= */
1431 /* the following is only for debugging purpose and normally not used         */
1432 /* ========================================================================= */
1433
1434 #define DEBUG_NUM_TIMESTAMPS    3
1435
1436 void debug_print_timestamp(int counter_nr, char *message)
1437 {
1438   static long counter[DEBUG_NUM_TIMESTAMPS][2];
1439
1440   if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
1441     Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
1442
1443   counter[counter_nr][0] = Counter();
1444
1445   if (message)
1446     printf("%s %.2f seconds\n", message,
1447            (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
1448
1449   counter[counter_nr][1] = Counter();
1450 }