rnd-20100325-1-src
[rocksndiamonds.git] / src / libgame / misc.c
index 2cf6bd84e8b7a8f7b335a0f76e238ce4e223dabb..35b9c5abc357cdb36b8c874a4b138d3dc1c0e6fd 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2002 Artsoft Entertainment                      *
+* (c) 1994-2006 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -14,6 +14,7 @@
 #include <time.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <stdarg.h>
 #include <ctype.h>
 #include <string.h>
 #include "image.h"
 
 
-/* ------------------------------------------------------------------------- */
+/* ========================================================================= */
 /* some generic helper functions                                             */
+/* ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+/* platform independent wrappers for printf() et al. (newline aware)         */
 /* ------------------------------------------------------------------------- */
 
-void fprintf_line(FILE *stream, char *line_string, int line_length)
+static void vfprintf_newline(FILE *stream, char *format, va_list ap)
+{
+  char *newline = STRING_NEWLINE;
+
+  vfprintf(stream, format, ap);
+
+  fprintf(stream, "%s", newline);
+}
+
+static void fprintf_newline(FILE *stream, char *format, ...)
+{
+  if (format)
+  {
+    va_list ap;
+
+    va_start(ap, format);
+    vfprintf_newline(stream, format, ap);
+    va_end(ap);
+  }
+}
+
+void fprintf_line(FILE *stream, char *line_chars, int line_length)
 {
   int i;
 
   for (i = 0; i < line_length; i++)
-    fprintf(stream, "%s", line_string);
+    fprintf(stream, "%s", line_chars);
 
-  fprintf(stream, "\n");
+  fprintf_newline(stream, "");
 }
 
-void printf_line(char *line_string, int line_length)
+void printf_line(char *line_chars, int line_length)
 {
-  fprintf_line(stdout, line_string, line_length);
+  fprintf_line(stdout, line_chars, line_length);
 }
 
-void printf_line_with_prefix(char *prefix, char *line_string, int line_length)
+void printf_line_with_prefix(char *prefix, char *line_chars, int line_length)
 {
   fprintf(stdout, "%s", prefix);
-  fprintf_line(stdout, line_string, line_length);
+  fprintf_line(stdout, line_chars, line_length);
 }
 
 
+/* ------------------------------------------------------------------------- */
+/* string functions                                                          */
+/* ------------------------------------------------------------------------- */
+
 /* int2str() returns a number converted to a string;
    the used memory is static, but will be overwritten by later calls,
    so if you want to save the result, copy it to a private string buffer;
@@ -77,7 +107,7 @@ char *int2str(int number, int size)
   if (size > 20)
     size = 20;
 
-  if (size)
+  if (size > 0)
   {
     sprintf(s, "                    %09d", number);
     return &s[strlen(s) - size];
@@ -127,6 +157,11 @@ int log_2(unsigned int x)
   return e;
 }
 
+boolean getTokenValueFromString(char *string, char **token, char **value)
+{
+  return getTokenValueFromSetupLine(string, token, value);
+}
+
 
 /* ------------------------------------------------------------------------- */
 /* counter functions                                                         */
@@ -303,13 +338,26 @@ unsigned int init_random_number(int nr, long seed)
 {
   if (seed == NEW_RANDOMIZE)
   {
-#if defined(TARGET_SDL)
-    seed = (long)SDL_GetTicks();
-#else
+    /* default random seed */
+    seed = (long)time(NULL);                   // seconds since the epoch
+
+#if !defined(PLATFORM_WIN32)
+    /* add some more randomness */
     struct timeval current_time;
 
     gettimeofday(&current_time, NULL);
-    seed = (long)current_time.tv_usec;
+
+    seed += (long)current_time.tv_usec;                // microseconds since the epoch
+#endif
+
+#if defined(TARGET_SDL)
+    /* add some more randomness */
+    seed += (long)SDL_GetTicks();              // milliseconds since SDL init
+#endif
+
+#if 1
+    /* add some more randomness */
+    seed += GetSimpleRandom(1000000);
 #endif
   }
 
@@ -421,36 +469,14 @@ char *getRealName()
   return real_name;
 }
 
-char *getHomeDir()
+time_t getFileTimestampEpochSeconds(char *filename)
 {
-  static char *dir = NULL;
+  struct stat file_status;
 
-#if defined(PLATFORM_WIN32)
-  if (dir == NULL)
-  {
-    dir = checked_malloc(MAX_PATH + 1);
-
-    if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir)))
-      strcpy(dir, ".");
-  }
-#elif defined(PLATFORM_UNIX)
-  if (dir == NULL)
-  {
-    if ((dir = getenv("HOME")) == NULL)
-    {
-      struct passwd *pwd;
-
-      if ((pwd = getpwuid(getuid())) != NULL)
-       dir = getStringCopy(pwd->pw_dir);
-      else
-       dir = ".";
-    }
-  }
-#else
-  dir = ".";
-#endif
+  if (stat(filename, &file_status) != 0)       /* cannot stat file */
+    return 0;
 
-  return dir;
+  return file_status.st_mtime;
 }
 
 
