updated contact info in source file headers
[rocksndiamonds.git] / src / libgame / misc.c
index 710917a36376117cd12fe7fdb826e463780baa9b..8a7c94d60acd22985fb44830cd165acf05a34732 100644 (file)
@@ -1,15 +1,13 @@
-/***********************************************************
-* Artsoft Retro-Game Library                               *
-*----------------------------------------------------------*
-* (c) 1994-2006 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* misc.c                                                   *
-***********************************************************/
+// ============================================================================
+// Artsoft Retro-Game Library
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// misc.c
+// ============================================================================
 
 #include <time.h>
 #include <sys/time.h>
@@ -65,6 +63,22 @@ static void fPrintLog(FILE *stream, char *format, va_list ap)
 }
 #endif
 
+static void vfprintf_nonewline(FILE *stream, char *format, va_list ap)
+{
+#if defined(PLATFORM_ANDROID)
+  // (prefix text of logging output is currently skipped on Android)
+  //__android_log_vprint(android_log_prio, program.program_title, format, ap);
+#else
+  va_list ap2;
+  va_copy(ap2, ap);
+
+  vfprintf(stream, format, ap);
+  vfprintf(stderr, format, ap2);
+
+  va_end(ap2);
+#endif
+}
+
 static void vfprintf_newline(FILE *stream, char *format, va_list ap)
 {
 #if defined(PLATFORM_ANDROID)
@@ -72,11 +86,28 @@ static void vfprintf_newline(FILE *stream, char *format, va_list ap)
 #else
   char *newline = STRING_NEWLINE;
 
+  va_list ap2;
+  va_copy(ap2, ap);
+
   vfprintf(stream, format, ap);
   fprintf(stream, "%s", newline);
+
+  vfprintf(stderr, format, ap2);
+  fprintf(stderr, "%s", newline);
+
+  va_end(ap2);
 #endif
 }
 
+static void fprintf_nonewline(FILE *stream, char *format, ...)
+{
+  va_list ap;
+
+  va_start(ap, format);
+  vfprintf_nonewline(stream, format, ap);
+  va_end(ap);
+}
+
 static void fprintf_newline(FILE *stream, char *format, ...)
 {
   va_list ap;
@@ -91,7 +122,7 @@ void fprintf_line(FILE *stream, char *line_chars, int line_length)
   int i;
 
   for (i = 0; i < line_length; i++)
-    fprintf(stream, "%s", line_chars);
+    fprintf_nonewline(stream, "%s", line_chars);
 
   fprintf_newline(stream, "");
 }
@@ -190,18 +221,6 @@ boolean getTokenValueFromString(char *string, char **token, char **value)
 /* counter functions                                                         */
 /* ------------------------------------------------------------------------- */
 
-#if defined(PLATFORM_MSDOS)
-volatile unsigned int counter = 0;
-
-void increment_counter()
-{
-  counter++;
-}
-
-END_OF_FUNCTION(increment_counter);
-#endif
-
-
 /* maximal allowed length of a command line option */
 #define MAX_OPTION_LEN         256
 
@@ -212,20 +231,7 @@ static unsigned int getCurrentMS()
 {
   return SDL_GetTicks();
 }
-
-#else /* !TARGET_SDL */
-
-#if defined(PLATFORM_UNIX)
-static unsigned int getCurrentMS()
-{
-  struct timeval current_time;
-
-  gettimeofday(&current_time, NULL);
-
-  return current_time.tv_sec * 1000 + current_time.tv_usec / 1000;
-}
-#endif /* PLATFORM_UNIX */
-#endif /* !TARGET_SDL */
+#endif
 
 static unsigned int mainCounter(int mode)
 {
@@ -262,50 +268,18 @@ static unsigned int mainCounter(int mode)
 
   return counter_ms;           /* return milliseconds since last init */
 }
-
-#else /* !TARGET_SDL */
-
-#if defined(PLATFORM_UNIX)
-static unsigned int mainCounter(int mode)
-{
-  static struct timeval base_time = { 0, 0 };
-  struct timeval current_time;
-  unsigned int counter_ms;
-
-  gettimeofday(&current_time, NULL);
-
-  /* reset base time in case of counter initializing or wrap-around */
-  if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
-    base_time = current_time;
-
-  counter_ms = (current_time.tv_sec  - base_time.tv_sec)  * 1000
-             + (current_time.tv_usec - base_time.tv_usec) / 1000;
-
-  return counter_ms;           /* return milliseconds since last init */
-}
-#endif /* PLATFORM_UNIX */
-#endif /* !TARGET_SDL */
+#endif
 
 #endif
 
 void InitCounter()             /* set counter back to zero */
 {
-#if !defined(PLATFORM_MSDOS)
   mainCounter(INIT_COUNTER);
-#else
-  LOCK_VARIABLE(counter);
-  LOCK_FUNCTION(increment_counter);
-  install_int_ex(increment_counter, BPS_TO_TIMER(100));
-#endif
 }
 
 unsigned int Counter() /* get milliseconds since last call of InitCounter() */
 {
-#if !defined(PLATFORM_MSDOS)
   return mainCounter(READ_COUNTER);
-#else
-  return (counter * 10);
-#endif
 }
 
 static void sleep_milliseconds(unsigned int milliseconds_delay)
