improved workaround for valid Android asset directory path names
[rocksndiamonds.git] / src / libgame / misc.c
index 883dd7d2afd7b18cc4b94db45622b2f12f012883..c02d392067f81401ad910a666f72da1eb9e196f8 100644 (file)
 /* ========================================================================= */
 
 /* ------------------------------------------------------------------------- */
-/* platform independent wrappers for printf() et al. (newline aware)         */
+/* logging functions                                                         */
 /* ------------------------------------------------------------------------- */
 
+#define DUPLICATE_LOG_OUTPUT_TO_STDERR         TRUE
+
+
 #if defined(PLATFORM_ANDROID)
 static int android_log_prio = ANDROID_LOG_INFO;
-#endif
+static char *android_log_buffer = NULL;
 
-#if 0
-static void vfPrintLog(FILE *stream, char *format, va_list ap)
+static void append_to_android_log_buffer(char *format, va_list ap)
 {
-}
+  char text_new[MAX_OUTPUT_LINESIZE];
 
-static void vfPrintLog(FILE *stream, char *format, va_list ap)
-{
+  // print text to temporary string
+  vsnprintf(text_new, MAX_OUTPUT_LINESIZE, format, ap);
+
+  if (android_log_buffer == NULL)
+  {
+    android_log_buffer = getStringCopy(text_new);
+  }
+  else
+  {
+    char *android_log_buffer_old = android_log_buffer;
+
+    // append new text to existing text
+    android_log_buffer = getStringCat2(android_log_buffer, text_new);
+
+    checked_free(android_log_buffer_old);
+  }
 }
 
-static void fPrintLog(FILE *stream, char *format, va_list ap)
+static void vprintf_log_nonewline(char *format, va_list ap)
 {
+  // add log output to buffer until text with newline is printed
+  append_to_android_log_buffer(format, ap);
 }
 
-static void fPrintLog(FILE *stream, char *format, va_list ap)
+static void vprintf_log(char *format, va_list ap)
 {
+  // add log output to buffer
+  append_to_android_log_buffer(format, ap);
+
+  // __android_log_vprint(android_log_prio, program.program_title, format, ap);
+  __android_log_write(android_log_prio, program.program_title,
+                     android_log_buffer);
+
+  checked_free(android_log_buffer);
+  android_log_buffer = NULL;
 }
-#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);
+static void vprintf_log_nonewline(char *format, va_list ap)
+{
+  FILE *file = program.error_file;
 
-  va_end(ap2);
+#if DUPLICATE_LOG_OUTPUT_TO_STDERR
+  if (file != stderr)
+  {
+    va_list ap2;
+    va_copy(ap2, ap);
+
+    vfprintf(stderr, format, ap2);
+
+    va_end(ap2);
+  }
 #endif
+
+  vfprintf(file, format, ap);
 }
 
-static void vfprintf_newline(FILE *stream, char *format, va_list ap)
+static void vprintf_log(char *format, va_list ap)
 {
-#if defined(PLATFORM_ANDROID)
-  __android_log_vprint(android_log_prio, program.program_title, format, ap);
-#else
+  FILE *file = program.error_file;
   char *newline = STRING_NEWLINE;
 
-  va_list ap2;
-  va_copy(ap2, ap);
-
-  vfprintf(stream, format, ap);
-  fprintf(stream, "%s", newline);
+#if DUPLICATE_LOG_OUTPUT_TO_STDERR
+  if (file != stderr)
+  {
+    va_list ap2;
+    va_copy(ap2, ap);
 
-  vfprintf(stderr, format, ap2);
-  fprintf(stderr, "%s", newline);
+    vfprintf(stderr, format, ap2);
+    fprintf(stderr, "%s", newline);
 
-  va_end(ap2);
+    va_end(ap2);
+  }
 #endif
+
+  vfprintf(file, format, ap);
+  fprintf(file, "%s", newline);
 }
+#endif
 
-static void fprintf_nonewline(FILE *stream, char *format, ...)
+static void printf_log_nonewline(char *format, ...)
 {
   va_list ap;
 
   va_start(ap, format);
-  vfprintf_nonewline(stream, format, ap);
+  vprintf_log_nonewline(format, ap);
   va_end(ap);
 }
 
