X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Flibgame%2Fmisc.c;h=239a7d8ce8b637ab26764b9dcc2fb9ff7db3585f;hb=2e69a002d77d8c0b18c8d44184504769c44d60d8;hp=8719e145160eb018f0520edc92a5ba8ba494f16f;hpb=4fc0827fea3bd1e340f4a3e580d83be1c8e2356a;p=rocksndiamonds.git diff --git a/src/libgame/misc.c b/src/libgame/misc.c index 8719e145..239a7d8c 100644 --- a/src/libgame/misc.c +++ b/src/libgame/misc.c @@ -21,7 +21,7 @@ #include "platform.h" -#if !defined(PLATFORM_WIN32) +#if defined(PLATFORM_UNIX) #include #include #endif @@ -252,7 +252,7 @@ void PrintLineWithPrefix(char *prefix, char *line_chars, int line_length) // ---------------------------------------------------------------------------- -// generic logging functions +// generic logging and error handling functions // ---------------------------------------------------------------------------- enum log_levels @@ -288,11 +288,29 @@ static void printf_log_prefix(int log_level, char *mode) printf_log_nonewline("[%s] ", log_token); } -static void Log(int log_level, char *mode, char *format, va_list ap) +static void vLog(int log_level, char *mode, char *format, va_list ap) { if (log_level < 0 || log_level > LOG_FATAL) return; + if (log_level == LOG_DEBUG) + { + // only show debug messages when in debug mode + if (!options.debug) + return; + + // if optional debug mode specified, limit debug output accordingly + if (options.debug_mode != NULL && + strstr(mode, options.debug_mode) == NULL) + return; + } + else if (log_level == LOG_WARN) + { + // only show warning messages when in verbose mode + if (!options.verbose) + return; + } + #if defined(PLATFORM_ANDROID) android_log_prio = (log_level == LOG_DEBUG ? ANDROID_LOG_DEBUG : log_level == LOG_INFO ? ANDROID_LOG_INFO : @@ -336,20 +354,53 @@ static void Log(int log_level, char *mode, char *format, va_list ap) vprintf_log(format, ap); } -void Debug(char *mode, char *format, ...) +static void Log(int log_level, char *mode, char *format, ...) { va_list ap; - if (!options.debug) - return; + va_start(ap, format); + vLog(log_level, mode, format, ap); + va_end(ap); +} + +void DebugContinued(char *mode, char *format, ...) +{ + static char message[MAX_LINE_LEN] = { 0 }; + + // initialize message (optional) + if (strEqual(format, "")) + { + message[0] = '\0'; - // if optional debug mode specified, limit debug output accordingly - if (options.debug_mode != NULL && - !strEqual(options.debug_mode, mode)) return; + } + char *message_ptr = message + strlen(message); + int size_left = MAX_LINE_LEN - strlen(message); + va_list ap; + + // append message va_start(ap, format); - Log(LOG_DEBUG, mode, format, ap); + vsnprintf(message_ptr, size_left, format, ap); + va_end(ap); + + // finalize message + if (strSuffix(format, "\n")) + { + message[strlen(message) - 1] = '\0'; + + Debug(mode, message); + + message[0] = '\0'; + } +} + +void Debug(char *mode, char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vLog(LOG_DEBUG, mode, format, ap); va_end(ap); } @@ -358,7 +409,7 @@ void Info(char *format, ...) va_list ap; va_start(ap, format); - Log(LOG_INFO, NULL, format, ap); + vLog(LOG_INFO, NULL, format, ap); va_end(ap); } @@ -367,10 +418,59 @@ void Warn(char *format, ...) va_list ap; va_start(ap, format); - Log(LOG_WARN, NULL, format, ap); + vLog(LOG_WARN, NULL, format, ap); + va_end(ap); +} + +void Error(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vLog(LOG_ERROR, NULL, format, ap); va_end(ap); } +void Fail(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vLog(LOG_FATAL, NULL, format, ap); + va_end(ap); + + if (!network.is_server_thread) + { + va_start(ap, format); + program.exit_message_function(format, ap); + va_end(ap); + } + + Log(LOG_FATAL, NULL, "aborting"); + + // network server thread: normal exit + if (network.is_server_thread) + exit(1); + + // main process: clean up stuff + program.exit_function(1); +} + +void FailWithHelp(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vLog(LOG_FATAL, NULL, format, ap); + va_end(ap); + + Log(LOG_FATAL, NULL, "try option '--help' for more information"); + Log(LOG_FATAL, NULL, "aborting"); + + // main process: clean up stuff + program.exit_function(1); +} + // ---------------------------------------------------------------------------- // string functions @@ -449,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 // ---------------------------------------------------------------------------- @@ -574,10 +718,15 @@ void SkipUntilDelayReached(unsigned int *counter_var, unsigned int delay, #if 0 #if DEBUG - printf("::: %d: %d ms", *loop_var, delay); if (skip_frames) - printf(" -> SKIP %d FRAME(S) [%d ms]", skip_frames, skip_frames * delay); - printf("\n"); + Debug("internal:SkipUntilDelayReached", + "%d: %d ms -> SKIP %d FRAME(S) [%d ms]", + *loop_var, delay, + skip_frames, skip_frames * delay); + else + Debug("internal:SkipUntilDelayReached", + "%d: %d ms", + *loop_var, delay); #endif #endif @@ -607,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) { @@ -635,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(¤t_time, NULL); + + 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 ? random_linux_libc(nr) % max : 0); + return (max > 0 ? get_random_number_ext(nr) % max : 0); } @@ -678,6 +850,48 @@ static char *get_corrected_real_name(char *real_name) } #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; @@ -686,6 +900,7 @@ char *getLoginName(void) 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) @@ -694,12 +909,12 @@ char *getLoginName(void) #elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID) if (login_name == NULL) { - struct passwd *pwd; + char *name = getUnixLoginName(); - if ((pwd = getpwuid(getuid())) == NULL) - login_name = ANONYMOUS_NAME; + if (name != NULL) + login_name = getStringCopy(name); else - login_name = getStringCopy(pwd->pw_name); + login_name = ANONYMOUS_NAME; } #else login_name = ANONYMOUS_NAME; @@ -726,10 +941,10 @@ char *getRealName(void) #elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID) if (real_name == NULL) { - struct passwd *pwd; + char *name = getUnixRealName(); - if ((pwd = getpwuid(getuid())) != NULL && strlen(pwd->pw_gecos) != 0) - real_name = get_corrected_real_name(pwd->pw_gecos); + if (name != NULL) + real_name = get_corrected_real_name(name); else real_name = ANONYMOUS_NAME; } @@ -740,6 +955,53 @@ char *getRealName(void) 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; @@ -983,6 +1245,22 @@ boolean strEqualN(char *s1, char *s2, int n) strncmp(s1, s2, n) == 0); } +boolean strEqualCase(char *s1, char *s2) +{ + return (s1 == NULL && s2 == NULL ? TRUE : + s1 == NULL && s2 != NULL ? FALSE : + s1 != NULL && s2 == NULL ? FALSE : + strcasecmp(s1, s2) == 0); +} + +boolean strEqualCaseN(char *s1, char *s2, int n) +{ + return (s1 == NULL && s2 == NULL ? TRUE : + s1 == NULL && s2 != NULL ? FALSE : + s1 != NULL && s2 == NULL ? FALSE : + strncasecmp(s1, s2, n) == 0); +} + boolean strPrefix(char *s, char *prefix) { return (s == NULL && prefix == NULL ? TRUE : @@ -1029,8 +1307,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 *rw_base_path = getProgramMainDataPath(argv[0], RW_BASE_PATH); + char *base_path = getProgramMainDataPath(argv[0], BASE_PATH); char **argvplus = checked_calloc((argc + 1) * sizeof(char **)); char **options_left = &argvplus[1]; @@ -1042,18 +1319,22 @@ void GetOptions(int argc, char *argv[], 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); - 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; @@ -1085,7 +1366,7 @@ void GetOptions(int argc, char *argv[], int option_len = strlen(option); if (option_len >= MAX_OPTION_LEN) - Error(ERR_EXIT_HELP, "unrecognized option '%s'", option); + FailWithHelp("unrecognized option '%s'", option); strcpy(option_str, option); // copy argument into buffer option = option_str; @@ -1103,14 +1384,14 @@ void GetOptions(int argc, char *argv[], { *option_arg++ = '\0'; // cut argument from option if (*option_arg == '\0') // no argument after '=' - Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str); + FailWithHelp("option '%s' has invalid argument", option_str); } option_len = strlen(option); if (strEqual(option, "-")) { - Error(ERR_EXIT_HELP, "unrecognized option '%s'", option); + FailWithHelp("unrecognized option '%s'", option); } else if (strncmp(option, "-help", option_len) == 0) { @@ -1121,26 +1402,24 @@ void GetOptions(int argc, char *argv[], else if (strncmp(option, "-basepath", option_len) == 0) { if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); + FailWithHelp("option '%s' requires an argument", option_str); - // this should be extended to separate options for ro and rw data - options.ro_base_directory = ro_base_path = getStringCopy(option_arg); - options.rw_base_directory = rw_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) { if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); + FailWithHelp("option '%s' requires an argument", option_str); options.level_directory = getStringCopy(option_arg); if (option_arg == next_option) @@ -1149,7 +1428,7 @@ void GetOptions(int argc, char *argv[], else if (strncmp(option, "-graphics", option_len) == 0) { if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); + FailWithHelp("option '%s' requires an argument", option_str); options.graphics_directory = getStringCopy(option_arg); if (option_arg == next_option) @@ -1158,7 +1437,7 @@ void GetOptions(int argc, char *argv[], else if (strncmp(option, "-sounds", option_len) == 0) { if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); + FailWithHelp("option '%s' requires an argument", option_str); options.sounds_directory = getStringCopy(option_arg); if (option_arg == next_option) @@ -1167,7 +1446,7 @@ void GetOptions(int argc, char *argv[], else if (strncmp(option, "-music", option_len) == 0) { if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); + FailWithHelp("option '%s' requires an argument", option_str); options.music_directory = getStringCopy(option_arg); if (option_arg == next_option) @@ -1193,6 +1472,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; @@ -1211,7 +1517,7 @@ void GetOptions(int argc, char *argv[], else if (strncmp(option, "-execute", option_len) == 0) { if (option_arg == NULL) - Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str); + FailWithHelp("option '%s' requires an argument", option_str); options.execute_command = getStringCopy(option_arg); if (option_arg == next_option) @@ -1220,6 +1526,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")) { @@ -1228,7 +1543,7 @@ void GetOptions(int argc, char *argv[], #endif else if (*option == '-') { - Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str); + FailWithHelp("unrecognized option '%s'", option_str); } else if (options.server_host == NULL) { @@ -1238,125 +1553,16 @@ void GetOptions(int argc, char *argv[], { options.server_port = atoi(*options_left); if (options.server_port < 1024) - Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port); + FailWithHelp("bad port number '%d'", options.server_port); } else - Error(ERR_EXIT_HELP, "too many arguments"); + FailWithHelp("too many arguments"); options_left++; } } -// ---------------------------------------------------------------------------- -// error handling functions -// ---------------------------------------------------------------------------- - -#define MAX_INTERNAL_ERROR_SIZE 1024 - -// used by SetError() and GetError() to store internal error messages -static char internal_error[MAX_INTERNAL_ERROR_SIZE]; - -void SetError(char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vsnprintf(internal_error, MAX_INTERNAL_ERROR_SIZE, format, ap); - va_end(ap); -} - -char *GetError(void) -{ - return internal_error; -} - -void Error(int mode, char *format, ...) -{ - static boolean last_line_was_separator = FALSE; - char *process_name = ""; - - if (program.log_file[LOG_ERR_ID] == NULL) - return; - -#if defined(PLATFORM_ANDROID) - android_log_prio = (mode & ERR_DEBUG ? ANDROID_LOG_DEBUG : - mode & ERR_INFO ? ANDROID_LOG_INFO : - mode & ERR_WARN ? ANDROID_LOG_WARN : - mode & ERR_EXIT ? ANDROID_LOG_FATAL : - ANDROID_LOG_UNKNOWN); -#endif - - // display debug messages only when running in debug mode - if (mode & ERR_DEBUG && !options.debug) - return; - - // display warnings only when running in verbose mode - if (mode & ERR_WARN && !options.verbose) - return; - - if (mode == ERR_INFO_LINE) - { - if (!last_line_was_separator) - printf_log_line(format, 79); - - last_line_was_separator = TRUE; - - return; - } - - last_line_was_separator = FALSE; - - if (mode & ERR_SOUND_SERVER) - process_name = " sound server"; - else if (mode & ERR_NETWORK_SERVER) - process_name = " network server"; - else if (mode & ERR_NETWORK_CLIENT) - process_name = " network client **"; - - if (format) - { -#if !defined(PLATFORM_ANDROID) - printf_log_nonewline("%s%s: ", program.command_basename, process_name); -#endif - - if (mode & ERR_WARN) - printf_log_nonewline("warning: "); - - if (mode & ERR_EXIT) - printf_log_nonewline("fatal error: "); - - va_list ap; - - va_start(ap, format); - vprintf_log(format, ap); - va_end(ap); - - if ((mode & ERR_EXIT) && !(mode & ERR_FROM_SERVER)) - { - va_start(ap, format); - program.exit_message_function(format, ap); - va_end(ap); - } - } - - if (mode & ERR_HELP) - printf_log("%s: Try option '--help' for more information.", - program.command_basename); - - if (mode & ERR_EXIT) - printf_log("%s%s: aborting", program.command_basename, process_name); - - if (mode & ERR_EXIT) - { - if (mode & ERR_FROM_SERVER) - exit(1); // child process: normal exit - else - program.exit_function(1); // main process: clean up stuff - } -} - - // ---------------------------------------------------------------------------- // checked memory allocation and freeing functions // ---------------------------------------------------------------------------- @@ -1368,7 +1574,7 @@ void *checked_malloc(unsigned int size) ptr = malloc(size); if (ptr == NULL) - Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size); + Fail("cannot allocate %d bytes -- out of memory", size); return ptr; } @@ -1380,7 +1586,7 @@ void *checked_calloc(unsigned int size) ptr = calloc(1, size); if (ptr == NULL) - Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size); + Fail("cannot allocate %d bytes -- out of memory", size); return ptr; } @@ -1390,7 +1596,7 @@ void *checked_realloc(void *ptr, unsigned int size) ptr = realloc(ptr, size); if (ptr == NULL) - Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size); + Fail("cannot allocate %d bytes -- out of memory", size); return ptr; } @@ -1623,6 +1829,167 @@ void WriteUnusedBytesToFile(FILE *file, unsigned int bytes) } +// ---------------------------------------------------------------------------- +// functions to convert between ISO-8859-1 and UTF-8 +// ---------------------------------------------------------------------------- + +char *getUTF8FromLatin1(char *latin1) +{ + int max_utf8_size = 2 * strlen(latin1) + 1; + char *utf8 = checked_calloc(max_utf8_size); + unsigned char *src = (unsigned char *)latin1; + unsigned char *dst = (unsigned char *)utf8; + + while (*src) + { + if (*src < 128) // pure 7-bit ASCII + { + *dst++ = *src; + } + else if (*src >= 160) // non-ASCII characters + { + *dst++ = 194 + (*src >= 192); + *dst++ = 128 + (*src & 63); + } + else // undefined in ISO-8859-1 + { + *dst++ = '?'; + } + + src++; + } + + // only use the smallest possible string buffer size + utf8 = checked_realloc(utf8, strlen(utf8) + 1); + + return utf8; +} + +char *getLatin1FromUTF8(char *utf8) +{ + int max_latin1_size = strlen(utf8) + 1; + char *latin1 = checked_calloc(max_latin1_size); + unsigned char *src = (unsigned char *)utf8; + unsigned char *dst = (unsigned char *)latin1; + + while (*src) + { + if (*src < 128) // pure 7-bit ASCII + { + *dst++ = *src++; + } + else if (src[0] == 194 && + src[1] >= 128 && src[1] < 192) // non-ASCII characters + { + *dst++ = src[1]; + src += 2; + } + else if (src[0] == 195 && + src[1] >= 128 && src[1] < 192) // non-ASCII characters + { + *dst++ = src[1] + 64; + src += 2; + } + + // all other UTF-8 characters are undefined in ISO-8859-1 + + else if (src[0] >= 192 && src[0] < 224 && + src[1] >= 128 && src[1] < 192) + { + *dst++ = '?'; + src += 2; + } + else if (src[0] >= 224 && src[0] < 240 && + src[1] >= 128 && src[1] < 192 && + src[2] >= 128 && src[2] < 192) + { + *dst++ = '?'; + 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) + { + *dst++ = '?'; + 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) + { + *dst++ = '?'; + 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) + { + *dst++ = '?'; + src += 6; + } + else + { + *dst++ = '?'; + src++; + } + } + + // only use the smallest possible string buffer size + latin1 = checked_realloc(latin1, strlen(latin1) + 1); + + return latin1; +} + + +// ---------------------------------------------------------------------------- +// 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 // ---------------------------------------------------------------------------- @@ -2060,47 +2427,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); @@ -2250,12 +2593,12 @@ static void dumpList(ListNode *node_first) while (node) { - printf("['%s' (%d)]\n", node->key, - ((struct ListNodeInfo *)node->content)->num_references); + Debug("internal:dumpList", "['%s' (%d)]", node->key, + ((struct ListNodeInfo *)node->content)->num_references); node = node->next; } - printf("[%d nodes]\n", getNumNodes(node_first)); + Debug("internal:dumpList", "[%d nodes]", getNumNodes(node_first)); } #endif @@ -2452,6 +2795,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 @@ -2944,13 +3303,14 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, num_file_list_entries_found = list_pos + 1; if (num_file_list_entries_found != num_file_list_entries) { - Error(ERR_INFO_LINE, "-"); - Error(ERR_INFO, "inconsistant config list information:"); - Error(ERR_INFO, "- should be: %d (according to 'src/conf_xxx.h')", + Error("---"); + Error("inconsistant config list information:"); + Error("- should be: %d (according to 'src/conf_xxx.h')", num_file_list_entries); - Error(ERR_INFO, "- found to be: %d (according to 'src/conf_xxx.c')", + Error("- found to be: %d (according to 'src/conf_xxx.c')", num_file_list_entries_found); - Error(ERR_EXIT, "please fix"); + + Fail("please fix"); } freeSetupFileHash(ignore_tokens_hash); @@ -3465,8 +3825,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++) @@ -3551,6 +3911,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); @@ -3561,14 +3926,14 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info, if (file_list_entry->default_is_cloned && strEqual(basename, UNDEFINED_FILENAME)) { - int error_mode = ERR_WARN; + void (*error_func)(char *, ...) = 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_func = Fail; - Error(error_mode, "token '%s' was cloned and has no default filename", - file_list_entry->token); + error_func("token '%s' was cloned and has no default filename", + file_list_entry->token); return; } @@ -3585,13 +3950,13 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info, if (filename == NULL) { - int error_mode = ERR_WARN; + void (*error_func)(char *, ...) = 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_func = Fail; - Error(error_mode, "cannot find default artwork file '%s'", basename); + error_func("cannot find default artwork file '%s'", basename); return; } @@ -3619,8 +3984,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) { @@ -3631,13 +3996,13 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info, } else { - int error_mode = ERR_WARN; + void (*error_func)(char *, ...) = Warn; // we can get away without sounds and music, but not without graphics if (artwork_info->type == ARTWORK_TYPE_GRAPHICS) - error_mode = ERR_EXIT; + error_func = Fail; - Error(error_mode, "cannot load artwork file '%s'", basename); + error_func("cannot load artwork file '%s'", basename); return; } @@ -3721,14 +4086,14 @@ void FreeCustomArtworkLists(struct ArtworkListInfo *artwork_info) char *getLogFilename(char *basename) { - return getPath2(getUserGameDataDir(), basename); + return getPath2(getMainUserGameDataDir(), basename); } void OpenLogFiles(void) { int i; - InitUserDataDirectory(); + InitMainUserDataDirectory(); for (i = 0; i < NUM_LOGS; i++) { @@ -3788,7 +4153,7 @@ void NotifyUserAboutErrorFile(void) #if DEBUG -#define DEBUG_PRINT_INIT_TIMESTAMPS FALSE +#define DEBUG_PRINT_INIT_TIMESTAMPS TRUE #define DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH 10 #define DEBUG_NUM_TIMESTAMPS 10 @@ -3836,9 +4201,9 @@ void debug_print_timestamp(int counter_nr, char *message) float timestamp_interval; if (counter_nr < 0) - Error(ERR_EXIT, "debugging: invalid negative counter"); + Fail("debugging: invalid negative counter"); else if (counter_nr >= DEBUG_NUM_TIMESTAMPS) - Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c"); + Fail("debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c"); #if DEBUG_TIME_IN_MICROSECONDS static double counter[DEBUG_NUM_TIMESTAMPS][2]; @@ -3856,12 +4221,12 @@ void debug_print_timestamp(int counter_nr, char *message) counter[counter_nr][1] = counter[counter_nr][0]; if (message) - Error(ERR_DEBUG, "%s%s%s %.3f %s", - debug_print_timestamp_get_padding(counter_nr * indent_size), - message, - debug_print_timestamp_get_padding(padding_size - strlen(message)), - timestamp_interval / 1000, - unit); + Debug("time:init", "%s%s%s %.3f %s", + debug_print_timestamp_get_padding(counter_nr * indent_size), + message, + debug_print_timestamp_get_padding(padding_size - strlen(message)), + timestamp_interval / 1000, + unit); } #if 0