added program config file support for run-time special edition support
[rocksndiamonds.git] / src / libgame / misc.c
index c123892d60343c061caa9b7741b5a35c909c28a2..660f4df6a80ee83a2a4cfc9733514b1c126f5cf2 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;
+
+#if DUPLICATE_LOG_OUTPUT_TO_STDERR
+  if (file != stderr)
+  {
+    va_list ap2;
+    va_copy(ap2, ap);
+
+    vfprintf(stderr, format, ap2);
 
-  va_end(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++)
+    printf_log_nonewline("%s", line_chars);
+
+  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_nonewline(stream, "%s", line_chars);
+    fprintf(file, "%s", line_chars);
 
-  fprintf_newline(stream, "");
+  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;
@@ -964,6 +1073,9 @@ void Error(int mode, char *format, ...)
   static boolean last_line_was_separator = FALSE;
   char *process_name = "";
 
+  if (program.error_file == NULL)
+    return;
+
 #if defined(PLATFORM_ANDROID)
   android_log_prio = (mode & ERR_DEBUG ? ANDROID_LOG_DEBUG :
                      mode & ERR_INFO ? ANDROID_LOG_INFO :
@@ -979,7 +1091,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;
 
@@ -997,19 +1109,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)
-      fprintf_nonewline(program.error_file, "fatal error: ");
+      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))
@@ -1021,13 +1134,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)
   {
@@ -1857,6 +1968,10 @@ void addNodeToList(ListNode **node_first, char *key, void *content)
   node_new->key = getStringCopy(key);
   node_new->content = content;
   node_new->next = *node_first;
+
+  if (*node_first)
+    (*node_first)->prev = node_new;
+
   *node_first = node_new;
 }
 
@@ -1869,8 +1984,15 @@ void deleteNodeFromList(ListNode **node_first, char *key,
   if (strEqual((*node_first)->key, key))
   {
     checked_free((*node_first)->key);
+
     if (destructor_function)
       destructor_function((*node_first)->content);
+
+    if ((*node_first)->next)
+      (*node_first)->next->prev = (*node_first)->prev;
+
+    checked_free(*node_first);
+
     *node_first = (*node_first)->next;
   }
   else
@@ -3386,7 +3508,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);
 }