-static void fprintf_newline(FILE *stream, char *format, ...)
+static void printf_log(char *format, ...)
 {
   va_list ap;
 
   va_start(ap, format);
-  vfprintf_newline(stream, format, ap);
+  vprintf_log(format, ap);
   va_end(ap);
 }
 
-void fprintf_line(FILE *stream, char *line_chars, int line_length)
+static void printf_log_line(char *line_chars, int line_length)
 {
   int i;
 
   for (i = 0; i < line_length; i++)
-    fprintf_nonewline(stream, "%s", line_chars);
+    printf_log_nonewline("%s", line_chars);
 
-  fprintf_newline(stream, "");
+  printf_log("");
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* platform independent wrappers for printf() et al.                         */
+/* ------------------------------------------------------------------------- */
+
+void fprintf_line(FILE *file, char *line_chars, int line_length)
+{
+  int i;
+
+  for (i = 0; i < line_length; i++)
+    fprintf(file, "%s", line_chars);
+
+  fprintf(file, "\n");
+}
+
+void fprintf_line_with_prefix(FILE *file, char *prefix, char *line_chars,
+                             int line_length)
+{
+  fprintf(file, "%s", prefix);
+  fprintf_line(file, line_chars, line_length);
 }
 
 void printf_line(char *line_chars, int line_length)
@@ -134,8 +190,7 @@ void printf_line(char *line_chars, int line_length)
 
 void printf_line_with_prefix(char *prefix, char *line_chars, int line_length)
 {
-  fprintf(stdout, "%s", prefix);
-  fprintf_line(stdout, line_chars, line_length);
+  fprintf_line_with_prefix(stdout, prefix, line_chars, line_length);
 }
 
 
@@ -555,6 +610,9 @@ static char *getProgramMainDataPath()
 
 char *getStringCat2WithSeparator(char *s1, char *s2, char *sep)
 {
+  if (s1 == NULL || s2 == NULL || sep == NULL)
+    return NULL;
+
   char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
                                         strlen(s2) + 1);
 
@@ -565,6 +623,9 @@ char *getStringCat2WithSeparator(char *s1, char *s2, char *sep)
 
 char *getStringCat3WithSeparator(char *s1, char *s2, char *s3, char *sep)
 {
+  if (s1 == NULL || s2 == NULL || s3 == NULL || sep == NULL)
+    return NULL;
+
   char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
                                         strlen(s2) + strlen(sep) +
                                         strlen(s3) + 1);
@@ -587,9 +648,11 @@ char *getStringCat3(char *s1, char *s2, char *s3)
 char *getPath2(char *path1, char *path2)
 {
 #if defined(PLATFORM_ANDROID)
-  // workaround for reading from APK assets directory -- skip leading "./"
+  // workaround for reading from assets directory -- skip "." subdirs in path
   if (strEqual(path1, "."))
     return getStringCopy(path2);
+  else if (strEqual(path2, "."))
+    return getStringCopy(path1);
 #endif
 
   return getStringCat2WithSeparator(path1, path2, STRING_PATH_SEPARATOR);
@@ -598,14 +661,60 @@ char *getPath2(char *path1, char *path2)
 char *getPath3(char *path1, char *path2, char *path3)
 {
 #if defined(PLATFORM_ANDROID)
-  // workaround for reading from APK assets directory -- skip leading "./"
+  // workaround for reading from assets directory -- skip "." subdirs in path
   if (strEqual(path1, "."))
     return getStringCat2WithSeparator(path2, path3, STRING_PATH_SEPARATOR);
+  else if (strEqual(path2, "."))
+    return getStringCat2WithSeparator(path1, path3, STRING_PATH_SEPARATOR);
+  else if (strEqual(path3, "."))
+    return getStringCat2WithSeparator(path1, path2, STRING_PATH_SEPARATOR);
 #endif
 
   return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR);
 }
 
+char *getImg2(char *path1, char *path2)
+{
+  char *filename = getPath2(path1, path2);
+
+  if (!fileExists(filename) && strSuffix(path2, ".png"))
+  {
+    // backward compatibility: if PNG file not found, check for PCX file
+    char *path2pcx = getStringCopy(path2);
+
+    strcpy(&path2pcx[strlen(path2pcx) - 3], "pcx");
+
+    free(filename);
+
+    filename = getPath2(path1, path2pcx);
+
+    free(path2pcx);
+  }
+
+  return filename;
+}
+
+char *getImg3(char *path1, char *path2, char *path3)
+{
+  char *filename = getPath3(path1, path2, path3);
+
+  if (!fileExists(filename) && strSuffix(path3, ".png"))
+  {
+    // backward compatibility: if PNG file not found, check for PCX file
+    char *path3pcx = getStringCopy(path3);
+
+    strcpy(&path3pcx[strlen(path3pcx) - 3], "pcx");
+
+    free(filename);
+
+    filename = getPath3(path1, path2, path3pcx);
+
+    free(path3pcx);
+  }
+
+  return filename;
+}
+
 char *getStringCopy(const char *s)
 {
   char *s_copy;
@@ -760,7 +869,6 @@ void GetOptions(char *argv[],
   options.network = FALSE;
   options.verbose = FALSE;
   options.debug = FALSE;
-  options.debug_x11_sync = FALSE;
 
 #if 1
   options.verbose = TRUE;
@@ -888,10 +996,6 @@ void GetOptions(char *argv[],
     {
       options.debug = TRUE;
     }
-    else if (strncmp(option, "-debug-x11-sync", option_len) == 0)
-    {
-      options.debug_x11_sync = TRUE;
-    }
     else if (strncmp(option, "-verbose", option_len) == 0)
     {
       options.verbose = TRUE;
@@ -984,7 +1088,7 @@ void Error(int mode, char *format, ...)
   if (mode == ERR_INFO_LINE)
   {
     if (!last_line_was_separator)
-      fprintf_line(program.error_file, format, 79);
+      printf_log_line(format, 79);
 
     last_line_was_separator = TRUE;
 
@@ -1002,16 +1106,20 @@ void Error(int mode, char *format, ...)
 
   if (format)
   {
-    va_list ap;
-
-    fprintf_nonewline(program.error_file, "%s%s: ", program.command_basename,
-                     process_name);
+#if !defined(PLATFORM_ANDROID)
+    printf_log_nonewline("%s%s: ", program.command_basename, process_name);
+#endif
 
     if (mode & ERR_WARN)
-      fprintf_nonewline(program.error_file, "warning: ");
+      printf_log_nonewline("warning: ");
+
+    if (mode & ERR_EXIT)
+      printf_log_nonewline("fatal error: ");
+
+    va_list ap;
 
     va_start(ap, format);
-    vfprintf_newline(program.error_file, format, ap);
+    vprintf_log(format, ap);
     va_end(ap);
 
     if ((mode & ERR_EXIT) && !(mode & ERR_FROM_SERVER))
@@ -1023,13 +1131,11 @@ void Error(int mode, char *format, ...)
   }
   
   if (mode & ERR_HELP)
-    fprintf_newline(program.error_file,
-                   "%s: Try option '--help' for more information.",
-                   program.command_basename);
+    printf_log("%s: Try option '--help' for more information.",
+              program.command_basename);
 
   if (mode & ERR_EXIT)
-    fprintf_newline(program.error_file, "%s%s: aborting",
-                   program.command_basename, process_name);
+    printf_log("%s%s: aborting", program.command_basename, process_name);
 
   if (mode & ERR_EXIT)
   {
@@ -1418,7 +1524,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     { KSYM_braceright, "XK_braceright",        "brace right" },
     { KSYM_asciitilde, "XK_asciitilde",        "~" },
 
-    /* special (non-ASCII) keys (ISO-Latin-1) */
+    /* special (non-ASCII) keys */
     { KSYM_degree,     "XK_degree",            "degree" },
     { KSYM_Adiaeresis, "XK_Adiaeresis",        "A umlaut" },
     { KSYM_Odiaeresis, "XK_Odiaeresis",        "O umlaut" },
@@ -1699,6 +1805,26 @@ Key getKeyFromX11KeyName(char *x11name)
 
 char getCharFromKey(Key key)
 {
+  static struct
+  {
+    Key key;
+    byte key_char;
+  } translate_key_char[] =
+  {
+    /* special (non-ASCII) keys (ISO-8859-1) */
+    { KSYM_degree,     CHAR_BYTE_DEGREE        },
+    { KSYM_Adiaeresis, CHAR_BYTE_UMLAUT_A      },
+    { KSYM_Odiaeresis, CHAR_BYTE_UMLAUT_O      },
+    { KSYM_Udiaeresis, CHAR_BYTE_UMLAUT_U      },
+    { KSYM_adiaeresis, CHAR_BYTE_UMLAUT_a      },
+    { KSYM_odiaeresis, CHAR_BYTE_UMLAUT_o      },
+    { KSYM_udiaeresis, CHAR_BYTE_UMLAUT_u      },
+    { KSYM_ssharp,     CHAR_BYTE_SHARP_S       },
+
+    /* end-of-array identifier */
+    { 0,                0                      }
+  };
+
   char *keyname = getKeyNameFromKey(key);
   char c = 0;
 
@@ -1706,6 +1832,21 @@ char getCharFromKey(Key key)
     c = keyname[0];
   else if (strEqual(keyname, "space"))
     c = ' ';
+  else
+  {
+    int i = 0;
+
+    do
+    {
+      if (key == translate_key_char[i].key)
+      {
+       c = translate_key_char[i].key_char;
+
+       break;
+      }
+    }
+    while (translate_key_char[++i].key_char);
+  }
 
   return c;
 }
@@ -2176,7 +2317,9 @@ boolean directoryExists(char *dir_name)
   if (dir_name == NULL)
     return FALSE;
 
-  boolean success = (access(dir_name, F_OK) == 0);
+  struct stat file_status;
+  boolean success = (stat(dir_name, &file_status) == 0 &&
+                    (file_status.st_mode & S_IFMT) == S_IFDIR);
 
 #if defined(PLATFORM_ANDROID)
   if (!success)
@@ -2264,40 +2407,37 @@ boolean fileHasSuffix(char *basename, char *suffix)
   return FALSE;
 }
 
-static boolean FileCouldBeArtwork(char *basename)
+static boolean FileCouldBeArtwork(char *filename)
 {
+  char *basename = getBaseNamePtr(filename);
+
   return (!strEqual(basename, ".") &&
          !strEqual(basename, "..") &&
          !fileHasSuffix(basename, "txt") &&
-         !fileHasSuffix(basename, "conf"));
+         !fileHasSuffix(basename, "conf") &&
+         !directoryExists(filename));
 }
 
 boolean FileIsGraphic(char *filename)
 {
-  char *basename = getBaseNamePtr(filename);
-
-  return FileCouldBeArtwork(basename);
+  return FileCouldBeArtwork(filename);
 }
 
 boolean FileIsSound(char *filename)
 {
-  char *basename = getBaseNamePtr(filename);
-
-  return FileCouldBeArtwork(basename);
+  return FileCouldBeArtwork(filename);
 }
 
 boolean FileIsMusic(char *filename)
 {
-  char *basename = getBaseNamePtr(filename);
-
-  return FileCouldBeArtwork(basename);
+  return FileCouldBeArtwork(filename);
 }
 
-boolean FileIsArtworkType(char *basename, int type)
+boolean FileIsArtworkType(char *filename, int type)
 {
-  if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(basename)) ||
-      (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(basename)) ||
-      (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(basename)))
+  if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(filename)) ||
+      (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(filename)) ||
+      (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(filename)))
     return TRUE;
 
   return FALSE;
@@ -3354,7 +3494,7 @@ void openErrorFile()
 
 void closeErrorFile()
 {
-  if (program.error_file != stderr)    /* do not close stream 'stderr' */
+  if (program.error_file != stderr)    /* do not close file 'stderr' */
     fclose(program.error_file);
 }