@@ -329,8 +303,6 @@ static void sleep_milliseconds(unsigned int milliseconds_delay)
   {
 #if defined(TARGET_SDL)
     SDL_Delay(milliseconds_delay);
-#elif defined(TARGET_ALLEGRO)
-    rest(milliseconds_delay);
 #else
     struct timeval delay;
 
@@ -441,7 +413,7 @@ unsigned int get_random_number(int nr, int max)
 /* system info functions                                                     */
 /* ------------------------------------------------------------------------- */
 
-#if !defined(PLATFORM_MSDOS) && !defined(PLATFORM_ANDROID)
+#if !defined(PLATFORM_ANDROID)
 static char *get_corrected_real_name(char *real_name)
 {
   char *real_name_new = checked_malloc(MAX_USERNAME_LEN + 1);
@@ -675,7 +647,7 @@ char *getPath3(char *path1, char *path2, char *path3)
   return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR);
 }
 
-char *getStringCopy(char *s)
+char *getStringCopy(const char *s)
 {
   char *s_copy;
 
@@ -688,7 +660,7 @@ char *getStringCopy(char *s)
   return s_copy;
 }
 
-char *getStringCopyN(char *s, int n)
+char *getStringCopyN(const char *s, int n)
 {
   char *s_copy;
   int s_len = MAX(0, n);
@@ -703,7 +675,18 @@ char *getStringCopyN(char *s, int n)
   return s_copy;
 }
 