@@ -460,17 +486,15 @@ char *getHomeDir()
 
 static char *getLastPathSeparatorPtr(char *filename)
 {
-  char *last_separator = strrchr(filename, '/');
+  char *last_separator = strrchr(filename, CHAR_PATH_SEPARATOR_UNIX);
 
-#if !defined(PLATFORM_UNIX)
   if (last_separator == NULL)  /* also try DOS/Windows variant */
-    last_separator = strrchr(filename, '\\');
-#endif
+    last_separator = strrchr(filename, CHAR_PATH_SEPARATOR_DOS);
 
   return last_separator;
 }
 
-static char *getBaseNamePtr(char *filename)
+char *getBaseNamePtr(char *filename)
 {
   char *last_separator = getLastPathSeparatorPtr(filename);
 
@@ -503,34 +527,45 @@ char *getBasePath(char *filename)
 /* various string functions                                                  */
 /* ------------------------------------------------------------------------- */
 
-char *getPath2(char *path1, char *path2)
+char *getStringCat2WithSeparator(char *s1, char *s2, char *sep)
 {
-  char *complete_path = checked_malloc(strlen(path1) + 1 +
-                                      strlen(path2) + 1);
+  char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
+                                        strlen(s2) + 1);
 
-  sprintf(complete_path, "%s/%s", path1, path2);
+  sprintf(complete_string, "%s%s%s", s1, sep, s2);
 
-  return complete_path;
+  return complete_string;
 }
 
-char *getPath3(char *path1, char *path2, char *path3)
+char *getStringCat3WithSeparator(char *s1, char *s2, char *s3, char *sep)
 {
-  char *complete_path = checked_malloc(strlen(path1) + 1 +
-                                      strlen(path2) + 1 +
-                                      strlen(path3) + 1);
+  char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
+                                        strlen(s2) + strlen(sep) +
+                                        strlen(s3) + 1);
 
-  sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
+  sprintf(complete_string, "%s%s%s%s%s", s1, sep, s2, sep, s3);
 
-  return complete_path;
+  return complete_string;
 }
 
 char *getStringCat2(char *s1, char *s2)
 {
-  char *complete_string = checked_malloc(strlen(s1) + strlen(s2) + 1);
+  return getStringCat2WithSeparator(s1, s2, "");
+}
 
-  sprintf(complete_string, "%s%s", s1, s2);
+char *getStringCat3(char *s1, char *s2, char *s3)
+{
+  return getStringCat3WithSeparator(s1, s2, s3, "");
+}
 
-  return complete_string;
+char *getPath2(char *path1, char *path2)
+{
+  return getStringCat2WithSeparator(path1, path2, STRING_PATH_SEPARATOR);
+}
+
+char *getPath3(char *path1, char *path2, char *path3)
+{
+  return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR);
 }
 
 char *getStringCopy(char *s)
@@ -546,6 +581,21 @@ char *getStringCopy(char *s)
   return s_copy;
 }
 
+char *getStringCopyN(char *s, int n)
+{
+  char *s_copy;
+  int s_len = MAX(0, n);
+
+  if (s == NULL)
+    return NULL;
+
+  s_copy = checked_malloc(s_len + 1);
+  strncpy(s_copy, s, s_len);
+  s_copy[s_len] = '\0';
+
+  return s_copy;
+}
+
 char *getStringToLower(char *s)
 {
   char *s_copy = checked_malloc(strlen(s) + 1);
@@ -573,6 +623,51 @@ boolean strEqual(char *s1, char *s2)
          strcmp(s1, s2) == 0);
 }
 
+boolean strEqualN(char *s1, char *s2, int n)
+{
+  return (s1 == NULL && s2 == NULL ? TRUE  :
+         s1 == NULL && s2 != NULL ? FALSE :
+         s1 != NULL && s2 == NULL ? FALSE :
+         strncmp(s1, s2, n) == 0);
+}
+
+boolean strPrefix(char *s, char *prefix)
+{
+  return (s == NULL && prefix == NULL ? TRUE  :
+         s == NULL && prefix != NULL ? FALSE :
+         s != NULL && prefix == NULL ? FALSE :
+         strncmp(s, prefix, strlen(prefix)) == 0);
+}
+
+boolean strSuffix(char *s, char *suffix)
+{
+  return (s == NULL && suffix == NULL ? TRUE  :
+         s == NULL && suffix != NULL ? FALSE :
+         s != NULL && suffix == NULL ? FALSE :
+         strlen(s) < strlen(suffix)  ? FALSE :
+         strncmp(&s[strlen(s) - strlen(suffix)], suffix, strlen(suffix)) == 0);
+}
+
+boolean strPrefixLower(char *s, char *prefix)
+{
+  char *s_lower = getStringToLower(s);
+  boolean match = strPrefix(s_lower, prefix);
+
+  free(s_lower);
+
+  return match;
+}
+
+boolean strSuffixLower(char *s, char *suffix)
+{
+  char *s_lower = getStringToLower(s);
+  boolean match = strSuffix(s_lower, suffix);
+
+  free(s_lower);
+
+  return match;
+}
+
 
 /* ------------------------------------------------------------------------- */
 /* command line option handling functions                                    */
@@ -601,6 +696,7 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
   options.display_name = NULL;
   options.server_host = NULL;
   options.server_port = 0;
+
   options.ro_base_directory = ro_base_path;
   options.rw_base_directory = rw_base_path;
   options.level_directory    = getPath2(ro_base_path, LEVELS_DIRECTORY);
