added dirty workaround to open program window on specified display
[rocksndiamonds.git] / src / libgame / misc.c
index 8bc457216de7448418ad5cebd885728e562b5b60..b1f4b66c755555bca3a567e915171333a1fa3950 100644 (file)
@@ -557,7 +557,12 @@ boolean getTokenValueFromString(char *string, char **token, char **value)
 #define UUID_CHARS             (UUID_BYTES * 2)
 #define UUID_LENGTH            (UUID_CHARS + 4)
 
-char *getUUID(void)
+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];
@@ -565,7 +570,7 @@ char *getUUID(void)
   int i;
 
   for (i = 0; i < UUID_BYTES; i++)
-    data[i] = GetSimpleRandom(256);
+    data[i] = random_function(256);
 
   data[6] = 0x40 | (data[6] & 0x0f);
   data[8] = 0x80 | (data[8] & 0x3f);
@@ -582,6 +587,11 @@ char *getUUID(void)
   return uuid;
 }
 
+char *getUUID(void)
+{
+  return getUUIDExt(uuid_random_function);
+}
+
 
 // ----------------------------------------------------------------------------
 // counter functions
@@ -631,8 +641,8 @@ void Delay(unsigned int delay)      // Sleep specified number of milliseconds
   sleep_milliseconds(delay);
 }
 
-boolean DelayReachedExt(unsigned int *counter_var, unsigned int delay,
-                       unsigned int actual_counter)
+boolean DelayReachedExt2(unsigned int *counter_var, unsigned int delay,
+                        unsigned int actual_counter)
 {
   if (actual_counter >= *counter_var &&
       actual_counter < *counter_var + delay)
@@ -643,34 +653,40 @@ boolean DelayReachedExt(unsigned int *counter_var, unsigned int delay,
   return TRUE;
 }
 
-boolean FrameReached(unsigned int *frame_counter_var, unsigned int frame_delay)
+boolean DelayReachedExt(DelayCounter *counter, unsigned int actual_counter)
 {
-  return DelayReachedExt(frame_counter_var, frame_delay, FrameCounter);
+  return DelayReachedExt2(&counter->count, counter->value, actual_counter);
 }
 
-boolean DelayReached(unsigned int *counter_var, unsigned int delay)
+boolean FrameReached(DelayCounter *counter)
 {
-  return DelayReachedExt(counter_var, delay, Counter());
+  return DelayReachedExt(counter, FrameCounter);
 }
 
-void ResetDelayCounterExt(unsigned int *counter_var,
-                         unsigned int actual_counter)
+boolean DelayReached(DelayCounter *counter)
 {
-  DelayReachedExt(counter_var, 0, actual_counter);
+  return DelayReachedExt(counter, Counter());
 }
 
-void ResetFrameCounter(unsigned int *frame_counter_var)
+void ResetDelayCounterExt(DelayCounter *counter, unsigned int actual_counter)
 {
-  FrameReached(frame_counter_var, 0);
+  DelayReachedExt2(&counter->count, 0, actual_counter);
 }
 
-void ResetDelayCounter(unsigned int *counter_var)
+void ResetFrameCounter(DelayCounter *counter)
 {
-  DelayReached(counter_var, 0);
+  ResetDelayCounterExt(counter, FrameCounter);
 }
 
-int WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay)
+void ResetDelayCounter(DelayCounter *counter)
 {
+  ResetDelayCounterExt(counter, Counter());
+}
+
+int WaitUntilDelayReached(DelayCounter *counter)
+{
+  unsigned int *counter_var = &counter->count;
+  unsigned int delay = counter->value;
   unsigned int actual_counter;
   int skip_frames = 0;
 
@@ -701,22 +717,22 @@ int WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay)
   return skip_frames;
 }
 
-void SkipUntilDelayReached(unsigned int *counter_var, unsigned int delay,
+void SkipUntilDelayReached(DelayCounter *counter,
                           int *loop_var, int last_loop_value)
 {
-  int skip_frames = WaitUntilDelayReached(counter_var, delay);
+  int skip_frames = WaitUntilDelayReached(counter);
 
 #if 0
 #if DEBUG
   if (skip_frames)
     Debug("internal:SkipUntilDelayReached",
          "%d: %d ms -> SKIP %d FRAME(S) [%d ms]",
-         *loop_var, delay,
-         skip_frames, skip_frames * delay);
+         *loop_var, counter->value,
+         skip_frames, skip_frames * counter->value);
   else
     Debug("internal:SkipUntilDelayReached",
          "%d: %d ms",
-         *loop_var, delay);
+         *loop_var, counter->value);
 #endif
 #endif
 
@@ -746,14 +762,14 @@ 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)
   {
     // default random seed
     seed = (int)time(NULL);                    // seconds since the epoch
 
-#if !defined(PLATFORM_WIN32)
+#if !defined(PLATFORM_WINDOWS)
     // add some more randomness
     struct timeval current_time;
 
@@ -774,9 +790,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);
 }
 
 
@@ -863,7 +902,7 @@ char *getLoginName(void)
 {
   static char *login_name = NULL;
 
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
   if (login_name == NULL)
   {
     unsigned long buffer_size = MAX_USERNAME_LEN + 1;
@@ -894,7 +933,7 @@ char *getRealName(void)
 {
   static char *real_name = NULL;
 
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
   if (real_name == NULL)
   {
     static char buffer[MAX_USERNAME_LEN + 1];
@@ -1265,6 +1304,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
@@ -1303,6 +1350,8 @@ void GetOptions(int argc, char *argv[],
   options.identifier = NULL;
   options.level_nr = NULL;
 
+  options.display_nr = 0;
+
   options.mytapes = FALSE;
   options.serveronly = FALSE;
   options.network = FALSE;
@@ -1502,7 +1551,29 @@ void GetOptions(int argc, char *argv[],
       if (option_arg == next_option)
        options_left++;
     }
-#if defined(PLATFORM_MACOSX)
+    else if (strncmp(option, "-display", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       FailWithHelp("option '%s' requires an argument", option_str);
+
+      if (option_arg == next_option)
+       options_left++;
+
+      int display_nr = atoi(option_arg);
+
+#if 1
+      // dirty hack: SDL_GetNumVideoDisplays() seems broken on some systems
+      options.display_nr = display_nr;
+#else
+      options.display_nr =
+       MAX(0, MIN(display_nr, SDL_GetNumVideoDisplays() - 1));
+
+      if (display_nr != options.display_nr)
+       Warn("invalid display %d -- using display %d",
+            display_nr, options.display_nr);
+#endif
+    }
+#if defined(PLATFORM_MAC)
     else if (strPrefix(option, "-psn"))
     {
       // ignore process serial number when launched via GUI on Mac OS X
@@ -1576,7 +1647,7 @@ void checked_free(void *ptr)
 
 void clear_mem(void *ptr, unsigned int size)
 {
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
   // for unknown reason, memset() sometimes crashes when compiled with MinGW
   char *cptr = (char *)ptr;
 
@@ -1913,6 +1984,64 @@ 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
@@ -2394,47 +2523,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);
 
@@ -2786,6 +2891,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
@@ -4110,7 +4231,7 @@ void DumpLogFile(int nr)
 
 void NotifyUserAboutErrorFile(void)
 {
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
   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:"