added function to check if string is URL (not used yet)
[rocksndiamonds.git] / src / libgame / misc.c
index a81f15532538efaa163fb591c1261108fc7dd53c..54e5bfd16b4822d7f682f3755aa6aac8e4952a66 100644 (file)
@@ -549,6 +549,50 @@ boolean getTokenValueFromString(char *string, char **token, char **value)
 }
 
 
+// ----------------------------------------------------------------------------
+// UUID functions
+// ----------------------------------------------------------------------------
+
+#define UUID_BYTES             16
+#define UUID_CHARS             (UUID_BYTES * 2)
+#define UUID_LENGTH            (UUID_CHARS + 4)
+
+static unsigned int uuid_random_function(int max)
+{
+  return GetBetterRandom(max);
+}
+
+char *getUUIDExt(unsigned int (*random_function)(int max))
+{
+  static char uuid[UUID_LENGTH + 1];
+  int data[UUID_BYTES];
+  int count = 0;
+  int i;
+
+  for (i = 0; i < UUID_BYTES; i++)
+    data[i] = random_function(256);
+
+  data[6] = 0x40 | (data[6] & 0x0f);
+  data[8] = 0x80 | (data[8] & 0x3f);
+
+  for (i = 0; i < UUID_BYTES; i++)
+  {
+    sprintf(&uuid[count], "%02x", data[i]);
+    count += 2;
+
+    if (i == 3 || i == 5 || i == 7 || i == 9)
+      strcat(&uuid[count++], "-");
+  }
+
+  return uuid;
+}
+
+char *getUUID(void)
+{
+  return getUUIDExt(uuid_random_function);
+}
+
+
 // ----------------------------------------------------------------------------
 // counter functions
 // ----------------------------------------------------------------------------