@@ -608,11 +704,15 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
   options.sounds_directory   = getPath2(ro_base_path, SOUNDS_DIRECTORY);
   options.music_directory    = getPath2(ro_base_path, MUSIC_DIRECTORY);
   options.docs_directory     = getPath2(ro_base_path, DOCS_DIRECTORY);
+
   options.execute_command = NULL;
+  options.special_flags = NULL;
+
   options.serveronly = FALSE;
   options.network = FALSE;
   options.verbose = FALSE;
   options.debug = FALSE;
+  options.debug_x11_sync = FALSE;
 
 #if !defined(PLATFORM_UNIX)
   if (*options_left == NULL)   /* no options given -- enable verbose mode */
@@ -636,7 +736,7 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
     if (strEqual(option, "--"))                        /* stop scanning arguments */
       break;
 
-    if (strncmp(option, "--", 2) == 0)         /* treat '--' like '-' */
+    if (strPrefix(option, "--"))               /* treat '--' like '-' */
       option++;
 
     option_arg = strchr(option, '=');
@@ -738,6 +838,29 @@ void GetOptions(char *argv[], void (*print_usage_function)(void))
     {
       options.debug = TRUE;
     }
+    else if (strncmp(option, "-debug-x11-sync", option_len) == 0)
+    {
+      options.debug_x11_sync = TRUE;
+    }
+    else if (strPrefix(option, "-D"))
+    {
+#if 1
+      options.special_flags = getStringCopy(&option[2]);
+#else
+      char *flags_string = &option[2];
+      unsigned long flags_value;
+
+      if (*flags_string == '\0')
+       Error(ERR_EXIT_HELP, "empty flag ignored");
+
+      flags_value = get_special_flags_function(flags_string);
+
+      if (flags_value == 0)
+       Error(ERR_EXIT_HELP, "unknown flag '%s'", flags_string);
+
+      options.special_flags |= flags_value;
+#endif
+    }
     else if (strncmp(option, "-execute", option_len) == 0)
     {
       if (option_arg == NULL)
@@ -797,17 +920,15 @@ void Error(int mode, char *format, ...)
 {
   static boolean last_line_was_separator = FALSE;
   char *process_name = "";
-  FILE *error = stderr;
-  char *newline = "\n";
 
   /* display warnings only when running in verbose mode */
   if (mode & ERR_WARN && !options.verbose)
     return;
 
-  if (mode == ERR_RETURN_LINE)
+  if (mode == ERR_INFO_LINE)
   {
     if (!last_line_was_separator)
-      fprintf_line(error, format, 79);
+      fprintf_line(program.error_file, format, 79);
 
     last_line_was_separator = TRUE;
 
@@ -816,16 +937,6 @@ void Error(int mode, char *format, ...)
 
   last_line_was_separator = FALSE;
 
-#if defined(PLATFORM_MSDOS)
-  newline = "\r\n";
-
-  if ((error = openErrorFile()) == NULL)
-  {
-    printf("Cannot write to error output file!%s", newline);
-    program.exit_function(1);
-  }
-#endif
-
   if (mode & ERR_SOUND_SERVER)
     process_name = " sound server";
   else if (mode & ERR_NETWORK_SERVER)
@@ -837,28 +948,25 @@ void Error(int mode, char *format, ...)
   {
     va_list ap;
 
-    fprintf(error, "%s%s: ", program.command_basename, process_name);
+    fprintf(program.error_file, "%s%s: ", program.command_basename,
+           process_name);
 
     if (mode & ERR_WARN)
-      fprintf(error, "warning: ");
+      fprintf(program.error_file, "warning: ");
 
     va_start(ap, format);
-    vfprintf(error, format, ap);
+    vfprintf_newline(program.error_file, format, ap);
     va_end(ap);
-  
-    fprintf(error, "%s", newline);
   }
   
   if (mode & ERR_HELP)
-    fprintf(error, "%s: Try option '--help' for more information.%s",
-           program.command_basename, newline);
+    fprintf_newline(program.error_file,
+                   "%s: Try option '--help' for more information.",
+                   program.command_basename);
 
   if (mode & ERR_EXIT)
-    fprintf(error, "%s%s: aborting%s",
-           program.command_basename, process_name, newline);
-
-  if (error != stderr)
-    fclose(error);
+    fprintf_newline(program.error_file, "%s%s: aborting",
+                   program.command_basename, process_name);
 
   if (mode & ERR_EXIT)
   {
@@ -914,6 +1022,19 @@ void checked_free(void *ptr)
     free(ptr);
 }
 
+void clear_mem(void *ptr, unsigned long size)
+{
+#if defined(PLATFORM_WIN32)
+  /* for unknown reason, memset() sometimes crashes when compiled with MinGW */
+  char *cptr = (char *)ptr;
+
+  while (size--)
+    *cptr++ = 0;
+#else
+  memset(ptr, 0, size);
+#endif
+}
+
 
 /* ------------------------------------------------------------------------- */
 /* various helper functions                                                  */
@@ -1041,17 +1162,27 @@ boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
   return (feof(file) || ferror(file) ? FALSE : TRUE);
 }
 
-void putFileChunk(FILE *file, char *chunk_name, int chunk_size,
-                 int byte_order)
+int putFileChunk(FILE *file, char *chunk_name, int chunk_size,
+                int byte_order)
 {
+  int num_bytes = 0;
+
   /* write chunk name */
-  fputs(chunk_name, file);
+  if (file != NULL)
+    fputs(chunk_name, file);
+
+  num_bytes += strlen(chunk_name);
 
   if (chunk_size >= 0)
   {
     /* write chunk size */
-    putFile32BitInteger(file, chunk_size, byte_order);
+    if (file != NULL)
+      putFile32BitInteger(file, chunk_size, byte_order);
+
+    num_bytes += 4;
   }
+
+  return num_bytes;
 }
 
 int getFileVersion(FILE *file)
@@ -1065,17 +1196,22 @@ int getFileVersion(FILE *file)
                       version_build);
 }
 
