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