-char *getStringToLower(char *s)
+char *getStringCopyNStatic(const char *s, int n)
+{
+  static char *s_copy = NULL;
+
+  checked_free(s_copy);
+
+  s_copy = getStringCopyN(s, n);
+
+  return s_copy;
+}
+
+char *getStringToLower(const char *s)
 {
   char *s_copy = checked_malloc(strlen(s) + 1);
   char *s_ptr = s_copy;
@@ -780,7 +763,9 @@ boolean strSuffixLower(char *s, char *suffix)
 /* command line option handling functions                                    */
 /* ------------------------------------------------------------------------- */
 
-void GetOptions(char *argv[], void (*print_usage_function)(void))
+void GetOptions(char *argv[],
+               void (*print_usage_function)(void),
+               void (*print_version_function)(void))
 {
   char *ro_base_path = RO_BASE_PATH;
   char *rw_base_path = RW_BASE_PATH;
@@ -877,7 +862,9 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
     option_len = strlen(option);
 
     if (strEqual(option, "-"))
+    {
       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
+    }
     else if (strncmp(option, "-help", option_len) == 0)
     {
       print_usage_function();
@@ -955,10 +942,6 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
     {
       options.serveronly = TRUE;
     }
-    else if (strncmp(option, "-verbose", option_len) == 0)
-    {
-      options.verbose = TRUE;
-    }
     else if (strncmp(option, "-debug", option_len) == 0)
     {
       options.debug = TRUE;
@@ -967,6 +950,17 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
     {
       options.debug_x11_sync = TRUE;
     }
+    else if (strncmp(option, "-verbose", option_len) == 0)
+    {
+      options.verbose = TRUE;
+    }
+    else if (strncmp(option, "-version", option_len) == 0 ||
+            strncmp(option, "-V", option_len) == 0)
+    {
+      print_version_function();
+
+      exit(0);
+    }
     else if (strPrefix(option, "-D"))
     {
 #if 1
@@ -1024,15 +1018,17 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
 /* error handling functions                                                  */
 /* ------------------------------------------------------------------------- */
 
+#define MAX_INTERNAL_ERROR_SIZE                1024
+
 /* used by SetError() and GetError() to store internal error messages */
-static char internal_error[1024];      /* this is bad */
+static char internal_error[MAX_INTERNAL_ERROR_SIZE];
 
 void SetError(char *format, ...)
 {
   va_list ap;
 
   va_start(ap, format);
-  vsprintf(internal_error, format, ap);
+  vsnprintf(internal_error, MAX_INTERNAL_ERROR_SIZE, format, ap);
   va_end(ap);
 }
 
@@ -1083,11 +1079,11 @@ void Error(int mode, char *format, ...)
   {
     va_list ap;
 
-    fprintf(program.error_file, "%s%s: ", program.command_basename,
-           process_name);
+    fprintf_nonewline(program.error_file, "%s%s: ", program.command_basename,
+                     process_name);
 
     if (mode & ERR_WARN)
-      fprintf(program.error_file, "warning: ");
+      fprintf_nonewline(program.error_file, "warning: ");
 
     va_start(ap, format);
     vfprintf_newline(program.error_file, format, ap);
@@ -1568,8 +1564,10 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     { KSYM_Page_Up,    "XK_Page_Up",           "page up" },
     { KSYM_Page_Down,  "XK_Page_Down",         "page down" },
 
+#if defined(TARGET_SDL2)
     { KSYM_Menu,       "XK_Menu",              "menu" },        /* menu key */
     { KSYM_Back,       "XK_Back",              "back" },        /* back key */
+#endif
 
     /* ASCII 0x20 to 0x40 keys (except numbers) */
     { KSYM_space,      "XK_space",             "space" },
@@ -1609,8 +1607,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     { KSYM_braceright, "XK_braceright",        "brace right" },
     { KSYM_asciitilde, "XK_asciitilde",        "~" },
 
-#if !defined(TARGET_SDL2)
-    /* special (non-ASCII) keys */
+    /* special (non-ASCII) keys (ISO-Latin-1) */
     { KSYM_degree,     "XK_degree",            "°" },
     { KSYM_Adiaeresis, "XK_Adiaeresis",        "Ä" },
     { KSYM_Odiaeresis, "XK_Odiaeresis",        "Ö" },
@@ -1619,6 +1616,34 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     { KSYM_odiaeresis, "XK_odiaeresis",        "ö" },
     { KSYM_udiaeresis, "XK_udiaeresis",        "ü" },
     { KSYM_ssharp,     "XK_ssharp",            "sharp s" },
+
+#if defined(TARGET_SDL2)
+    /* special (non-ASCII) keys (UTF-8, for reverse mapping only) */
+    { KSYM_degree,     "XK_degree",            "\xc2\xb0" },
+    { KSYM_Adiaeresis, "XK_Adiaeresis",        "\xc3\x84" },
+    { KSYM_Odiaeresis, "XK_Odiaeresis",        "\xc3\x96" },
+    { KSYM_Udiaeresis, "XK_Udiaeresis",        "\xc3\x9c" },
+    { KSYM_adiaeresis, "XK_adiaeresis",        "\xc3\xa4" },
+    { KSYM_odiaeresis, "XK_odiaeresis",        "\xc3\xb6" },
+    { KSYM_udiaeresis, "XK_udiaeresis",        "\xc3\xbc" },
+    { KSYM_ssharp,     "XK_ssharp",            "\xc3\x9f" },
+
+    /* other keys (for reverse mapping only) */
+    { KSYM_space,      "XK_space",             " " },
+#endif
+
+#if defined(TARGET_SDL2)
+    /* keypad keys are not in numerical order in SDL2 */
+    { KSYM_KP_0,       "XK_KP_0",              "keypad 0" },
+    { KSYM_KP_1,       "XK_KP_1",              "keypad 1" },
+    { KSYM_KP_2,       "XK_KP_2",              "keypad 2" },
+    { KSYM_KP_3,       "XK_KP_3",              "keypad 3" },
+    { KSYM_KP_4,       "XK_KP_4",              "keypad 4" },
+    { KSYM_KP_5,       "XK_KP_5",              "keypad 5" },
+    { KSYM_KP_6,       "XK_KP_6",              "keypad 6" },
+    { KSYM_KP_7,       "XK_KP_7",              "keypad 7" },
+    { KSYM_KP_8,       "XK_KP_8",              "keypad 8" },
+    { KSYM_KP_9,       "XK_KP_9",              "keypad 9" },
 #endif
 
     /* end-of-array identifier */
@@ -1638,8 +1663,10 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
     else if (key >= KSYM_0 && key <= KSYM_9)
       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
+#if !defined(TARGET_SDL2)
     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
+#endif
     else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
       sprintf(name_buffer, "F%d", (int)(key - KSYM_FKEY_FIRST + 1));
     else if (key == KSYM_UNDEFINED)
@@ -1675,8 +1702,10 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
     else if (key >= KSYM_0 && key <= KSYM_9)
       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
+#if !defined(TARGET_SDL2)
     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
+#endif
     else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
       sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_FKEY_FIRST + 1));
     else if (key == KSYM_UNDEFINED)
@@ -1704,17 +1733,34 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
   else if (mode == TRANSLATE_KEYNAME_TO_KEYSYM)
   {
     Key key = KSYM_UNDEFINED;
+    char *name_ptr = *name;
 
-    i = 0;
-    do
+    if (strlen(*name) == 1)
     {
-      if (strEqual(translate_key[i].name, *name))
+      char c = name_ptr[0];
+
+      if (c >= 'A' && c <= 'Z')
+       key = KSYM_A + (Key)(c - 'A');
+      else if (c >= 'a' && c <= 'z')
+       key = KSYM_a + (Key)(c - 'a');
+      else if (c >= '0' && c <= '9')
+       key = KSYM_0 + (Key)(c - '0');
+    }
+
+    if (key == KSYM_UNDEFINED)
+    {
+      i = 0;
+
+      do
       {
-       key = translate_key[i].key;
-       break;
+       if (strEqual(translate_key[i].name, *name))
+       {
+         key = translate_key[i].key;
+         break;
+       }
       }
+      while (translate_key[++i].x11name);
     }
-    while (translate_key[++i].x11name);
 
     if (key == KSYM_UNDEFINED)
       Error(ERR_WARN, "getKeyFromKeyName(): not completely implemented");
@@ -1737,6 +1783,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       else if (c >= '0' && c <= '9')
        key = KSYM_0 + (Key)(c - '0');
     }
+#if !defined(TARGET_SDL2)
     else if (strPrefix(name_ptr, "XK_KP_") && strlen(name_ptr) == 7)
     {
       char c = name_ptr[6];
@@ -1744,6 +1791,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       if (c >= '0' && c <= '9')
        key = KSYM_KP_0 + (Key)(c - '0');
     }
+#endif
     else if (strPrefix(name_ptr, "XK_F") && strlen(name_ptr) <= 6)
     {
       char c1 = name_ptr[4];
@@ -2055,7 +2103,7 @@ int closeFile(File *file)
   if (file == NULL)
     return -1;
 
-  int result;
+  int result = 0;
 
 #if defined(PLATFORM_ANDROID)
   if (file->asset_file)
@@ -2215,7 +2263,7 @@ int closeDirectory(Directory *dir)
   if (dir == NULL)
     return -1;
 
-  int result;
+  int result = 0;
 
 #if defined(PLATFORM_ANDROID)
   if (dir->asset_toc_file)
@@ -2411,13 +2459,22 @@ boolean fileHasSuffix(char *basename, char *suffix)
   return FALSE;
 }
 
+#if defined(TARGET_SDL)
+static boolean FileCouldBeArtwork(char *basename)
+{
+  return (!strEqual(basename, ".") &&
+         !strEqual(basename, "..") &&
+         !fileHasSuffix(basename, "txt") &&
+         !fileHasSuffix(basename, "conf"));
+}
+#endif
+
 boolean FileIsGraphic(char *filename)
 {
   char *basename = getBaseNamePtr(filename);
 
 #if defined(TARGET_SDL)
-  return (!fileHasSuffix(basename, "txt") &&
-         !fileHasSuffix(basename, "conf"));
+  return FileCouldBeArtwork(basename);
 #else
   return fileHasSuffix(basename, "pcx");
 #endif
@@ -2428,8 +2485,7 @@ boolean FileIsSound(char *filename)
   char *basename = getBaseNamePtr(filename);
 
 #if defined(TARGET_SDL)
-  return (!fileHasSuffix(basename, "txt") &&
-         !fileHasSuffix(basename, "conf"));
+  return FileCouldBeArtwork(basename);
 #else
   return fileHasSuffix(basename, "wav");
 #endif
@@ -2440,8 +2496,7 @@ boolean FileIsMusic(char *filename)
   char *basename = getBaseNamePtr(filename);
 
 #if defined(TARGET_SDL)
-  return (!fileHasSuffix(basename, "txt") &&
-         !fileHasSuffix(basename, "conf"));
+  return FileCouldBeArtwork(basename);
 #else
   if (FileIsSound(basename))
     return TRUE;
@@ -3645,6 +3700,9 @@ void openErrorFile()
     Error(ERR_WARN, "cannot open file '%s' for writing: %s",
          program.error_filename, strerror(errno));
   }
+
+  /* error output should be unbuffered so it is not truncated in a crash */
+  setbuf(program.error_file, NULL);
 }
 
 void closeErrorFile()
@@ -3685,7 +3743,7 @@ void NotifyUserAboutErrorFile()
 
 #if DEBUG
 
-#define DEBUG_PRINT_INIT_TIMESTAMPS            TRUE
+#define DEBUG_PRINT_INIT_TIMESTAMPS            FALSE
 #define DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH      10
 
 #define DEBUG_NUM_TIMESTAMPS                   10
@@ -3782,6 +3840,8 @@ void debug_print_parent_only(char *format, ...)
   }
 }
 
+#endif /* DEBUG */
+
 void print_timestamp_ext(char *message, char *mode)
 {
 #if DEBUG_PRINT_INIT_TIMESTAMPS
@@ -3845,5 +3905,3 @@ void print_timestamp_done(char *message)
 {
   print_timestamp_ext(message, "DONE");
 }
-
-#endif /* DEBUG */