@@ -712,7 +756,7 @@ void SkipUntilDelayReached(unsigned int *counter_var, unsigned int delay,
 // random generator functions
 // ----------------------------------------------------------------------------
 
-unsigned int init_random_number(int nr, int seed)
+static unsigned int init_random_number_ext(int nr, int seed)
 {
   if (seed == NEW_RANDOMIZE)
   {
@@ -740,9 +784,32 @@ unsigned int init_random_number(int nr, int seed)
   return (unsigned int) seed;
 }
 
+static unsigned int prng_seed_gettimeofday(void)
+{
+  struct timeval current_time;
+
+  gettimeofday(&current_time, NULL);
+
+  prng_seed_bytes(&current_time, sizeof(current_time));
+
+  return 0;
+}
+
+unsigned int init_random_number(int nr, int seed)
+{
+  return (nr == RANDOM_BETTER ? prng_seed_gettimeofday() :
+         init_random_number_ext(nr, seed));
+}
+
+static unsigned int get_random_number_ext(int nr)
+{
+  return (nr == RANDOM_BETTER ? prng_get_uint() :
+         random_linux_libc(nr));
+}
+
 unsigned int get_random_number(int nr, int max)
 {
-  return (max > 0 ? random_linux_libc(nr) % max : 0);
+  return (max > 0 ? get_random_number_ext(nr) % max : 0);
 }
 
 
@@ -1231,6 +1298,14 @@ boolean strSuffixLower(char *s, char *suffix)
   return match;
 }
 
+boolean isURL(char *s)
+{
+  while (*s && *s >= 'a' && *s <= 'z')
+    s++;
+
+  return strPrefix(s, "://");
+}
+
 
 // ----------------------------------------------------------------------------
 // command line option handling functions
@@ -1240,7 +1315,7 @@ void GetOptions(int argc, char *argv[],
                void (*print_usage_function)(void),
                void (*print_version_function)(void))
 {
-  char *ro_base_path = getProgramMainDataPath(argv[0], RO_BASE_PATH);
+  char *base_path = getProgramMainDataPath(argv[0], BASE_PATH);
   char **argvplus = checked_calloc((argc + 1) * sizeof(char **));
   char **options_left = &argvplus[1];
 
@@ -1252,17 +1327,22 @@ void GetOptions(int argc, char *argv[],
   options.server_host = NULL;
   options.server_port = 0;
 
-  options.ro_base_directory = ro_base_path;
-  options.level_directory    = getPath2(ro_base_path, LEVELS_DIRECTORY);
-  options.graphics_directory = getPath2(ro_base_path, GRAPHICS_DIRECTORY);
-  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.conf_directory     = getPath2(ro_base_path, CONF_DIRECTORY);
+  options.base_directory = base_path;
+
+  options.level_directory    = getPath2(base_path, LEVELS_DIRECTORY);
+  options.graphics_directory = getPath2(base_path, GRAPHICS_DIRECTORY);
+  options.sounds_directory   = getPath2(base_path, SOUNDS_DIRECTORY);
+  options.music_directory    = getPath2(base_path, MUSIC_DIRECTORY);
+  options.docs_directory     = getPath2(base_path, DOCS_DIRECTORY);
+  options.conf_directory     = getPath2(base_path, CONF_DIRECTORY);
 
   options.execute_command = NULL;
+  options.tape_log_filename = NULL;
   options.special_flags = NULL;
   options.debug_mode = NULL;
+  options.player_name = NULL;
+  options.identifier = NULL;
+  options.level_nr = NULL;
 
   options.mytapes = FALSE;
   options.serveronly = FALSE;
@@ -1332,17 +1412,17 @@ void GetOptions(int argc, char *argv[],
       if (option_arg == NULL)
        FailWithHelp("option '%s' requires an argument", option_str);
 
-      options.ro_base_directory = ro_base_path = getStringCopy(option_arg);
+      options.base_directory = base_path = getStringCopy(option_arg);
       if (option_arg == next_option)
        options_left++;
 
       // adjust paths for sub-directories in base directory accordingly
-      options.level_directory    = getPath2(ro_base_path, LEVELS_DIRECTORY);
-      options.graphics_directory = getPath2(ro_base_path, GRAPHICS_DIRECTORY);
-      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.conf_directory     = getPath2(ro_base_path, CONF_DIRECTORY);
+      options.level_directory    = getPath2(base_path, LEVELS_DIRECTORY);
+      options.graphics_directory = getPath2(base_path, GRAPHICS_DIRECTORY);
+      options.sounds_directory   = getPath2(base_path, SOUNDS_DIRECTORY);
+      options.music_directory    = getPath2(base_path, MUSIC_DIRECTORY);
+      options.docs_directory     = getPath2(base_path, DOCS_DIRECTORY);
+      options.conf_directory     = getPath2(base_path, CONF_DIRECTORY);
     }
     else if (strncmp(option, "-levels", option_len) == 0)
     {
@@ -1400,6 +1480,33 @@ void GetOptions(int argc, char *argv[],
       if (option_arg != next_option)
        options.debug_mode = getStringCopy(option_arg);
     }
+    else if (strncmp(option, "-player-name", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       FailWithHelp("option '%s' requires an argument", option_str);
+
+      options.player_name = getStringCopy(option_arg);
+      if (option_arg == next_option)
+       options_left++;
+    }
+    else if (strncmp(option, "-identifier", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       FailWithHelp("option '%s' requires an argument", option_str);
+
+      options.identifier = getStringCopy(option_arg);
+      if (option_arg == next_option)
+       options_left++;
+    }
+    else if (strncmp(option, "-level-nr", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       FailWithHelp("option '%s' requires an argument", option_str);
+
+      options.level_nr = getStringCopy(option_arg);
+      if (option_arg == next_option)
+       options_left++;
+    }
     else if (strncmp(option, "-verbose", option_len) == 0)
     {
       options.verbose = TRUE;
@@ -1427,6 +1534,15 @@ void GetOptions(int argc, char *argv[],
       // when doing batch processing, always enable verbose mode (warnings)
       options.verbose = TRUE;
     }
+    else if (strncmp(option, "-tape_logfile", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       FailWithHelp("option '%s' requires an argument", option_str);
+
+      options.tape_log_filename = getStringCopy(option_arg);
+      if (option_arg == next_option)
+       options_left++;
+    }
 #if defined(PLATFORM_MACOSX)
     else if (strPrefix(option, "-psn"))
     {
@@ -1838,6 +1954,107 @@ char *getLatin1FromUTF8(char *utf8)
   return latin1;
 }
 
+int getTextEncoding(char *text)
+{
+  unsigned char *src = (unsigned char *)text;
+  int encoding = TEXT_ENCODING_ASCII;  // default: assume encoding is ASCII
+
+  while (*src)
+  {
+    if (*src >= 128)
+      encoding = TEXT_ENCODING_UTF_8;  // non-ASCII character: assume UTF-8
+
+    if (*src < 128)
+    {
+      src++;
+    }
+    else if (src[0] >= 192 && src[0] < 224 &&
+            src[1] >= 128 && src[1] < 192)
+    {
+      src += 2;
+    }
+    else if (src[0] >= 224 && src[0] < 240 &&
+            src[1] >= 128 && src[1] < 192 &&
+            src[2] >= 128 && src[2] < 192)
+    {
+      src += 3;
+    }
+    else if (src[0] >= 240 && src[0] < 248 &&
+            src[1] >= 128 && src[1] < 192 &&
+            src[2] >= 128 && src[2] < 192 &&
+            src[3] >= 128 && src[3] < 192)
+    {
+      src += 4;
+    }
+    else if (src[0] >= 248 && src[0] < 252 &&
+            src[1] >= 128 && src[1] < 192 &&
+            src[2] >= 128 && src[2] < 192 &&
+            src[3] >= 128 && src[3] < 192 &&
+            src[4] >= 128 && src[4] < 192)
+    {
+      src += 5;
+    }
+    else if (src[0] >= 252 && src[0] < 254 &&
+            src[1] >= 128 && src[1] < 192 &&
+            src[2] >= 128 && src[2] < 192 &&
+            src[3] >= 128 && src[3] < 192 &&
+            src[4] >= 128 && src[4] < 192 &&
+            src[5] >= 128 && src[5] < 192)
+    {
+      src += 6;
+    }
+    else
+    {
+      return TEXT_ENCODING_UNKNOWN;    // non-UTF-8 character: unknown encoding
+    }
+  }
+
+  return encoding;
+}
+
+
+// ----------------------------------------------------------------------------
+// functions for JSON handling
+// ----------------------------------------------------------------------------
+
+char *getEscapedJSON(char *s)
+{
+  int max_json_size = 2 * strlen(s) + 1;
+  char *json = checked_calloc(max_json_size);
+  unsigned char *src = (unsigned char *)s;
+  unsigned char *dst = (unsigned char *)json;
+  char *escaped[256] =
+  {
+    ['\b'] = "\\b",
+    ['\f'] = "\\f",
+    ['\n'] = "\\n",
+    ['\r'] = "\\r",
+    ['\t'] = "\\t",
+    ['\"'] = "\\\"",
+    ['\\'] = "\\\\",
+  };
+
+  while (*src)
+  {
+    if (escaped[*src] != NULL)
+    {
+      char *esc = escaped[*src++];
+
+      while (*esc)
+       *dst++ = *esc++;
+    }
+    else
+    {
+      *dst++ = *src++;
+    }
+  }
+
+  // only use the smallest possible string buffer size
+  json = checked_realloc(json, strlen(json) + 1);
+
+  return json;
+}
+
 
 // ----------------------------------------------------------------------------
 // functions to translate key identifiers between different format
@@ -2276,47 +2493,23 @@ char getValidConfigValueChar(char c)
 
 int get_integer_from_string(char *s)
 {
-  static char *number_text[][3] =
-  {
-    { "0",     "zero",         "null",         },
-    { "1",     "one",          "first"         },
-    { "2",     "two",          "second"        },
-    { "3",     "three",        "third"         },
-    { "4",     "four",         "fourth"        },
-    { "5",     "five",         "fifth"         },
-    { "6",     "six",          "sixth"         },
-    { "7",     "seven",        "seventh"       },
-    { "8",     "eight",        "eighth"        },
-    { "9",     "nine",         "ninth"         },
-    { "10",    "ten",          "tenth"         },
-    { "11",    "eleven",       "eleventh"      },
-    { "12",    "twelve",       "twelfth"       },
-
-    { NULL,    NULL,           NULL            },
-  };
+  // check for the most common case first
+  if (s[0] >= '0' && s[0] <= '9')
+    return atoi(s);
 
-  int i, j;
   char *s_lower = getStringToLower(s);
   int result = -1;
 
-  for (i = 0; number_text[i][0] != NULL; i++)
-    for (j = 0; j < 3; j++)
-      if (strEqual(s_lower, number_text[i][j]))
-       result = i;
-
-  if (result == -1)
-  {
-    if (strEqual(s_lower, "false") ||
-       strEqual(s_lower, "no") ||
-       strEqual(s_lower, "off"))
-      result = 0;
-    else if (strEqual(s_lower, "true") ||
-            strEqual(s_lower, "yes") ||
-            strEqual(s_lower, "on"))
-      result = 1;
-    else
-      result = atoi(s);
-  }
+  if (strEqual(s_lower, "false") ||
+      strEqual(s_lower, "no") ||
+      strEqual(s_lower, "off"))
+    result = 0;
+  else if (strEqual(s_lower, "true") ||
+          strEqual(s_lower, "yes") ||
+          strEqual(s_lower, "on"))
+    result = 1;
+  else
+    result = atoi(s);
 
   free(s_lower);
 
@@ -2668,6 +2861,22 @@ int copyFile(char *filename_from, char *filename_to)
   return 0;
 }
 
+boolean touchFile(char *filename)
+{
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Warn("cannot touch file '%s'", filename);
+
+    return FALSE;
+  }
+
+  fclose(file);
+
+  return TRUE;
+}
+
 
 // ----------------------------------------------------------------------------
 // functions for directory handling
@@ -3682,8 +3891,8 @@ void LoadArtworkConfig(struct ArtworkListInfo *artwork_info)
   char *filename_base = UNDEFINED_FILENAME, *filename_local;
   int i, j;
 
-  DrawInitText("Loading artwork config", 120, FC_GREEN);
-  DrawInitText(ARTWORKINFO_FILENAME(artwork_info->type), 150, FC_YELLOW);
+  DrawInitTextHead("Loading artwork config");
+  DrawInitTextItem(ARTWORKINFO_FILENAME(artwork_info->type));
 
   // always start with reliable default values
   for (i = 0; i < num_file_list_entries; i++)
@@ -3768,6 +3977,11 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
   char *basename = file_list_entry->filename;
   char *filename = getCustomArtworkFilename(basename, artwork_info->type);
 
+  // mark all images from non-default graphics directory as "redefined"
+  if (artwork_info->type == ARTWORK_TYPE_GRAPHICS &&
+      !strPrefix(filename, options.graphics_directory))
+    file_list_entry->redefined = TRUE;
+
   if (filename == NULL)
   {
     Warn("cannot find artwork file '%s'", basename);
@@ -3836,8 +4050,8 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info,
       return;
   }
 
-  DrawInitText(init_text[artwork_info->type], 120, FC_GREEN);
-  DrawInitText(basename, 150, FC_YELLOW);
+  DrawInitTextHead(init_text[artwork_info->type]);
+  DrawInitTextItem(basename);
 
   if ((*listnode = artwork_info->load_artwork(filename)) != NULL)
   {