-void putFileVersion(FILE *file, int version)
+int putFileVersion(FILE *file, int version)
 {
-  int version_major = VERSION_MAJOR(version);
-  int version_minor = VERSION_MINOR(version);
-  int version_patch = VERSION_PATCH(version);
-  int version_build = VERSION_BUILD(version);
+  if (file != NULL)
+  {
+    int version_major = VERSION_MAJOR(version);
+    int version_minor = VERSION_MINOR(version);
+    int version_patch = VERSION_PATCH(version);
+    int version_build = VERSION_BUILD(version);
+
+    fputc(version_major, file);
+    fputc(version_minor, file);
+    fputc(version_patch, file);
+    fputc(version_build, file);
+  }
 
-  fputc(version_major, file);
-  fputc(version_minor, file);
-  fputc(version_patch, file);
-  fputc(version_build, file);
+  return 4;
 }
 
 void ReadBytesFromFile(FILE *file, byte *buffer, unsigned long bytes)
@@ -1213,6 +1349,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     { KSYM_asciitilde, "XK_asciitilde",        "~" },
 
     /* special (non-ASCII) keys */
+    { KSYM_degree,     "XK_degree",            "°" },
     { KSYM_Adiaeresis, "XK_Adiaeresis",        "Ä" },
     { KSYM_Odiaeresis, "XK_Odiaeresis",        "Ö" },
     { KSYM_Udiaeresis, "XK_Udiaeresis",        "Ãœ" },
@@ -1240,13 +1377,8 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
-#if 1
     else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
       sprintf(name_buffer, "F%d", (int)(key - KSYM_FKEY_FIRST + 1));
-#else
-    else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
-      sprintf(name_buffer, "function F%d", (int)(key - KSYM_FKEY_FIRST + 1));
-#endif
     else if (key == KSYM_UNDEFINED)
       strcpy(name_buffer, "(undefined)");
     else
@@ -1331,7 +1463,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     Key key = KSYM_UNDEFINED;
     char *name_ptr = *x11name;
 
-    if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
+    if (strPrefix(name_ptr, "XK_") && strlen(name_ptr) == 4)
     {
       char c = name_ptr[3];
 
@@ -1342,14 +1474,14 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       else if (c >= '0' && c <= '9')
        key = KSYM_0 + (Key)(c - '0');
     }
-    else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
+    else if (strPrefix(name_ptr, "XK_KP_") && strlen(name_ptr) == 7)
     {
       char c = name_ptr[6];
 
       if (c >= '0' && c <= '9')
        key = KSYM_KP_0 + (Key)(c - '0');
     }
-    else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
+    else if (strPrefix(name_ptr, "XK_F") && strlen(name_ptr) <= 6)
     {
       char c1 = name_ptr[4];
       char c2 = name_ptr[5];
@@ -1362,7 +1494,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       if (d >= 1 && d <= KSYM_NUM_FKEYS)
        key = KSYM_F1 + (Key)(d - 1);
     }
-    else if (strncmp(name_ptr, "XK_", 3) == 0)
+    else if (strPrefix(name_ptr, "XK_"))
     {
       i = 0;
 
@@ -1376,7 +1508,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       }
       while (translate_key[++i].x11name);
     }
