X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Flibgame%2Fmisc.c;h=78dddf8aac39dc4b4ca62e7136e8b1daff56a9f1;hp=c06eac4f097f9e32f7031323b4613ba78aad1bf9;hb=3ff2e8a0b5c27b99a9920bdf5ed82bc41bf40181;hpb=2c54d1765fec2db1ab23e2d5b5fba927efa463ce diff --git a/src/libgame/misc.c b/src/libgame/misc.c index c06eac4f..78dddf8a 100644 --- a/src/libgame/misc.c +++ b/src/libgame/misc.c @@ -1,23 +1,23 @@ -/*********************************************************** -* Artsoft Retro-Game Library * -*----------------------------------------------------------* -* (c) 1994-2006 Artsoft Entertainment * -* Holger Schemel * -* Detmolder Strasse 189 * -* 33604 Bielefeld * -* Germany * -* e-mail: info@artsoft.org * -*----------------------------------------------------------* -* misc.c * -***********************************************************/ +// ============================================================================ +// Artsoft Retro-Game Library +// ---------------------------------------------------------------------------- +// (c) 1995-2014 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// misc.c +// ============================================================================ #include #include #include +#include #include #include #include #include +#include #include "platform.h" @@ -41,25 +41,80 @@ /* platform independent wrappers for printf() et al. (newline aware) */ /* ------------------------------------------------------------------------- */ +#if defined(PLATFORM_ANDROID) +static int android_log_prio = ANDROID_LOG_INFO; +#endif + +#if 0 +static void vfPrintLog(FILE *stream, char *format, va_list ap) +{ +} + +static void vfPrintLog(FILE *stream, char *format, va_list ap) +{ +} + +static void fPrintLog(FILE *stream, char *format, va_list ap) +{ +} + +static void fPrintLog(FILE *stream, char *format, va_list ap) +{ +} +#endif + +static void vfprintf_nonewline(FILE *stream, char *format, va_list ap) +{ +#if defined(PLATFORM_ANDROID) + // (prefix text of logging output is currently skipped on Android) + //__android_log_vprint(android_log_prio, program.program_title, format, ap); +#else + va_list ap2; + va_copy(ap2, ap); + + vfprintf(stream, format, ap); + vfprintf(stderr, format, ap2); + + va_end(ap2); +#endif +} + static void vfprintf_newline(FILE *stream, char *format, va_list ap) { +#if defined(PLATFORM_ANDROID) + __android_log_vprint(android_log_prio, program.program_title, format, ap); +#else char *newline = STRING_NEWLINE; - vfprintf(stream, format, ap); + va_list ap2; + va_copy(ap2, ap); + vfprintf(stream, format, ap); fprintf(stream, "%s", newline); + + vfprintf(stderr, format, ap2); + fprintf(stderr, "%s", newline); + + va_end(ap2); +#endif +} + +static void fprintf_nonewline(FILE *stream, char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf_nonewline(stream, format, ap); + va_end(ap); } static void fprintf_newline(FILE *stream, char *format, ...) { - if (format) - { - va_list ap; + va_list ap; - va_start(ap, format); - vfprintf_newline(stream, format, ap); - va_end(ap); - } + va_start(ap, format); + vfprintf_newline(stream, format, ap); + va_end(ap); } void fprintf_line(FILE *stream, char *line_chars, int line_length) @@ -67,7 +122,7 @@ void fprintf_line(FILE *stream, char *line_chars, int line_length) int i; for (i = 0; i < line_length; i++) - fprintf(stream, "%s", line_chars); + fprintf_nonewline(stream, "%s", line_chars); fprintf_newline(stream, ""); } @@ -106,7 +161,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]; @@ -166,83 +221,41 @@ boolean getTokenValueFromString(char *string, char **token, char **value) /* counter functions */ /* ------------------------------------------------------------------------- */ -#if defined(PLATFORM_MSDOS) -volatile unsigned long counter = 0; - -void increment_counter() -{ - counter++; -} - -END_OF_FUNCTION(increment_counter); -#endif - - /* maximal allowed length of a command line option */ #define MAX_OPTION_LEN 256 -#ifdef TARGET_SDL -static unsigned long mainCounter(int mode) +static unsigned int getCurrentMS() { - static unsigned long base_ms = 0; - unsigned long current_ms; - unsigned long counter_ms; - - current_ms = SDL_GetTicks(); - - /* reset base time in case of counter initializing or wrap-around */ - if (mode == INIT_COUNTER || current_ms < base_ms) - base_ms = current_ms; - - counter_ms = current_ms - base_ms; - - return counter_ms; /* return milliseconds since last init */ + return SDL_GetTicks(); } -#else /* !TARGET_SDL */ - -#if defined(PLATFORM_UNIX) -static unsigned long mainCounter(int mode) +static unsigned int mainCounter(int mode) { - static struct timeval base_time = { 0, 0 }; - struct timeval current_time; - unsigned long counter_ms; - - gettimeofday(¤t_time, NULL); + static unsigned int base_ms = 0; + unsigned int current_ms; - /* reset base time in case of counter initializing or wrap-around */ - if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec) - base_time = current_time; + /* get current system milliseconds */ + current_ms = getCurrentMS(); - counter_ms = (current_time.tv_sec - base_time.tv_sec) * 1000 - + (current_time.tv_usec - base_time.tv_usec) / 1000; + /* reset base timestamp in case of counter reset or wrap-around */ + if (mode == INIT_COUNTER || current_ms < base_ms) + base_ms = current_ms; - return counter_ms; /* return milliseconds since last init */ + /* return milliseconds since last counter reset */ + return current_ms - base_ms; } -#endif /* PLATFORM_UNIX */ -#endif /* !TARGET_SDL */ void InitCounter() /* set counter back to zero */ { -#if !defined(PLATFORM_MSDOS) mainCounter(INIT_COUNTER); -#else - LOCK_VARIABLE(counter); - LOCK_FUNCTION(increment_counter); - install_int_ex(increment_counter, BPS_TO_TIMER(100)); -#endif } -unsigned long Counter() /* get milliseconds since last call of InitCounter() */ +unsigned int Counter() /* get milliseconds since last call of InitCounter() */ { -#if !defined(PLATFORM_MSDOS) return mainCounter(READ_COUNTER); -#else - return (counter * 10); -#endif } -static void sleep_milliseconds(unsigned long milliseconds_delay) +static void sleep_milliseconds(unsigned int milliseconds_delay) { boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE); @@ -253,7 +266,7 @@ static void sleep_milliseconds(unsigned long milliseconds_delay) therefore it's better to do a short interval of busy waiting to get our sleeping time more accurate */ - unsigned long base_counter = Counter(), actual_counter = Counter(); + unsigned int base_counter = Counter(), actual_counter = Counter(); while (actual_counter < base_counter + milliseconds_delay && actual_counter >= base_counter) @@ -261,31 +274,19 @@ static void sleep_milliseconds(unsigned long milliseconds_delay) } else { -#if defined(TARGET_SDL) SDL_Delay(milliseconds_delay); -#elif defined(TARGET_ALLEGRO) - rest(milliseconds_delay); -#else - struct timeval delay; - - delay.tv_sec = milliseconds_delay / 1000; - delay.tv_usec = 1000 * (milliseconds_delay % 1000); - - if (select(0, NULL, NULL, NULL, &delay) != 0) - Error(ERR_WARN, "sleep_milliseconds(): select() failed"); -#endif } } -void Delay(unsigned long delay) /* Sleep specified number of milliseconds */ +void Delay(unsigned int delay) /* Sleep specified number of milliseconds */ { sleep_milliseconds(delay); } -boolean FrameReached(unsigned long *frame_counter_var, - unsigned long frame_delay) +boolean FrameReached(unsigned int *frame_counter_var, + unsigned int frame_delay) { - unsigned long actual_frame_counter = FrameCounter; + unsigned int actual_frame_counter = FrameCounter; if (actual_frame_counter >= *frame_counter_var && actual_frame_counter < *frame_counter_var + frame_delay) @@ -296,10 +297,10 @@ boolean FrameReached(unsigned long *frame_counter_var, return TRUE; } -boolean DelayReached(unsigned long *counter_var, - unsigned long delay) +boolean DelayReached(unsigned int *counter_var, + unsigned int delay) { - unsigned long actual_counter = Counter(); + unsigned int actual_counter = Counter(); if (actual_counter >= *counter_var && actual_counter < *counter_var + delay) @@ -310,9 +311,9 @@ boolean DelayReached(unsigned long *counter_var, return TRUE; } -void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay) +void WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay) { - unsigned long actual_counter; + unsigned int actual_counter; while (1) { @@ -333,18 +334,27 @@ void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay) /* random generator functions */ /* ------------------------------------------------------------------------- */ -unsigned int init_random_number(int nr, long seed) +unsigned int init_random_number(int nr, int seed) { if (seed == NEW_RANDOMIZE) { -#if defined(TARGET_SDL) - seed = (long)SDL_GetTicks(); -#else + /* default random seed */ + seed = (int)time(NULL); // seconds since the epoch + +#if !defined(PLATFORM_WIN32) + /* add some more randomness */ struct timeval current_time; gettimeofday(¤t_time, NULL); - seed = (long)current_time.tv_usec; + + seed += (int)current_time.tv_usec; // microseconds since the epoch #endif + + /* add some more randomness */ + seed += (int)SDL_GetTicks(); // milliseconds since SDL init + + /* add some more randomness */ + seed += GetSimpleRandom(1000000); } srandom_linux_libc(nr, (unsigned int) seed); @@ -362,7 +372,7 @@ unsigned int get_random_number(int nr, int max) /* system info functions */ /* ------------------------------------------------------------------------- */ -#if !defined(PLATFORM_MSDOS) +#if !defined(PLATFORM_ANDROID) static char *get_corrected_real_name(char *real_name) { char *real_name_new = checked_malloc(MAX_USERNAME_LEN + 1); @@ -370,7 +380,7 @@ static char *get_corrected_real_name(char *real_name) char *to_ptr = real_name_new; /* copy the name string, but not more than MAX_USERNAME_LEN characters */ - while (*from_ptr && (long)(to_ptr - real_name_new) < MAX_USERNAME_LEN - 1) + 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 */ @@ -438,7 +448,7 @@ char *getRealName() else real_name = ANONYMOUS_NAME; } -#elif defined(PLATFORM_UNIX) +#elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID) if (real_name == NULL) { struct passwd *pwd; @@ -455,6 +465,16 @@ char *getRealName() return real_name; } +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 */ @@ -498,6 +518,36 @@ char *getBasePath(char *filename) return basepath; } +static char *getProgramMainDataPath() +{ + char *main_data_path = getStringCopy(program.command_basepath); + +#if defined(PLATFORM_MACOSX) + static char *main_data_binary_subdir = NULL; + + if (main_data_binary_subdir == NULL) + { + main_data_binary_subdir = checked_malloc(strlen(program.program_title) + 1 + + strlen("app") + 1 + + strlen(MAC_APP_BINARY_SUBDIR) + 1); + + sprintf(main_data_binary_subdir, "%s.app/%s", + program.program_title, MAC_APP_BINARY_SUBDIR); + } + + // cut relative path to Mac OS X application binary directory from path + if (strSuffix(main_data_path, main_data_binary_subdir)) + main_data_path[strlen(main_data_path) - + strlen(main_data_binary_subdir)] = '\0'; + + // cut trailing path separator from path (but not if path is root directory) + if (strSuffix(main_data_path, "/") && !strEqual(main_data_path, "/")) + main_data_path[strlen(main_data_path) - 1] = '\0'; +#endif + + return main_data_path; +} + /* ------------------------------------------------------------------------- */ /* various string functions */ @@ -536,15 +586,27 @@ char *getStringCat3(char *s1, char *s2, char *s3) char *getPath2(char *path1, char *path2) { +#if defined(PLATFORM_ANDROID) + // workaround for reading from APK assets directory -- skip leading "./" + if (strEqual(path1, ".")) + return getStringCopy(path2); +#endif + return getStringCat2WithSeparator(path1, path2, STRING_PATH_SEPARATOR); } char *getPath3(char *path1, char *path2, char *path3) { +#if defined(PLATFORM_ANDROID) + // workaround for reading from APK assets directory -- skip leading "./" + if (strEqual(path1, ".")) + return getStringCat2WithSeparator(path2, path3, STRING_PATH_SEPARATOR); +#endif + return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR); } -char *getStringCopy(char *s) +char *getStringCopy(const char *s) { char *s_copy; @@ -557,7 +619,33 @@ char *getStringCopy(char *s) return s_copy; } -char *getStringToLower(char *s) +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 *getStringToLower(const char *s) { char *s_copy = checked_malloc(strlen(s) + 1); char *s_ptr = s_copy; @@ -592,34 +680,71 @@ boolean strEqualN(char *s1, char *s2, int n) 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 */ /* ------------------------------------------------------------------------- */ -void GetOptions(char *argv[], void (*print_usage_function)(void)) +void GetOptions(char *argv[], + void (*print_usage_function)(void), + void (*print_version_function)(void)) { char *ro_base_path = RO_BASE_PATH; char *rw_base_path = RW_BASE_PATH; char **options_left = &argv[1]; -#if !defined(PLATFORM_MACOSX) /* if the program is configured to start from current directory (default), - determine program package directory (KDE/Konqueror does not do this by - itself and fails otherwise); on Mac OS X, the program binary is stored - in an application package directory -- do not try to use this directory - as the program data directory (Mac OS X handles this correctly anyway) */ + determine program package directory from program binary (some versions + of KDE/Konqueror and Mac OS X (especially "Mavericks") apparently do not + set the current working directory to the program package directory) */ if (strEqual(ro_base_path, ".")) - ro_base_path = program.command_basepath; + ro_base_path = getProgramMainDataPath(); if (strEqual(rw_base_path, ".")) - rw_base_path = program.command_basepath; -#endif + rw_base_path = getProgramMainDataPath(); /* initialize global program options */ 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); @@ -627,15 +752,23 @@ 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 1 + options.verbose = TRUE; +#else #if !defined(PLATFORM_UNIX) if (*options_left == NULL) /* no options given -- enable verbose mode */ options.verbose = TRUE; +#endif #endif while (*options_left) @@ -655,7 +788,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, '='); @@ -671,7 +804,9 @@ void GetOptions(char *argv[], void (*print_usage_function)(void)) option_len = strlen(option); if (strEqual(option, "-")) + { Error(ERR_EXIT_HELP, "unrecognized option '%s'", option); + } else if (strncmp(option, "-help", option_len) == 0) { print_usage_function(); @@ -749,13 +884,28 @@ void GetOptions(char *argv[], void (*print_usage_function)(void)) { options.serveronly = TRUE; } + else if (strncmp(option, "-debug", option_len) == 0) + { + options.debug = TRUE; + } + else if (strncmp(option, "-debug-x11-sync", option_len) == 0) + { + options.debug_x11_sync = TRUE; + } else if (strncmp(option, "-verbose", option_len) == 0) { options.verbose = TRUE; } - else if (strncmp(option, "-debug", option_len) == 0) + else if (strncmp(option, "-version", option_len) == 0 || + strncmp(option, "-V", option_len) == 0) { - options.debug = TRUE; + print_version_function(); + + exit(0); + } + else if (strPrefix(option, "-D")) + { + options.special_flags = getStringCopy(&option[2]); } else if (strncmp(option, "-execute", option_len) == 0) { @@ -795,15 +945,17 @@ void GetOptions(char *argv[], void (*print_usage_function)(void)) /* error handling functions */ /* ------------------------------------------------------------------------- */ +#define MAX_INTERNAL_ERROR_SIZE 1024 + /* used by SetError() and GetError() to store internal error messages */ -static char internal_error[1024]; /* this is bad */ +static char internal_error[MAX_INTERNAL_ERROR_SIZE]; void SetError(char *format, ...) { va_list ap; va_start(ap, format); - vsprintf(internal_error, format, ap); + vsnprintf(internal_error, MAX_INTERNAL_ERROR_SIZE, format, ap); va_end(ap); } @@ -817,6 +969,14 @@ void Error(int mode, char *format, ...) static boolean last_line_was_separator = FALSE; char *process_name = ""; +#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 warnings only when running in verbose mode */ if (mode & ERR_WARN && !options.verbose) return; @@ -844,15 +1004,22 @@ void Error(int mode, char *format, ...) { va_list ap; - fprintf(program.error_file, "%s%s: ", program.command_basename, - process_name); + fprintf_nonewline(program.error_file, "%s%s: ", program.command_basename, + process_name); if (mode & ERR_WARN) - fprintf(program.error_file, "warning: "); + fprintf_nonewline(program.error_file, "warning: "); va_start(ap, format); vfprintf_newline(program.error_file, 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) @@ -878,7 +1045,7 @@ void Error(int mode, char *format, ...) /* checked memory allocation and freeing functions */ /* ------------------------------------------------------------------------- */ -void *checked_malloc(unsigned long size) +void *checked_malloc(unsigned int size) { void *ptr; @@ -890,7 +1057,7 @@ void *checked_malloc(unsigned long size) return ptr; } -void *checked_calloc(unsigned long size) +void *checked_calloc(unsigned int size) { void *ptr; @@ -902,7 +1069,7 @@ void *checked_calloc(unsigned long size) return ptr; } -void *checked_realloc(void *ptr, unsigned long size) +void *checked_realloc(void *ptr, unsigned int size) { ptr = realloc(ptr, size); @@ -918,6 +1085,19 @@ void checked_free(void *ptr) free(ptr); } +void clear_mem(void *ptr, unsigned int 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 */ @@ -949,9 +1129,9 @@ inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2) of the (not yet written) chunk, write the correct chunk size and finally write the chunk itself */ -int getFile8BitInteger(FILE *file) +int getFile8BitInteger(File *file) { - return fgetc(file); + return getByteFromFile(file); } int putFile8BitInteger(FILE *file, int value) @@ -962,14 +1142,14 @@ int putFile8BitInteger(FILE *file, int value) return 1; } -int getFile16BitInteger(FILE *file, int byte_order) +int getFile16BitInteger(File *file, int byte_order) { if (byte_order == BYTE_ORDER_BIG_ENDIAN) - return ((fgetc(file) << 8) | - (fgetc(file) << 0)); + return ((getByteFromFile(file) << 8) | + (getByteFromFile(file) << 0)); else /* BYTE_ORDER_LITTLE_ENDIAN */ - return ((fgetc(file) << 0) | - (fgetc(file) << 8)); + return ((getByteFromFile(file) << 0) | + (getByteFromFile(file) << 8)); } int putFile16BitInteger(FILE *file, int value, int byte_order) @@ -991,18 +1171,18 @@ int putFile16BitInteger(FILE *file, int value, int byte_order) return 2; } -int getFile32BitInteger(FILE *file, int byte_order) +int getFile32BitInteger(File *file, int byte_order) { if (byte_order == BYTE_ORDER_BIG_ENDIAN) - return ((fgetc(file) << 24) | - (fgetc(file) << 16) | - (fgetc(file) << 8) | - (fgetc(file) << 0)); + return ((getByteFromFile(file) << 24) | + (getByteFromFile(file) << 16) | + (getByteFromFile(file) << 8) | + (getByteFromFile(file) << 0)); else /* BYTE_ORDER_LITTLE_ENDIAN */ - return ((fgetc(file) << 0) | - (fgetc(file) << 8) | - (fgetc(file) << 16) | - (fgetc(file) << 24)); + return ((getByteFromFile(file) << 0) | + (getByteFromFile(file) << 8) | + (getByteFromFile(file) << 16) | + (getByteFromFile(file) << 24)); } int putFile32BitInteger(FILE *file, int value, int byte_order) @@ -1028,13 +1208,14 @@ int putFile32BitInteger(FILE *file, int value, int byte_order) return 4; } -boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size, +boolean getFileChunk(File *file, char *chunk_name, int *chunk_size, int byte_order) { const int chunk_name_length = 4; /* read chunk name */ - fgets(chunk_name, chunk_name_length + 1, file); + if (getStringFromFile(file, chunk_name, chunk_name_length + 1) == NULL) + return FALSE; if (chunk_size != NULL) { @@ -1042,7 +1223,7 @@ boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size, *chunk_size = getFile32BitInteger(file, byte_order); } - return (feof(file) || ferror(file) ? FALSE : TRUE); + return (checkEndOfFile(file) ? FALSE : TRUE); } int putFileChunk(FILE *file, char *chunk_name, int chunk_size, @@ -1068,12 +1249,12 @@ int putFileChunk(FILE *file, char *chunk_name, int chunk_size, return num_bytes; } -int getFileVersion(FILE *file) +int getFileVersion(File *file) { - int version_major = fgetc(file); - int version_minor = fgetc(file); - int version_patch = fgetc(file); - int version_build = fgetc(file); + int version_major = getByteFromFile(file); + int version_minor = getByteFromFile(file); + int version_patch = getByteFromFile(file); + int version_build = getByteFromFile(file); return VERSION_IDENT(version_major, version_minor, version_patch, version_build); @@ -1097,15 +1278,15 @@ int putFileVersion(FILE *file, int version) return 4; } -void ReadBytesFromFile(FILE *file, byte *buffer, unsigned long bytes) +void ReadBytesFromFile(File *file, byte *buffer, unsigned int bytes) { int i; - for(i = 0; i < bytes && !feof(file); i++) - buffer[i] = fgetc(file); + for (i = 0; i < bytes && !checkEndOfFile(file); i++) + buffer[i] = getByteFromFile(file); } -void WriteBytesToFile(FILE *file, byte *buffer, unsigned long bytes) +void WriteBytesToFile(FILE *file, byte *buffer, unsigned int bytes) { int i; @@ -1113,13 +1294,13 @@ void WriteBytesToFile(FILE *file, byte *buffer, unsigned long bytes) fputc(buffer[i], file); } -void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes) +void ReadUnusedBytesFromFile(File *file, unsigned int bytes) { - while (bytes-- && !feof(file)) - fgetc(file); + while (bytes-- && !checkEndOfFile(file)) + getByteFromFile(file); } -void WriteUnusedBytesToFile(FILE *file, unsigned long bytes) +void WriteUnusedBytesToFile(FILE *file, unsigned int bytes) { while (bytes--) fputc(0, file); @@ -1177,8 +1358,10 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) { KSYM_Meta_R, "XK_Meta_R", "right meta" }, { KSYM_Alt_L, "XK_Alt_L", "left alt" }, { KSYM_Alt_R, "XK_Alt_R", "right alt" }, +#if !defined(TARGET_SDL2) { KSYM_Super_L, "XK_Super_L", "left super" }, /* Win-L */ { KSYM_Super_R, "XK_Super_R", "right super" }, /* Win-R */ +#endif { KSYM_Mode_switch, "XK_Mode_switch", "mode switch" }, /* Alt-R */ { KSYM_Multi_key, "XK_Multi_key", "multi key" }, /* Ctrl-R */ @@ -1191,7 +1374,11 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) { KSYM_End, "XK_End", "end" }, { KSYM_Page_Up, "XK_Page_Up", "page up" }, { KSYM_Page_Down, "XK_Page_Down", "page down" }, - { KSYM_Menu, "XK_Menu", "menu" }, /* Win-Menu */ + +#if defined(TARGET_SDL2) + { KSYM_Menu, "XK_Menu", "menu" }, /* menu key */ + { KSYM_Back, "XK_Back", "back" }, /* back key */ +#endif /* ASCII 0x20 to 0x40 keys (except numbers) */ { KSYM_space, "XK_space", "space" }, @@ -1231,7 +1418,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) { KSYM_braceright, "XK_braceright", "brace right" }, { KSYM_asciitilde, "XK_asciitilde", "~" }, - /* special (non-ASCII) keys */ + /* special (non-ASCII) keys (ISO-Latin-1) */ { KSYM_degree, "XK_degree", "°" }, { KSYM_Adiaeresis, "XK_Adiaeresis", "Ä" }, { KSYM_Odiaeresis, "XK_Odiaeresis", "Ö" }, @@ -1241,6 +1428,35 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) { KSYM_udiaeresis, "XK_udiaeresis", "ü" }, { KSYM_ssharp, "XK_ssharp", "sharp s" }, +#if defined(TARGET_SDL2) + /* special (non-ASCII) keys (UTF-8, for reverse mapping only) */ + { KSYM_degree, "XK_degree", "\xc2\xb0" }, + { KSYM_Adiaeresis, "XK_Adiaeresis", "\xc3\x84" }, + { KSYM_Odiaeresis, "XK_Odiaeresis", "\xc3\x96" }, + { KSYM_Udiaeresis, "XK_Udiaeresis", "\xc3\x9c" }, + { KSYM_adiaeresis, "XK_adiaeresis", "\xc3\xa4" }, + { KSYM_odiaeresis, "XK_odiaeresis", "\xc3\xb6" }, + { KSYM_udiaeresis, "XK_udiaeresis", "\xc3\xbc" }, + { KSYM_ssharp, "XK_ssharp", "\xc3\x9f" }, + + /* other keys (for reverse mapping only) */ + { KSYM_space, "XK_space", " " }, +#endif + +#if defined(TARGET_SDL2) + /* keypad keys are not in numerical order in SDL2 */ + { KSYM_KP_0, "XK_KP_0", "keypad 0" }, + { KSYM_KP_1, "XK_KP_1", "keypad 1" }, + { KSYM_KP_2, "XK_KP_2", "keypad 2" }, + { KSYM_KP_3, "XK_KP_3", "keypad 3" }, + { KSYM_KP_4, "XK_KP_4", "keypad 4" }, + { KSYM_KP_5, "XK_KP_5", "keypad 5" }, + { KSYM_KP_6, "XK_KP_6", "keypad 6" }, + { KSYM_KP_7, "XK_KP_7", "keypad 7" }, + { KSYM_KP_8, "XK_KP_8", "keypad 8" }, + { KSYM_KP_9, "XK_KP_9", "keypad 9" }, +#endif + /* end-of-array identifier */ { 0, NULL, NULL } }; @@ -1258,8 +1474,10 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a)); else if (key >= KSYM_0 && key <= KSYM_9) sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0)); +#if !defined(TARGET_SDL2) else if (key >= KSYM_KP_0 && key <= KSYM_KP_9) sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0)); +#endif else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST) sprintf(name_buffer, "F%d", (int)(key - KSYM_FKEY_FIRST + 1)); else if (key == KSYM_UNDEFINED) @@ -1295,8 +1513,10 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a)); else if (key >= KSYM_0 && key <= KSYM_9) sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0)); +#if !defined(TARGET_SDL2) else if (key >= KSYM_KP_0 && key <= KSYM_KP_9) sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0)); +#endif else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST) sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_FKEY_FIRST + 1)); else if (key == KSYM_UNDEFINED) @@ -1316,7 +1536,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) while (translate_key[++i].x11name); if (!translate_key[i].x11name) - sprintf(name_buffer, "0x%04lx", (unsigned long)key); + sprintf(name_buffer, "0x%04x", (unsigned int)key); } *x11name = name_buffer; @@ -1324,17 +1544,34 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode) else if (mode == TRANSLATE_KEYNAME_TO_KEYSYM) { Key key = KSYM_UNDEFINED; + char *name_ptr = *name; + + if (strlen(*name) == 1) + { + char c = name_ptr[0]; + + if (c >= 'A' && c <= 'Z') + key = KSYM_A + (Key)(c - 'A'); + else if (c >= 'a' && c <= 'z') + key = KSYM_a + (Key)(c - 'a'); + else if (c >= '0' && c <= '9') + key = KSYM_0 + (Key)(c - '0'); + } - i = 0; - do + if (key == KSYM_UNDEFINED) { - if (strEqual(translate_key[i].name, *name)) + i = 0; + + do { - key = translate_key[i].key; - break; + if (strEqual(translate_key[i].name, *name)) + { + key = translate_key[i].key; + break; + } } + while (translate_key[++i].x11name); } - while (translate_key[++i].x11name); if (key == KSYM_UNDEFINED) Error(ERR_WARN, "getKeyFromKeyName(): not completely implemented"); @@ -1346,7 +1583,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]; @@ -1357,14 +1594,16 @@ 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) +#if !defined(TARGET_SDL2) + 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) +#endif + else if (strPrefix(name_ptr, "XK_F") && strlen(name_ptr) <= 6) { char c1 = name_ptr[4]; char c2 = name_ptr[5]; @@ -1377,7 +1616,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; @@ -1391,9 +1630,9 @@ 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; + unsigned int value = 0; name_ptr += 2; @@ -1517,9 +1756,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); @@ -1546,6 +1789,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 */ @@ -1615,96 +1876,421 @@ void dumpList(ListNode *node_first) /* ------------------------------------------------------------------------- */ -/* functions for checking files and filenames */ +/* functions for file handling */ /* ------------------------------------------------------------------------- */ -boolean fileExists(char *filename) +File *openFile(char *filename, char *mode) { - if (filename == NULL) - return FALSE; + File *file = checked_calloc(sizeof(File)); - return (access(filename, F_OK) == 0); -} + file->file = fopen(filename, mode); -boolean fileHasPrefix(char *basename, char *prefix) -{ - static char *basename_lower = NULL; - int basename_length, prefix_length; + if (file->file != NULL) + { + file->filename = getStringCopy(filename); - checked_free(basename_lower); + return file; + } - if (basename == NULL || prefix == NULL) - return FALSE; +#if defined(PLATFORM_ANDROID) + file->asset_file = SDL_RWFromFile(filename, mode); - basename_lower = getStringToLower(basename); - basename_length = strlen(basename_lower); - prefix_length = strlen(prefix); + if (file->asset_file != NULL) + { + file->file_is_asset = TRUE; + file->filename = getStringCopy(filename); - if (basename_length > prefix_length + 1 && - basename_lower[prefix_length] == '.' && - strncmp(basename_lower, prefix, prefix_length) == 0) - return TRUE; + return file; + } +#endif - return FALSE; + checked_free(file); + + return NULL; } -boolean fileHasSuffix(char *basename, char *suffix) +int closeFile(File *file) { - static char *basename_lower = NULL; - int basename_length, suffix_length; + if (file == NULL) + return -1; - checked_free(basename_lower); + int result = 0; - if (basename == NULL || suffix == NULL) - return FALSE; +#if defined(PLATFORM_ANDROID) + if (file->asset_file) + result = SDL_RWclose(file->asset_file); +#endif - basename_lower = getStringToLower(basename); - basename_length = strlen(basename_lower); - suffix_length = strlen(suffix); + if (file->file) + result = fclose(file->file); - if (basename_length > suffix_length + 1 && - basename_lower[basename_length - suffix_length - 1] == '.' && + checked_free(file->filename); + checked_free(file); + + return result; +} + +int checkEndOfFile(File *file) +{ +#if defined(PLATFORM_ANDROID) + if (file->file_is_asset) + return file->end_of_file; +#endif + + return feof(file->file); +} + +size_t readFile(File *file, void *buffer, size_t item_size, size_t num_items) +{ +#if defined(PLATFORM_ANDROID) + if (file->file_is_asset) + { + if (file->end_of_file) + return 0; + + size_t num_items_read = + SDL_RWread(file->asset_file, buffer, item_size, num_items); + + if (num_items_read < num_items) + file->end_of_file = TRUE; + + return num_items_read; + } +#endif + + return fread(buffer, item_size, num_items, file->file); +} + +int seekFile(File *file, long offset, int whence) +{ +#if defined(PLATFORM_ANDROID) + if (file->file_is_asset) + { + int sdl_whence = (whence == SEEK_SET ? RW_SEEK_SET : + whence == SEEK_CUR ? RW_SEEK_CUR : + whence == SEEK_END ? RW_SEEK_END : 0); + + return (SDL_RWseek(file->asset_file, offset, sdl_whence) == -1 ? -1 : 0); + } +#endif + + return fseek(file->file, offset, whence); +} + +int getByteFromFile(File *file) +{ +#if defined(PLATFORM_ANDROID) + if (file->file_is_asset) + { + if (file->end_of_file) + return EOF; + + byte c; + size_t num_bytes_read = SDL_RWread(file->asset_file, &c, 1, 1); + + if (num_bytes_read < 1) + file->end_of_file = TRUE; + + return (file->end_of_file ? EOF : (int)c); + } +#endif + + return fgetc(file->file); +} + +char *getStringFromFile(File *file, char *line, int size) +{ +#if defined(PLATFORM_ANDROID) + if (file->file_is_asset) + { + if (file->end_of_file) + return NULL; + + char *line_ptr = line; + int num_bytes_read = 0; + + while (num_bytes_read < size - 1 && + SDL_RWread(file->asset_file, line_ptr, 1, 1) == 1 && + *line_ptr++ != '\n') + num_bytes_read++; + + *line_ptr = '\0'; + + if (strlen(line) == 0) + { + file->end_of_file = TRUE; + + return NULL; + } + + return line; + } +#endif + + return fgets(line, size, file->file); +} + + +/* ------------------------------------------------------------------------- */ +/* functions for directory handling */ +/* ------------------------------------------------------------------------- */ + +Directory *openDirectory(char *dir_name) +{ + Directory *dir = checked_calloc(sizeof(Directory)); + + dir->dir = opendir(dir_name); + + if (dir->dir != NULL) + { + dir->filename = getStringCopy(dir_name); + + return dir; + } + +#if defined(PLATFORM_ANDROID) + char *asset_toc_filename = getPath2(dir_name, ASSET_TOC_BASENAME); + + dir->asset_toc_file = SDL_RWFromFile(asset_toc_filename, MODE_READ); + + checked_free(asset_toc_filename); + + if (dir->asset_toc_file != NULL) + { + dir->directory_is_asset = TRUE; + dir->filename = getStringCopy(dir_name); + + return dir; + } +#endif + + checked_free(dir); + + return NULL; +} + +int closeDirectory(Directory *dir) +{ + if (dir == NULL) + return -1; + + int result = 0; + +#if defined(PLATFORM_ANDROID) + if (dir->asset_toc_file) + result = SDL_RWclose(dir->asset_toc_file); +#endif + + if (dir->dir) + result = closedir(dir->dir); + + if (dir->dir_entry) + freeDirectoryEntry(dir->dir_entry); + + checked_free(dir->filename); + checked_free(dir); + + return result; +} + +DirectoryEntry *readDirectory(Directory *dir) +{ + if (dir->dir_entry) + freeDirectoryEntry(dir->dir_entry); + + dir->dir_entry = NULL; + +#if defined(PLATFORM_ANDROID) + if (dir->directory_is_asset) + { + char line[MAX_LINE_LEN]; + char *line_ptr = line; + int num_bytes_read = 0; + + while (num_bytes_read < MAX_LINE_LEN - 1 && + SDL_RWread(dir->asset_toc_file, line_ptr, 1, 1) == 1 && + *line_ptr != '\n') + { + line_ptr++; + num_bytes_read++; + } + + *line_ptr = '\0'; + + if (strlen(line) == 0) + return NULL; + + dir->dir_entry = checked_calloc(sizeof(DirectoryEntry)); + + dir->dir_entry->is_directory = FALSE; + if (line[strlen(line) - 1] == '/') + { + dir->dir_entry->is_directory = TRUE; + + line[strlen(line) - 1] = '\0'; + } + + dir->dir_entry->basename = getStringCopy(line); + dir->dir_entry->filename = getPath2(dir->filename, line); + + return dir->dir_entry; + } +#endif + + struct dirent *dir_entry = readdir(dir->dir); + + if (dir_entry == NULL) + return NULL; + + dir->dir_entry = checked_calloc(sizeof(DirectoryEntry)); + + dir->dir_entry->basename = getStringCopy(dir_entry->d_name); + dir->dir_entry->filename = getPath2(dir->filename, dir_entry->d_name); + + struct stat file_status; + + dir->dir_entry->is_directory = + (stat(dir->dir_entry->filename, &file_status) == 0 && + (file_status.st_mode & S_IFMT) == S_IFDIR); + + return dir->dir_entry; +} + +void freeDirectoryEntry(DirectoryEntry *dir_entry) +{ + if (dir_entry == NULL) + return; + + checked_free(dir_entry->basename); + checked_free(dir_entry->filename); + checked_free(dir_entry); +} + + +/* ------------------------------------------------------------------------- */ +/* functions for checking files and filenames */ +/* ------------------------------------------------------------------------- */ + +boolean directoryExists(char *dir_name) +{ + if (dir_name == NULL) + return FALSE; + + boolean success = (access(dir_name, F_OK) == 0); + +#if defined(PLATFORM_ANDROID) + if (!success) + { + // this might be an asset directory; check by trying to open toc file + char *asset_toc_filename = getPath2(dir_name, ASSET_TOC_BASENAME); + SDL_RWops *file = SDL_RWFromFile(asset_toc_filename, MODE_READ); + + checked_free(asset_toc_filename); + + success = (file != NULL); + + if (success) + SDL_RWclose(file); + } +#endif + + return success; +} + +boolean fileExists(char *filename) +{ + if (filename == NULL) + return FALSE; + + boolean success = (access(filename, F_OK) == 0); + +#if defined(PLATFORM_ANDROID) + if (!success) + { + // this might be an asset file; check by trying to open it + SDL_RWops *file = SDL_RWFromFile(filename, MODE_READ); + + success = (file != NULL); + + if (success) + SDL_RWclose(file); + } +#endif + + return success; +} + +boolean fileHasPrefix(char *basename, char *prefix) +{ + static char *basename_lower = NULL; + int basename_length, prefix_length; + + checked_free(basename_lower); + + if (basename == NULL || prefix == NULL) + return FALSE; + + basename_lower = getStringToLower(basename); + basename_length = strlen(basename_lower); + prefix_length = strlen(prefix); + + if (basename_length > prefix_length + 1 && + basename_lower[prefix_length] == '.' && + strncmp(basename_lower, prefix, prefix_length) == 0) + return TRUE; + + return FALSE; +} + +boolean fileHasSuffix(char *basename, char *suffix) +{ + static char *basename_lower = NULL; + int basename_length, suffix_length; + + checked_free(basename_lower); + + if (basename == NULL || suffix == NULL) + return FALSE; + + basename_lower = getStringToLower(basename); + basename_length = strlen(basename_lower); + suffix_length = strlen(suffix); + + if (basename_length > suffix_length + 1 && + basename_lower[basename_length - suffix_length - 1] == '.' && strEqual(&basename_lower[basename_length - suffix_length], suffix)) return TRUE; return FALSE; } +static boolean FileCouldBeArtwork(char *basename) +{ + return (!strEqual(basename, ".") && + !strEqual(basename, "..") && + !fileHasSuffix(basename, "txt") && + !fileHasSuffix(basename, "conf")); +} + boolean FileIsGraphic(char *filename) { char *basename = getBaseNamePtr(filename); - return fileHasSuffix(basename, "pcx"); + return FileCouldBeArtwork(basename); } boolean FileIsSound(char *filename) { char *basename = getBaseNamePtr(filename); - return fileHasSuffix(basename, "wav"); + return FileCouldBeArtwork(basename); } boolean FileIsMusic(char *filename) { char *basename = getBaseNamePtr(filename); - if (FileIsSound(basename)) - return TRUE; - -#if defined(TARGET_SDL) - if (fileHasPrefix(basename, "mod") || - fileHasSuffix(basename, "mod") || - fileHasSuffix(basename, "s3m") || - fileHasSuffix(basename, "it") || - fileHasSuffix(basename, "xm") || - fileHasSuffix(basename, "midi") || - fileHasSuffix(basename, "mid") || - fileHasSuffix(basename, "mp3") || - fileHasSuffix(basename, "ogg")) - return TRUE; -#endif - - return FALSE; + return FileCouldBeArtwork(basename); } boolean FileIsArtworkType(char *basename, int type) @@ -1830,6 +2416,20 @@ 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, "inner_corners")) + result |= STYLE_INNER_CORNERS; + } else if (strEqual(suffix, ".fade_mode")) { result = (string_has_parameter(value, "none") ? FADE_MODE_NONE : @@ -1838,7 +2438,7 @@ int get_parameter_value(char *value_raw, char *suffix, int type) string_has_parameter(value, "melt") ? FADE_MODE_MELT : FADE_MODE_DEFAULT); } - else if (strEqualN(suffix, ".font", 5)) /* (may also be ".font_xyz") */ + else if (strPrefix(suffix, ".font")) /* (may also be ".font_xyz") */ { result = gfx.get_font_from_token_function(value); } @@ -1855,35 +2455,6 @@ int get_parameter_value(char *value_raw, char *suffix, int type) return result; } -int get_token_parameter_value(char *token, char *value_raw) -{ - char *suffix; - - if (token == NULL || value_raw == NULL) - return ARG_UNDEFINED_VALUE; - - suffix = strrchr(token, '.'); - if (suffix == NULL) - suffix = token; - -#if 0 - if (strncmp(suffix, ".font", 5) == 0) - { - int i; - - /* !!! OPTIMIZE THIS BY USING HASH !!! */ - for (i = 0; i < NUM_FONTS; i++) - if (strEqual(value_raw, font_info[i].token_name)) - return i; - - /* if font not found, use reliable default value */ - return FONT_INITIAL_1; - } -#endif - - return get_parameter_value(value_raw, suffix, TYPE_INTEGER); -} - struct ScreenModeInfo *get_screen_mode_from_string(char *screen_mode_string) { static struct ScreenModeInfo screen_mode; @@ -1968,14 +2539,15 @@ 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; } } list_pos = 0; + for (i = 0; config_list[i].token != NULL; i++) { int len_config_token = strlen(config_list[i].token); - int len_config_value = strlen(config_list[i].value); boolean is_file_entry = TRUE; for (j = 0; suffix_list[j].token != NULL; j++) @@ -1990,6 +2562,7 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, config_list[i].value); is_file_entry = FALSE; + break; } } @@ -2007,23 +2580,12 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, if (list_pos >= num_file_list_entries) break; - /* simple sanity check if this is really a file definition */ - if (!strEqual(&config_list[i].value[len_config_value - 4], ".pcx") && - !strEqual(&config_list[i].value[len_config_value - 4], ".wav") && - !strEqual(config_list[i].value, UNDEFINED_FILENAME)) - { - 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; @@ -2038,10 +2600,6 @@ struct FileInfo *getFileListFromConfigList(struct ConfigInfo *config_list, Error(ERR_EXIT, "please fix"); } -#if 0 - printf("::: ---------- DONE ----------\n"); -#endif - return file_list; } @@ -2132,6 +2690,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); } @@ -2182,10 +2741,6 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, if (filename == NULL) return; -#if 0 - printf("LoadArtworkConfigFromFilename '%s' ...\n", filename); -#endif - if ((setup_file_hash = loadSetupFileHash(filename)) == NULL) return; @@ -2268,10 +2823,6 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, boolean base_prefix_found = FALSE; boolean parameter_suffix_found = FALSE; -#if 0 - printf("::: examining '%s' -> '%s'\n", token, HASH_ITERATION_VALUE(itr)); -#endif - /* skip all parameter definitions (handled by read_token_parameters()) */ for (i = 0; i < num_suffix_list_entries && !parameter_suffix_found; i++) { @@ -2324,11 +2875,6 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, continue; } -#if 0 - if (IS_PARENT_PROCESS()) - printf("---> examining token '%s': search 1st suffix ...\n", token); -#endif - /* ---------- step 1: search for matching first suffix ---------- */ start_pos += len_base_prefix; @@ -2367,11 +2913,6 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, if (exact_match) break; -#if 0 - if (IS_PARENT_PROCESS()) - printf("---> examining token '%s': search 2nd suffix ...\n", token); -#endif - /* ---------- step 2: search for matching second suffix ---------- */ for (k = 0; k < num_ext2_suffixes && !ext2_suffix_found; k++) @@ -2409,11 +2950,6 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, if (exact_match) break; -#if 0 - if (IS_PARENT_PROCESS()) - printf("---> examining token '%s': search 3rd suffix ...\n",token); -#endif - /* ---------- step 3: search for matching third suffix ---------- */ for (l = 0; l < num_ext3_suffixes && !ext3_suffix_found; l++) @@ -2531,17 +3067,6 @@ static void LoadArtworkConfigFromFilename(struct ArtworkListInfo *artwork_info, freeSetupFileHash(extra_file_hash); freeSetupFileHash(empty_file_hash); - -#if 0 - for (i = 0; i < num_file_list_entries; i++) - { - printf("'%s' ", file_list[i].token); - if (file_list[i].filename) - printf("-> '%s'\n", file_list[i].filename); - else - printf("-> UNDEFINED [-> '%s']\n", file_list[i].default_filename); - } -#endif } void LoadArtworkConfig(struct ArtworkListInfo *artwork_info) @@ -2593,7 +3118,7 @@ void LoadArtworkConfig(struct ArtworkListInfo *artwork_info) artwork_info->num_property_mapping_entries = 0; } - if (!SETUP_OVERRIDE_ARTWORK(setup, artwork_info->type)) + if (!GFX_OVERRIDE_ARTWORK(artwork_info->type)) { /* first look for special artwork configured in level series config */ filename_base = getCustomArtworkLevelConfigFilename(artwork_info->type); @@ -2644,6 +3169,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; @@ -2675,23 +3216,15 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info, This usually means that this artwork does not exist in this artwork set and a fallback to the existing artwork is done. */ -#if 0 - printf("[artwork '%s' already exists (same list entry)]\n", filename); -#endif - return; } /* delete existing artwork file entry */ deleteArtworkListEntry(artwork_info, listnode); - /* check if the new artwork file already exists in the list of artworks */ + /* check if the new artwork file already exists in the list of artwork */ if ((node = getNodeFromKey(artwork_info->content_list, filename)) != NULL) { -#if 0 - printf("[artwork '%s' already exists (other list entry)]\n", filename); -#endif - *listnode = (struct ListNodeInfo *)node->content; (*listnode)->num_references++; @@ -2703,10 +3236,7 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info, if ((*listnode = artwork_info->load_artwork(filename)) != NULL) { -#if 0 - printf("[adding new artwork '%s']\n", filename); -#endif - + /* add new artwork file entry to the list of artwork files */ (*listnode)->num_references = 1; addNodeToList(&artwork_info->content_list, (*listnode)->source_filename, *listnode); @@ -2720,6 +3250,7 @@ static void replaceArtworkListEntry(struct ArtworkListInfo *artwork_info, error_mode = ERR_EXIT; Error(error_mode, "cannot load artwork file '%s'", basename); + return; } } @@ -2728,13 +3259,10 @@ static void LoadCustomArtwork(struct ArtworkListInfo *artwork_info, struct ListNodeInfo **listnode, struct FileInfo *file_list_entry) { -#if 0 - printf("GOT CUSTOM ARTWORK FILE '%s'\n", file_list_entry->filename); -#endif - if (strEqual(file_list_entry->filename, UNDEFINED_FILENAME)) { deleteArtworkListEntry(artwork_info, listnode); + return; } @@ -2750,6 +3278,8 @@ void ReloadCustomArtworkList(struct ArtworkListInfo *artwork_info) artwork_info->num_dynamic_file_list_entries; int i; + print_timestamp_init("ReloadCustomArtworkList"); + for (i = 0; i < num_file_list_entries; i++) LoadCustomArtwork(artwork_info, &artwork_info->artwork_list[i], &file_list[i]); @@ -2758,6 +3288,8 @@ void ReloadCustomArtworkList(struct ArtworkListInfo *artwork_info) LoadCustomArtwork(artwork_info, &artwork_info->dynamic_artwork_list[i], &dynamic_file_list[i]); + print_timestamp_done("ReloadCustomArtworkList"); + #if 0 dumpList(artwork_info->content_list); #endif @@ -2809,8 +3341,15 @@ void openErrorFile() 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); + { + program.error_file = stderr; + + Error(ERR_WARN, "cannot open file '%s' for writing: %s", + program.error_filename, strerror(errno)); + } + + /* error output should be unbuffered so it is not truncated in a crash */ + setbuf(program.error_file, NULL); } void closeErrorFile() @@ -2851,8 +3390,11 @@ void NotifyUserAboutErrorFile() #if DEBUG -#define DEBUG_NUM_TIMESTAMPS 3 -#define DEBUG_TIME_IN_MICROSECONDS 0 +#define DEBUG_PRINT_INIT_TIMESTAMPS FALSE +#define DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH 10 + +#define DEBUG_NUM_TIMESTAMPS 10 +#define DEBUG_TIME_IN_MICROSECONDS 0 #if DEBUG_TIME_IN_MICROSECONDS static double Counter_Microseconds() @@ -2875,35 +3417,53 @@ static double Counter_Microseconds() } #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) { -#if DEBUG_TIME_IN_MICROSECONDS - static double 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"); - counter[counter_nr][0] = Counter_Microseconds(); - - if (message) - printf("%s %.3f ms\n", message, - (counter[counter_nr][0] - counter[counter_nr][1]) / 1000); +#if DEBUG_TIME_IN_MICROSECONDS + static double counter[DEBUG_NUM_TIMESTAMPS][2]; + char *unit = "ms"; - counter[counter_nr][1] = counter[counter_nr][0]; + counter[counter_nr][0] = Counter_Microseconds(); #else - static long counter[DEBUG_NUM_TIMESTAMPS][2]; - - if (counter_nr >= DEBUG_NUM_TIMESTAMPS) - Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c"); + static int counter[DEBUG_NUM_TIMESTAMPS][2]; + char *unit = "s"; counter[counter_nr][0] = Counter(); +#endif - if (message) - printf("%s %.3f s\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]; -#endif + + 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); } void debug_print_parent_only(char *format, ...) @@ -2922,4 +3482,69 @@ void debug_print_parent_only(char *format, ...) printf("\n"); } } + +#endif /* DEBUG */ + +void print_timestamp_ext(char *message, char *mode) +{ +#if DEBUG_PRINT_INIT_TIMESTAMPS + static char *debug_message = NULL; + static char *last_message = NULL; + static int counter_nr = 0; + int max_depth = DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH; + + checked_free(debug_message); + debug_message = getStringCat3(mode, " ", message); + + if (strEqual(mode, "INIT")) + { + debug_print_timestamp(counter_nr, NULL); + + if (counter_nr + 1 < max_depth) + debug_print_timestamp(counter_nr, debug_message); + + counter_nr++; + + debug_print_timestamp(counter_nr, NULL); + } + else if (strEqual(mode, "DONE")) + { + counter_nr--; + + if (counter_nr + 1 < max_depth || + (counter_nr == 0 && max_depth == 1)) + { + last_message = message; + + if (counter_nr == 0 && max_depth == 1) + { + checked_free(debug_message); + debug_message = getStringCat3("TIME", " ", message); + } + + debug_print_timestamp(counter_nr, debug_message); + } + } + else if (!strEqual(mode, "TIME") || + !strEqual(message, last_message)) + { + if (counter_nr < max_depth) + debug_print_timestamp(counter_nr, debug_message); + } #endif +} + +void print_timestamp_init(char *message) +{ + print_timestamp_ext(message, "INIT"); +} + +void print_timestamp_time(char *message) +{ + print_timestamp_ext(message, "TIME"); +} + +void print_timestamp_done(char *message) +{ + print_timestamp_ext(message, "DONE"); +}