+ prng_seed_bytes(¤t_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 ? get_random_number_ext(nr) % max : 0);
+}
+
+
+// ----------------------------------------------------------------------------
+// system info functions
+// ----------------------------------------------------------------------------
+
+#if !defined(PLATFORM_ANDROID)
+static char *get_corrected_real_name(char *real_name)
+{
+ char *real_name_new = checked_malloc(MAX_USERNAME_LEN + 1);
+ char *from_ptr = real_name;
+ char *to_ptr = real_name_new;
+
+ // copy the name string, but not more than MAX_USERNAME_LEN characters
+ while (*from_ptr && (int)(to_ptr - real_name_new) < MAX_USERNAME_LEN - 1)
+ {
+ // the name field read from "passwd" file may also contain additional
+ // user information, separated by commas, which will be removed here
+ if (*from_ptr == ',')
+ break;
+
+ // the user's real name may contain 'german sharp s' characters,
+ // which have no equivalent in upper case letters (used by our fonts)
+ if (*from_ptr == CHAR_BYTE_SHARP_S)
+ {
+ from_ptr++;
+ *to_ptr++ = 's';
+ *to_ptr++ = 's';
+ }
+ else
+ *to_ptr++ = *from_ptr++;
+ }
+
+ *to_ptr = '\0';
+
+ return real_name_new;
+}
+#endif
+
+#if defined(PLATFORM_UNIX)
+static struct passwd *getPasswdEntry(void)
+{
+#if defined(PLATFORM_EMSCRIPTEN)
+ // currently not fully supported; force fallback to default values
+ return NULL;
+#else
+ return getpwuid(getuid());
+#endif
+}
+
+char *getUnixLoginName(void)
+{
+ struct passwd *pwd = getPasswdEntry();
+
+ if (pwd != NULL && strlen(pwd->pw_name) != 0)
+ return pwd->pw_name;
+
+ return NULL;
+}
+
+char *getUnixRealName(void)
+{
+ struct passwd *pwd = getPasswdEntry();
+
+ if (pwd != NULL && strlen(pwd->pw_gecos) != 0)
+ return pwd->pw_gecos;
+
+ return NULL;
+}
+
+char *getUnixHomeDir(void)
+{
+ struct passwd *pwd = getPasswdEntry();
+
+ if (pwd != NULL && strlen(pwd->pw_dir) != 0)
+ return pwd->pw_dir;
+
+ return NULL;
+}
+#endif
+
+char *getLoginName(void)
+{
+ static char *login_name = NULL;
+
+#if defined(PLATFORM_WINDOWS)
+ if (login_name == NULL)
+ {
+ unsigned long buffer_size = MAX_USERNAME_LEN + 1;
+
+ login_name = checked_malloc(buffer_size);
+
+ if (GetUserName(login_name, &buffer_size) == 0)
+ strcpy(login_name, ANONYMOUS_NAME);
+ }
+#elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID)
+ if (login_name == NULL)
+ {
+ char *name = getUnixLoginName();
+
+ if (name != NULL)
+ login_name = getStringCopy(name);
+ else
+ login_name = ANONYMOUS_NAME;
+ }
+#else
+ login_name = ANONYMOUS_NAME;
+#endif
+
+ return login_name;
+}
+
+char *getRealName(void)
+{
+ static char *real_name = NULL;
+
+#if defined(PLATFORM_WINDOWS)
+ if (real_name == NULL)
+ {
+ static char buffer[MAX_USERNAME_LEN + 1];
+ unsigned long buffer_size = MAX_USERNAME_LEN + 1;
+
+ if (GetUserName(buffer, &buffer_size) != 0)
+ real_name = get_corrected_real_name(buffer);
+ else
+ real_name = ANONYMOUS_NAME;
+ }
+#elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID)
+ if (real_name == NULL)
+ {
+ char *name = getUnixRealName();
+
+ if (name != NULL)
+ real_name = get_corrected_real_name(name);
+ else
+ real_name = ANONYMOUS_NAME;
+ }
+#else
+ real_name = ANONYMOUS_NAME;
+#endif
+
+ return real_name;
+}
+
+char *getFixedUserName(char *name)
+{
+ // needed because player name must be a fixed length string
+ char *name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
+
+ strncpy(name_new, name, MAX_PLAYER_NAME_LEN);
+ name_new[MAX_PLAYER_NAME_LEN] = '\0';
+
+ if (strlen(name) > MAX_PLAYER_NAME_LEN) // name has been cut
+ if (strchr(name_new, ' '))
+ *strchr(name_new, ' ') = '\0';
+
+ return name_new;
+}
+
+char *getDefaultUserName(int nr)
+{
+ static char *user_name[MAX_PLAYER_NAMES] = { NULL };
+
+ nr = MIN(MAX(0, nr), MAX_PLAYER_NAMES - 1);
+
+ if (user_name[nr] == NULL)
+ {
+ user_name[nr] = (nr == 0 ? getLoginName() : EMPTY_PLAYER_NAME);
+ user_name[nr] = getFixedUserName(user_name[nr]);
+ }
+
+ return user_name[nr];
+}
+
+char *getTimestampFromEpoch(time_t epoch_seconds)
+{
+ struct tm *now = localtime(&epoch_seconds);
+ static char timestamp[20];
+
+ sprintf(timestamp, "%04d%02d%02d-%02d%02d%02d",
+ now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
+ now->tm_hour, now->tm_min, now->tm_sec);
+
+ return timestamp;
+}
+
+char *getCurrentTimestamp(void)
+{
+ return getTimestampFromEpoch(time(NULL));
+}
+
+time_t getFileTimestampEpochSeconds(char *filename)
+{
+ struct stat file_status;
+
+ if (stat(filename, &file_status) != 0) // cannot stat file
+ return 0;
+
+ return file_status.st_mtime;
+}
+
+
+// ----------------------------------------------------------------------------
+// path manipulation functions
+// ----------------------------------------------------------------------------
+
+static char *getLastPathSeparatorPtr(char *filename)
+{
+ char *last_separator = strrchr(filename, CHAR_PATH_SEPARATOR_UNIX);
+
+ if (last_separator == NULL) // also try DOS/Windows variant
+ last_separator = strrchr(filename, CHAR_PATH_SEPARATOR_DOS);
+
+ return last_separator;
+}
+
+char *getBaseNamePtr(char *filename)
+{
+ char *last_separator = getLastPathSeparatorPtr(filename);
+
+ if (last_separator != NULL)
+ return last_separator + 1; // separator found: strip base path
+ else
+ return filename; // no separator found: filename has no path
+}
+
+char *getBaseName(char *filename)
+{
+ return getStringCopy(getBaseNamePtr(filename));
+}
+
+char *getBaseNameNoSuffix(char *filename)
+{
+ char *basename = getStringCopy(getBaseNamePtr(filename));
+
+ // remove trailing suffix (separated by dot or hyphen)
+ if (basename[0] != '.' && basename[0] != '-')
+ {
+ if (strchr(basename, '.') != NULL)
+ *strchr(basename, '.') = '\0';
+
+ if (strchr(basename, '-') != NULL)
+ *strchr(basename, '-') = '\0';
+ }
+
+ return basename;
+}
+
+char *getBasePath(char *filename)
+{
+ char *basepath = getStringCopy(filename);
+ char *last_separator = getLastPathSeparatorPtr(basepath);
+
+ // if no separator was found, use current directory
+ if (last_separator == NULL)
+ {
+ free(basepath);
+
+ return getStringCopy(".");
+ }
+
+ // separator found: strip basename
+ *last_separator = '\0';
+
+ return basepath;
+}
+
+
+// ----------------------------------------------------------------------------
+// various string functions
+// ----------------------------------------------------------------------------
+
+char *getStringCat2WithSeparator(const char *s1,
+ const char *s2,
+ const char *sep)
+{
+ if (s1 == NULL || s2 == NULL || sep == NULL)
+ return NULL;
+
+ char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
+ strlen(s2) + 1);
+
+ sprintf(complete_string, "%s%s%s", s1, sep, s2);
+
+ return complete_string;
+}
+
+char *getStringCat3WithSeparator(const char *s1,
+ const char *s2,
+ const char *s3,
+ const 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);
+
+ sprintf(complete_string, "%s%s%s%s%s", s1, sep, s2, sep, s3);
+
+ return complete_string;
+}
+
+char *getStringCat2(const char *s1, const char *s2)
+{
+ return getStringCat2WithSeparator(s1, s2, "");
+}
+
+char *getStringCat3(const char *s1, const char *s2, const char *s3)
+{
+ return getStringCat3WithSeparator(s1, s2, s3, "");
+}
+
+char *getPath2(const char *path1, const char *path2)
+{
+#if defined(PLATFORM_ANDROID)
+ // 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);
+}
+
+char *getPath3(const char *path1, const char *path2, const char *path3)
+{
+#if defined(PLATFORM_ANDROID)
+ // 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);
+}
+
+static char *getPngOrPcxIfNotExists(char *filename)
+{
+ // switch from PNG to PCX file and vice versa, if file does not exist
+ // (backwards compatibility with PCX files used in previous versions)
+
+ if (!fileExists(filename) && strSuffix(filename, ".png"))
+ strcpy(&filename[strlen(filename) - 3], "pcx");
+ else if (!fileExists(filename) && strSuffix(filename, ".pcx"))
+ strcpy(&filename[strlen(filename) - 3], "png");
+
+ return filename;
+}
+
+char *getImg2(const char *path1, const char *path2)
+{
+ return getPngOrPcxIfNotExists(getPath2(path1, path2));
+}
+
+char *getImg3(const char *path1, const char *path2, const char *path3)
+{
+ return getPngOrPcxIfNotExists(getPath3(path1, path2, path3));
+}
+
+char *getStringCopy(const char *s)
+{
+ char *s_copy;
+
+ if (s == NULL)
+ return NULL;
+
+ s_copy = checked_malloc(strlen(s) + 1);
+ strcpy(s_copy, s);
+
+ return s_copy;
+}
+
+char *getStringCopyN(const 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 *getStringCopyNStatic(const char *s, int n)
+{
+ static char *s_copy = NULL;
+
+ checked_free(s_copy);
+
+ s_copy = getStringCopyN(s, n);
+
+ return s_copy;
+}
+
+char *getStringToUpper(const char *s)
+{
+ if (s == NULL)
+ return NULL;
+
+ char *s_copy = checked_malloc(strlen(s) + 1);
+ char *s_ptr = s_copy;
+
+ while (*s)
+ *s_ptr++ = toupper(*s++);
+ *s_ptr = '\0';
+
+ return s_copy;
+}
+
+char *getStringToLower(const char *s)
+{
+ if (s == NULL)
+ return NULL;
+
+ char *s_copy = checked_malloc(strlen(s) + 1);
+ char *s_ptr = s_copy;
+
+ while (*s)
+ *s_ptr++ = tolower(*s++);
+ *s_ptr = '\0';
+
+ return s_copy;
+}
+
+static char *getStringVPrint(char *format, va_list ap)
+{
+ char s[MAX_LINE_LEN];
+
+ vsnprintf(s, MAX_LINE_LEN, format, ap); // may truncate output string
+
+ return getStringCopy(s);
+}
+
+char *getStringPrint(char *format, ...)
+{
+ va_list ap;
+ char *s;
+
+ va_start(ap, format);
+ s = getStringVPrint(format, ap);
+ va_end(ap);
+
+ return s;
+}
+
+void setStringPrint(char **s, char *format, ...)
+{
+ va_list ap;
+
+ checked_free(*s);
+
+ va_start(ap, format);
+ *s = getStringVPrint(format, ap);
+ va_end(ap);
+}
+
+void appendStringPrint(char **s_old, char *format, ...)
+{
+ va_list ap;
+ char *s_new;
+
+ va_start(ap, format);
+ s_new = getStringVPrint(format, ap);
+ va_end(ap);
+
+ char *s_combined = getStringCat2(*s_old, s_new);