-    else if (strncmp(name_ptr, "0x", 2) == 0)
+    else if (strPrefix(name_ptr, "0x"))
     {
       unsigned long value = 0;
 
@@ -1446,16 +1578,23 @@ Key getKeyFromX11KeyName(char *x11name)
 char getCharFromKey(Key key)
 {
   char *keyname = getKeyNameFromKey(key);
-  char letter = 0;
+  char c = 0;
 
   if (strlen(keyname) == 1)
-    letter = keyname[0];
+    c = keyname[0];
   else if (strEqual(keyname, "space"))
-    letter = ' ';
-  else if (strEqual(keyname, "circumflex"))
-    letter = '^';
+    c = ' ';
 
-  return letter;
+  return c;
+}
+
+char getValidConfigValueChar(char c)
+{
+  if (c == '#' ||      /* used to mark comments */
+      c == '\\')       /* used to mark continued lines */
+    c = 0;
+
+  return c;
 }
 
 
@@ -1495,9 +1634,13 @@ int get_integer_from_string(char *s)
 
   if (result == -1)
   {
-    if (strEqual(s_lower, "false"))
+    if (strEqual(s_lower, "false") ||
+       strEqual(s_lower, "no") ||
+       strEqual(s_lower, "off"))
       result = 0;
-    else if (strEqual(s_lower, "true"))
+    else if (strEqual(s_lower, "true") ||
+            strEqual(s_lower, "yes") ||
+            strEqual(s_lower, "on"))
       result = 1;
     else
       result = atoi(s);
@@ -1524,6 +1667,24 @@ boolean get_boolean_from_string(char *s)
   return result;
 }
 
+int get_switch3_from_string(char *s)
+{
+  char *s_lower = getStringToLower(s);
+  int result = FALSE;
+
+  if (strEqual(s_lower, "true") ||
+      strEqual(s_lower, "yes") ||
+      strEqual(s_lower, "on") ||
+      get_integer_from_string(s) == 1)
+    result = TRUE;
+  else if (strEqual(s_lower, "auto"))
+    result = AUTO;
+
+  free(s_lower);
+
+  return result;
+}
+
 
 /* ------------------------------------------------------------------------- */
 /* functions for generic lists                                               */
@@ -1552,7 +1713,7 @@ void deleteNodeFromList(ListNode **node_first, char *key,
 
   if (strEqual((*node_first)->key, key))
   {
-    free((*node_first)->key);
+    checked_free((*node_first)->key);
     if (destructor_function)
       destructor_function((*node_first)->content);
     *node_first = (*node_first)->next;
@@ -1769,6 +1930,20 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              strEqual(value, "up")    ? MV_UP :
              strEqual(value, "down")  ? MV_DOWN : MV_NONE);
   }
+  else if (strEqual(suffix, ".align"))
+  {
+    result = (strEqual(value, "left")   ? ALIGN_LEFT :
+             strEqual(value, "right")  ? ALIGN_RIGHT :
+             strEqual(value, "center") ? ALIGN_CENTER :
+             strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
+  }
+  else if (strEqual(suffix, ".valign"))
+  {
+    result = (strEqual(value, "top")    ? VALIGN_TOP :
+             strEqual(value, "bottom") ? VALIGN_BOTTOM :
+             strEqual(value, "middle") ? VALIGN_MIDDLE :
+             strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
+  }
   else if (strEqual(suffix, ".anim_mode"))
   {
     result = (string_has_parameter(value, "none")      ? ANIM_NONE :
@@ -1779,8 +1954,10 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              string_has_parameter(value, "random")     ? ANIM_RANDOM :
              string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
              string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
+             string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
              string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
              string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
+             string_has_parameter(value, "centered")   ? ANIM_CENTERED :
              ANIM_DEFAULT);
 
     if (string_has_parameter(value, "reverse"))
@@ -1792,6 +1969,36 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
     if (string_has_parameter(value, "static_panel"))
       result |= ANIM_STATIC_PANEL;
   }
+  else if (strEqual(suffix, ".class"))
+  {
+    result = get_hash_from_key(value);
+  }
+  else if (strEqual(suffix, ".style"))
+  {
+    result = STYLE_DEFAULT;
+
+    if (string_has_parameter(value, "accurate_borders"))
+      result |= STYLE_ACCURATE_BORDERS;
+
+    if (string_has_parameter(value, "with_inner_corners"))
+      result |= STYLE_WITH_INNER_CORNERS;
+  }
+  else if (strEqual(suffix, ".fade_mode"))
+  {
+    result = (string_has_parameter(value, "none")      ? FADE_MODE_NONE :
+             string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
+             string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
+             string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
+             FADE_MODE_DEFAULT);
+  }
+#if 1
+  else if (strPrefix(suffix, ".font"))         /* (may also be ".font_xyz") */
+#else
+  else if (strEqualN(suffix, ".font", 5))      /* (may also be ".font_xyz") */
+#endif
+  {
+    result = gfx.get_font_from_token_function(value);
+  }
   else         /* generic parameter of type integer or boolean */
   {
     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
@@ -1805,18 +2012,46 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
   return result;
 }
 
-int get_auto_parameter_value(char *token, char *value_raw)
+struct ScreenModeInfo *get_screen_mode_from_string(char *screen_mode_string)
+{
+  static struct ScreenModeInfo screen_mode;
+  char *screen_mode_string_x = strchr(screen_mode_string, 'x');
+  char *screen_mode_string_copy;
+  char *screen_mode_string_pos_w;
+  char *screen_mode_string_pos_h;
+
+  if (screen_mode_string_x == NULL)    /* invalid screen mode format */
+    return NULL;
+
+  screen_mode_string_copy = getStringCopy(screen_mode_string);
+
+  screen_mode_string_pos_w = screen_mode_string_copy;
+  screen_mode_string_pos_h = strchr(screen_mode_string_copy, 'x');
+  *screen_mode_string_pos_h++ = '\0';
+
+  screen_mode.width  = atoi(screen_mode_string_pos_w);
+  screen_mode.height = atoi(screen_mode_string_pos_h);
+
+  return &screen_mode;
+}
+
+void get_aspect_ratio_from_screen_mode(struct ScreenModeInfo *screen_mode,
+                                      int *x, int *y)
 {
-  char *suffix;
+  float aspect_ratio = (float)screen_mode->width / (float)screen_mode->height;
+  float aspect_ratio_new;
+  int i = 1;
 
-  if (token == NULL || value_raw == NULL)
-    return ARG_UNDEFINED_VALUE;
+  do
+  {
+    *x = i * aspect_ratio + 0.000001;
+    *y = i;
 
-  suffix = strrchr(token, '.');
-  if (suffix == NULL)
-    suffix = token;
+    aspect_ratio_new = (float)*x / (float)*y;
 
-  return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
+    i++;
+  }
+  while (aspect_ratio_new != aspect_ratio && *y < screen_mode->height);
 }
 
 static void FreeCustomArtworkList(struct ArtworkListInfo *,
@@ -1861,6 +2096,7 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list,
 
       file_list[i].redefined = FALSE;
       file_list[i].fallback_to_default = FALSE;
+      file_list[i].default_is_cloned = FALSE;
     }
   }
 
@@ -1905,28 +2141,39 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list,
          !strEqual(&config_list[i].value[len_config_value - 4], ".wav") &&
          !strEqual(config_list[i].value, UNDEFINED_FILENAME))
       {
-       Error(ERR_RETURN, "Configuration directive '%s' -> '%s':",
+       Error(ERR_INFO, "Configuration directive '%s' -> '%s':",
              config_list[i].token, config_list[i].value);
        Error(ERR_EXIT, "This seems to be no valid definition -- please fix");
       }
 
       file_list[list_pos].token = config_list[i].token;
       file_list[list_pos].default_filename = config_list[i].value;
+
+#if 0
+      printf("::: '%s' => '%s'\n", config_list[i].token, config_list[i].value);
+#endif
     }
+
+    if (strSuffix(config_list[i].token, ".clone_from"))
+      file_list[list_pos].default_is_cloned = TRUE;
   }
 
   num_file_list_entries_found = list_pos + 1;
   if (num_file_list_entries_found != num_file_list_entries)
   {
-    Error(ERR_RETURN_LINE, "-");
-    Error(ERR_RETURN, "inconsistant config list information:");
-    Error(ERR_RETURN, "- should be:   %d (according to 'src/conf_gfx.h')",
+    Error(ERR_INFO_LINE, "-");
+    Error(ERR_INFO, "inconsistant config list information:");
+    Error(ERR_INFO, "- should be:   %d (according to 'src/conf_xxx.h')",
          num_file_list_entries);
-    Error(ERR_RETURN, "- found to be: %d (according to 'src/conf_gfx.c')",
+    Error(ERR_INFO, "- found to be: %d (according to 'src/conf_xxx.c')",
          num_file_list_entries_found);
     Error(ERR_EXIT,   "please fix");
   }
 
+#if 0
+  printf("::: ---------- DONE ----------\n");
+#endif
+
   return file_list;
 }
 
@@ -2017,6 +2264,7 @@ static void add_dynamic_file_list_entry(struct FileInfo **list,
 
   new_list_entry->redefined = FALSE;
   new_list_entry->fallback_to_default = FALSE;
+  new_list_entry->default_is_cloned = FALSE;
 
   read_token_parameters(extra_file_hash, suffix_list, new_list_entry);
 }
@@ -2192,10 +2440,20 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
 
       base_index = i;
 
+#if 0
+      if (IS_PARENT_PROCESS())
+       printf("===> MATCH: '%s', '%s'\n", token, base_prefix);
+#endif
+
       if (start_pos + len_base_prefix == len_token)    /* exact match */
       {
        exact_match = TRUE;
 
+#if 0
+       if (IS_PARENT_PROCESS())
+         printf("===> EXACT MATCH: '%s', '%s'\n", token, base_prefix);
+#endif
+
        add_dynamic_file_list_entry(dynamic_file_list,
                                    num_dynamic_file_list_entries,
                                    extra_file_hash,
@@ -2229,10 +2487,20 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
 
        ext1_index = j;
 
+#if 0
+       if (IS_PARENT_PROCESS())
+         printf("===> MATCH: '%s', '%s'\n", token, ext1_suffix);
+#endif
+
        if (start_pos + len_ext1_suffix == len_token)   /* exact match */
        {
          exact_match = TRUE;
 
+#if 0
+       if (IS_PARENT_PROCESS())
+         printf("===> EXACT MATCH: '%s', '%s'\n", token, ext1_suffix);
+#endif
+
          add_dynamic_file_list_entry(dynamic_file_list,
                                      num_dynamic_file_list_entries,
                                      extra_file_hash,
@@ -2271,10 +2539,20 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
 
        ext2_index = k;
 
+#if 0
+       if (IS_PARENT_PROCESS())
+         printf("===> MATCH: '%s', '%s'\n", token, ext2_suffix);
+#endif
+
        if (start_pos + len_ext2_suffix == len_token)   /* exact match */
        {
          exact_match = TRUE;
 
+#if 0
+         if (IS_PARENT_PROCESS())
+           printf("===> EXACT MATCH: '%s', '%s'\n", token, ext2_suffix);
+#endif
+
          add_dynamic_file_list_entry(dynamic_file_list,
                                      num_dynamic_file_list_entries,
                                      extra_file_hash,
@@ -2313,10 +2591,20 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
 
        ext3_index = l;
 
+#if 0
+       if (IS_PARENT_PROCESS())
+         printf("===> MATCH: '%s', '%s'\n", token, ext3_suffix);
+#endif
+
        if (start_pos + len_ext3_suffix == len_token) /* exact match */
        {
          exact_match = TRUE;
 
+#if 0
+         if (IS_PARENT_PROCESS())
+           printf("===> EXACT MATCH: '%s', '%s'\n", token, ext3_suffix);
+#endif
+
          add_dynamic_file_list_entry(dynamic_file_list,
                                      num_dynamic_file_list_entries,
                                      extra_file_hash,
@@ -2362,53 +2650,53 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info,
 
     if (options.debug && dynamic_tokens_found)
     {
-      Error(ERR_RETURN_LINE, "-");
-      Error(ERR_RETURN, "dynamic token(s) found in config file:");
-      Error(ERR_RETURN, "- config file: '%s'", filename);
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_INFO, "dynamic token(s) found in config file:");
+      Error(ERR_INFO, "- config file: '%s'", filename);
 
       for (list = setup_file_list; list != NULL; list = list->next)
       {
        char *value = getHashEntry(extra_file_hash, list->token);
 
        if (value != NULL && strEqual(value, known_token_value))
-         Error(ERR_RETURN, "- dynamic token: '%s'", list->token);
+         Error(ERR_INFO, "- dynamic token: '%s'", list->token);
       }
 
-      Error(ERR_RETURN_LINE, "-");
+      Error(ERR_INFO_LINE, "-");
     }
 
     if (unknown_tokens_found)
     {
-      Error(ERR_RETURN_LINE, "-");
-      Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
-      Error(ERR_RETURN, "- config file: '%s'", filename);
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_INFO, "warning: unknown token(s) found in config file:");
+      Error(ERR_INFO, "- config file: '%s'", filename);
 
       for (list = setup_file_list; list != NULL; list = list->next)
       {
        char *value = getHashEntry(extra_file_hash, list->token);
 
        if (value != NULL && !strEqual(value, known_token_value))
-         Error(ERR_RETURN, "- dynamic token: '%s'", list->token);
+         Error(ERR_INFO, "- dynamic token: '%s'", list->token);
       }
 
-      Error(ERR_RETURN_LINE, "-");
+      Error(ERR_INFO_LINE, "-");
     }
 
     if (undefined_values_found)
     {
-      Error(ERR_RETURN_LINE, "-");
-      Error(ERR_RETURN, "warning: undefined values found in config file:");
-      Error(ERR_RETURN, "- config file: '%s'", filename);
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_INFO, "warning: undefined values found in config file:");
+      Error(ERR_INFO, "- config file: '%s'", filename);
 
       for (list = setup_file_list; list != NULL; list = list->next)
       {
        char *value = getHashEntry(empty_file_hash, list->token);
 
        if (value != NULL)
-         Error(ERR_RETURN, "- undefined value for token: '%s'", list->token);
+         Error(ERR_INFO, "- undefined value for token: '%s'", list->token);
       }
 
-      Error(ERR_RETURN_LINE, "-");
+      Error(ERR_INFO_LINE, "-");
     }
 
     freeSetupFileList(setup_file_list);
@@ -2437,7 +2725,7 @@ void LoadArtworkConfig(struct ArtworkListInfo *artwork_info)
   char *filename_base = UNDEFINED_FILENAME, *filename_local;
   int i, j;
 
-  DrawInitText("Loading artwork config:", 120, FC_GREEN);
+  DrawInitText("Loading artwork config", 120, FC_GREEN);
   DrawInitText(ARTWORKINFO_FILENAME(artwork_info->type), 150, FC_YELLOW);
 
   /* always start with reliable default values */
@@ -2478,11 +2766,21 @@ void LoadArtworkConfig(struct ArtworkListInfo *artwork_info)
     artwork_info->num_property_mapping_entries = 0;
   }
 
+#if 1
+  if (!GFX_OVERRIDE_ARTWORK(artwork_info->type))
+#else
   if (!SETUP_OVERRIDE_ARTWORK(setup, artwork_info->type))
+#endif
   {
     /* first look for special artwork configured in level series config */
     filename_base = getCustomArtworkLevelConfigFilename(artwork_info->type);
 
+#if 0
+    printf("::: filename_base == '%s' [%s, %s]\n", filename_base,
+          leveldir_current->graphics_set,
+          leveldir_current->graphics_path);
+#endif
+
     if (fileExists(filename_base))
       LoadArtworkConfigFromFilename(artwork_info, filename_base);
   }
@@ -2514,9 +2812,9 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
 {
   char *init_text[] =
   {
-    "Loading graphics:",
-    "Loading sounds:",
-    "Loading music:"
+    "Loading graphics",
+    "Loading sounds",
+    "Loading music"
   };
 
   ListNode *node;
@@ -2529,6 +2827,22 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
 
     basename = file_list_entry->default_filename;
 
+    /* fail for cloned default artwork that has no default filename defined */
+    if (file_list_entry->default_is_cloned &&
+       strEqual(basename, UNDEFINED_FILENAME))
+    {
+      int error_mode = ERR_WARN;
+
+      /* we can get away without sounds and music, but not without graphics */
+      if (*listnode == NULL && artwork_info->type == ARTWORK_TYPE_GRAPHICS)
+       error_mode = ERR_EXIT;
+
+      Error(error_mode, "token '%s' was cloned and has no default filename",
+           file_list_entry->token);
+
+      return;
+    }
+
     /* dynamic artwork has no default filename / skip empty default artwork */
     if (basename == NULL || strEqual(basename, UNDEFINED_FILENAME))
       return;
@@ -2605,6 +2919,7 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
       error_mode = ERR_EXIT;
 
     Error(error_mode, "cannot load artwork file '%s'", basename);
+
     return;
   }
 }
@@ -2614,7 +2929,7 @@ static void LoadCustomArtwork(struct ArtworkListInfo *artwork_info,
                              struct FileInfo *file_list_entry)
 {
 #if 0
-  printf("GOT CUSTOM ARTWORK FILE '%s'\n", filename);
+  printf("GOT CUSTOM ARTWORK FILE '%s'\n", file_list_entry->filename);
 #endif
 
   if (strEqual(file_list_entry->filename, UNDEFINED_FILENAME))
@@ -2681,25 +2996,32 @@ void FreeCustomArtworkLists(struct ArtworkListInfo *artwork_info)
 /* ------------------------------------------------------------------------- */
 /* functions only needed for non-Unix (non-command-line) systems             */
 /* (MS-DOS only; SDL/Windows creates files "stdout.txt" and "stderr.txt")    */
+/* (now also added for Windows, to create files in user data directory)      */
 /* ------------------------------------------------------------------------- */
 
-#if defined(PLATFORM_MSDOS)
-
-#define ERROR_FILENAME         "stderr.txt"
+char *getErrorFilename(char *basename)
+{
+  return getPath2(getUserGameDataDir(), basename);
+}
 
-void initErrorFile()
+void openErrorFile()
 {
-  unlink(ERROR_FILENAME);
+  InitUserDataDirectory();
+
+  if ((program.error_file = fopen(program.error_filename, MODE_WRITE)) == NULL)
+    fprintf_newline(stderr, "ERROR: cannot open file '%s' for writing!",
+                   program.error_filename);
 }
 
-FILE *openErrorFile()
+void closeErrorFile()
 {
-  return fopen(ERROR_FILENAME, MODE_APPEND);
+  if (program.error_file != stderr)    /* do not close stream 'stderr' */
+    fclose(program.error_file);
 }
 
 void dumpErrorFile()
 {
-  FILE *error_file = fopen(ERROR_FILENAME, MODE_READ);
+  FILE *error_file = fopen(program.error_filename, MODE_READ);
 
   if (error_file != NULL)
   {
@@ -2709,29 +3031,97 @@ void dumpErrorFile()
     fclose(error_file);
   }
 }
+
+void NotifyUserAboutErrorFile()
+{
+#if defined(PLATFORM_WIN32)
+  char *title_text = getStringCat2(program.program_title, " Error Message");
+  char *error_text = getStringCat2("The program was aborted due to an error; "
+                                  "for details, see the following error file:"
+                                  STRING_NEWLINE, program.error_filename);
+
+  MessageBox(NULL, error_text, title_text, MB_OK);
 #endif
+}
 
 
 /* ------------------------------------------------------------------------- */
 /* the following is only for debugging purpose and normally not used         */
 /* ------------------------------------------------------------------------- */
 
-#define DEBUG_NUM_TIMESTAMPS   3
+#if DEBUG
+
+#define DEBUG_NUM_TIMESTAMPS           5
+#define DEBUG_TIME_IN_MICROSECONDS     0
+
+#if DEBUG_TIME_IN_MICROSECONDS
+static double Counter_Microseconds()
+{
+  static struct timeval base_time = { 0, 0 };
+  struct timeval current_time;
+  double counter;
+
+  gettimeofday(&current_time, NULL);
+
+  /* reset base time in case of wrap-around */
+  if (current_time.tv_sec < base_time.tv_sec)
+    base_time = current_time;
+
+  counter =
+    ((double)(current_time.tv_sec  - base_time.tv_sec)) * 1000000 +
+    ((double)(current_time.tv_usec - base_time.tv_usec));
+
+  return counter;              /* return microseconds since last init */
+}
+#endif
+
+char *debug_print_timestamp_get_padding(int padding_size)
+{
+  static char *padding = NULL;
+  int max_padding_size = 100;
+
+  if (padding == NULL)
+  {
+    padding = checked_calloc(max_padding_size + 1);
+    memset(padding, ' ', max_padding_size);
+  }
+
+  return &padding[MAX(0, max_padding_size - padding_size)];
+}
 
 void debug_print_timestamp(int counter_nr, char *message)
 {
-  static long counter[DEBUG_NUM_TIMESTAMPS][2];
+  int indent_size = 8;
+  int padding_size = 40;
+  float timestamp_interval;
 
-  if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
+  if (counter_nr < 0)
+    Error(ERR_EXIT, "debugging: invalid negative counter");
+  else if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
     Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
 
+#if DEBUG_TIME_IN_MICROSECONDS
+  static double counter[DEBUG_NUM_TIMESTAMPS][2];
+  char *unit = "ms";
+
+  counter[counter_nr][0] = Counter_Microseconds();
+#else
+  static long counter[DEBUG_NUM_TIMESTAMPS][2];
+  char *unit = "s";
+
   counter[counter_nr][0] = Counter();
+#endif
 
-  if (message)
-    printf("%s %.2f seconds\n", message,
-          (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
+  timestamp_interval = counter[counter_nr][0] - counter[counter_nr][1];
+  counter[counter_nr][1] = counter[counter_nr][0];
 
-  counter[counter_nr][1] = Counter();
+  if (message)
+    printf("%s%s%s %.3f %s\n",
+          debug_print_timestamp_get_padding(counter_nr * indent_size),
+          message,
+          debug_print_timestamp_get_padding(padding_size - strlen(message)),
+          timestamp_interval / 1000,
+          unit);
 }
 
 void debug_print_parent_only(char *format, ...)
@@ -2750,3 +3140,4 @@ void debug_print_parent_only(char *format, ...)
     printf("\n");
   }
 }
+#endif