/* ========================================================================= */
/* ------------------------------------------------------------------------- */
-/* platform independent wrappers for printf() et al. (newline aware) */
+/* logging functions */
/* ------------------------------------------------------------------------- */
+#define DUPLICATE_LOG_OUT_TO_STDOUT TRUE
+#define DUPLICATE_LOG_ERR_TO_STDERR TRUE
+
+
#if defined(PLATFORM_ANDROID)
static int android_log_prio = ANDROID_LOG_INFO;
-#endif
+static char *android_log_buffer = NULL;
-#if 0
-static void vfPrintLog(FILE *stream, char *format, va_list ap)
+static void append_to_android_log_buffer(char *format, va_list ap)
{
-}
+ char text_new[MAX_OUTPUT_LINESIZE];
-static void vfPrintLog(FILE *stream, char *format, va_list ap)
-{
+ // print text to temporary string
+ vsnprintf(text_new, MAX_OUTPUT_LINESIZE, format, ap);
+
+ if (android_log_buffer == NULL)
+ {
+ android_log_buffer = getStringCopy(text_new);
+ }
+ else
+ {
+ char *android_log_buffer_old = android_log_buffer;
+
+ // append new text to existing text
+ android_log_buffer = getStringCat2(android_log_buffer, text_new);
+
+ checked_free(android_log_buffer_old);
+ }
}
-static void fPrintLog(FILE *stream, char *format, va_list ap)
+static void vprintf_log_nonewline(char *format, va_list ap)
{
+ // add log output to buffer until text with newline is printed
+ append_to_android_log_buffer(format, ap);
}
-static void fPrintLog(FILE *stream, char *format, va_list ap)
+static void vprintf_log(char *format, va_list ap)
{
+ // add log output to buffer
+ append_to_android_log_buffer(format, ap);
+
+ // __android_log_vprint(android_log_prio, program.program_title, format, ap);
+ __android_log_write(android_log_prio, program.program_title,
+ android_log_buffer);
+
+ checked_free(android_log_buffer);
+ android_log_buffer = NULL;
}
-#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);
+static void vprintf_log_nonewline(char *format, va_list ap)
+{
+ FILE *file = program.log_file[LOG_ERR_ID];
+
+#if DUPLICATE_LOG_ERR_TO_STDERR
+ if (file != program.log_file_default[LOG_ERR_ID])
+ {
+ va_list ap2;
+ va_copy(ap2, ap);
+
+ vfprintf(program.log_file_default[LOG_ERR_ID], format, ap2);
- va_end(ap2);
+ va_end(ap2);
+ }
#endif
+
+ vfprintf(file, format, ap);
}
-static void vfprintf_newline(FILE *stream, char *format, va_list ap)
+static void vprintf_log(char *format, va_list ap)
{
-#if defined(PLATFORM_ANDROID)
- __android_log_vprint(android_log_prio, program.program_title, format, ap);
-#else
+ FILE *file = program.log_file[LOG_ERR_ID];
char *newline = STRING_NEWLINE;
- va_list ap2;
- va_copy(ap2, ap);
-
- vfprintf(stream, format, ap);
- fprintf(stream, "%s", newline);
+#if DUPLICATE_LOG_ERR_TO_STDERR
+ if (file != program.log_file_default[LOG_ERR_ID])
+ {
+ va_list ap2;
+ va_copy(ap2, ap);
- vfprintf(stderr, format, ap2);
- fprintf(stderr, "%s", newline);
+ vfprintf(program.log_file_default[LOG_ERR_ID], format, ap2);
+ fprintf(program.log_file_default[LOG_ERR_ID], "%s", newline);
- va_end(ap2);
+ va_end(ap2);
+ }
#endif
+
+ vfprintf(file, format, ap);
+ fprintf(file, "%s", newline);
}
+#endif
-static void fprintf_nonewline(FILE *stream, char *format, ...)
+static void printf_log_nonewline(char *format, ...)
{
va_list ap;
va_start(ap, format);
- vfprintf_nonewline(stream, format, ap);
+ vprintf_log_nonewline(format, ap);
va_end(ap);
}
-static void fprintf_newline(FILE *stream, char *format, ...)
+static void printf_log(char *format, ...)
{
va_list ap;
va_start(ap, format);
- vfprintf_newline(stream, format, ap);
+ vprintf_log(format, ap);
va_end(ap);
}
-void fprintf_line(FILE *stream, char *line_chars, int line_length)
+static void printf_log_line(char *line_chars, int line_length)
+{
+ int i;
+
+ for (i = 0; i < line_length; i++)
+ printf_log_nonewline("%s", line_chars);
+
+ printf_log("");
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* platform independent wrappers for printf() et al. */
+/* ------------------------------------------------------------------------- */
+
+void fprintf_line(FILE *file, char *line_chars, int line_length)
{
int i;
for (i = 0; i < line_length; i++)
- fprintf_nonewline(stream, "%s", line_chars);
+ fprintf(file, "%s", line_chars);
+
+ fprintf(file, "\n");
+}
- fprintf_newline(stream, "");
+void fprintf_line_with_prefix(FILE *file, char *prefix, char *line_chars,
+ int line_length)
+{
+ fprintf(file, "%s", prefix);
+ fprintf_line(file, line_chars, line_length);
}
void printf_line(char *line_chars, int line_length)
void printf_line_with_prefix(char *prefix, char *line_chars, int line_length)
{
- fprintf(stdout, "%s", prefix);
- fprintf_line(stdout, line_chars, line_length);
+ fprintf_line_with_prefix(stdout, prefix, line_chars, line_length);
+}
+
+static void vPrint(char *format, va_list ap)
+{
+ FILE *file = program.log_file[LOG_OUT_ID];
+
+#if DUPLICATE_LOG_OUT_TO_STDOUT
+ if (file != program.log_file_default[LOG_OUT_ID])
+ {
+ va_list ap2;
+ va_copy(ap2, ap);
+
+ vfprintf(program.log_file_default[LOG_OUT_ID], format, ap2);
+
+ va_end(ap2);
+ }
+#endif
+
+ vfprintf(file, format, ap);
+}
+
+void Print(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vPrint(format, ap);
+ va_end(ap);
+}
+
+void PrintLine(char *line_chars, int line_length)
+{
+ int i;
+
+ for (i = 0; i < line_length; i++)
+ Print(line_chars);
+
+ Print("\n");
+}
+
+void PrintLineWithPrefix(char *prefix, char *line_chars, int line_length)
+{
+ Print(prefix);
+ PrintLine(line_chars, line_length);
}
if (do_busy_waiting)
{
/* we want to wait only a few ms -- if we assume that we have a
- kernel timer resolution of 10 ms, we would wait far to long;
+ kernel timer resolution of 10 ms, we would wait far too long;
therefore it's better to do a short interval of busy waiting
to get our sleeping time more accurate */
return TRUE;
}
-void WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay)
+int WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay)
{
unsigned int actual_counter;
+ int skip_frames = 0;
while (1)
{
break;
}
+ if (*counter_var != 0 &&
+ delay != 0 &&
+ actual_counter >= *counter_var + delay)
+ {
+ int lag = actual_counter - (*counter_var + delay);
+ int delay2 = (delay + 1) / 2;
+
+ if (lag >= delay2)
+ skip_frames = (lag + delay2) / delay;
+ }
+
*counter_var = actual_counter;
+
+ return skip_frames;
+}
+
+void SkipUntilDelayReached(unsigned int *counter_var, unsigned int delay,
+ int *loop_var, int last_loop_value)
+{
+ int skip_frames = WaitUntilDelayReached(counter_var, 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");
+#endif
+#endif
+
+ if (skip_frames == 0)
+ return;
+
+ // when skipping frames, make sure to never skip the last frame, as
+ // this may be needed for animations to reach a defined end state;
+ // furthermore, we assume that this function is called at the end
+ // of a "for" loop, which continues by incrementing the loop variable
+ // by one before checking the loop condition again; therefore we have
+ // to check against the last loop value minus one here
+
+ last_loop_value--;
+
+ if (*loop_var < last_loop_value) // never skip the last frame
+ {
+ *loop_var += skip_frames;
+
+ if (*loop_var > last_loop_value) // never skip the last frame
+ *loop_var = last_loop_value;
+ }
}
if (*from_ptr == ',')
break;
- /* the user's real name may contain 'ß' characters (german sharp s),
+ /* 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 == 'ß')
+ if (*from_ptr == CHAR_BYTE_SHARP_S)
{
from_ptr++;
*to_ptr++ = 's';
char *getStringCat2WithSeparator(char *s1, char *s2, char *sep)
{
+ if (s1 == NULL || s2 == NULL || sep == NULL)
+ return NULL;
+
char *complete_string = checked_malloc(strlen(s1) + strlen(sep) +
strlen(s2) + 1);
char *getStringCat3WithSeparator(char *s1, char *s2, char *s3, 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);
char *getPath2(char *path1, char *path2)
{
#if defined(PLATFORM_ANDROID)
- // workaround for reading from APK assets directory -- skip leading "./"
+ // 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(char *path1, char *path2, char *path3)
{
#if defined(PLATFORM_ANDROID)
- // workaround for reading from APK assets directory -- skip leading "./"
+ // 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);
}
+char *getImg2(char *path1, char *path2)
+{
+ char *filename = getPath2(path1, path2);
+
+ if (!fileExists(filename) && strSuffix(path2, ".png"))
+ {
+ // backward compatibility: if PNG file not found, check for PCX file
+ char *path2pcx = getStringCopy(path2);
+
+ strcpy(&path2pcx[strlen(path2pcx) - 3], "pcx");
+
+ free(filename);
+
+ filename = getPath2(path1, path2pcx);
+
+ free(path2pcx);
+ }
+
+ return filename;
+}
+
+char *getImg3(char *path1, char *path2, char *path3)
+{
+ char *filename = getPath3(path1, path2, path3);
+
+ if (!fileExists(filename) && strSuffix(path3, ".png"))
+ {
+ // backward compatibility: if PNG file not found, check for PCX file
+ char *path3pcx = getStringCopy(path3);
+
+ strcpy(&path3pcx[strlen(path3pcx) - 3], "pcx");
+
+ free(filename);
+
+ filename = getPath3(path1, path2, path3pcx);
+
+ free(path3pcx);
+ }
+
+ return filename;
+}
+
char *getStringCopy(const char *s)
{
char *s_copy;
options.network = FALSE;
options.verbose = FALSE;
options.debug = FALSE;
- options.debug_x11_sync = FALSE;
#if 1
options.verbose = TRUE;
{
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;
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 :
if (mode == ERR_INFO_LINE)
{
if (!last_line_was_separator)
- fprintf_line(program.error_file, format, 79);
+ printf_log_line(format, 79);
last_line_was_separator = TRUE;
if (format)
{
- va_list ap;
-
- fprintf_nonewline(program.error_file, "%s%s: ", program.command_basename,
- process_name);
+#if !defined(PLATFORM_ANDROID)
+ printf_log_nonewline("%s%s: ", program.command_basename, process_name);
+#endif
if (mode & ERR_WARN)
- fprintf_nonewline(program.error_file, "warning: ");
+ printf_log_nonewline("warning: ");
+
+ if (mode & ERR_EXIT)
+ printf_log_nonewline("fatal error: ");
+
+ va_list ap;
va_start(ap, format);
- vfprintf_newline(program.error_file, format, ap);
+ vprintf_log(format, ap);
va_end(ap);
if ((mode & ERR_EXIT) && !(mode & ERR_FROM_SERVER))
}
if (mode & ERR_HELP)
- fprintf_newline(program.error_file,
- "%s: Try option '--help' for more information.",
- program.command_basename);
+ printf_log("%s: Try option '--help' for more information.",
+ program.command_basename);
if (mode & ERR_EXIT)
- fprintf_newline(program.error_file, "%s%s: aborting",
- program.command_basename, process_name);
+ printf_log("%s%s: aborting", program.command_basename, process_name);
if (mode & ERR_EXIT)
{
/* various helper functions */
/* ------------------------------------------------------------------------- */
-inline void swap_numbers(int *i1, int *i2)
+void swap_numbers(int *i1, int *i2)
{
int help = *i1;
*i2 = help;
}
-inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
+void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
{
int help_x = *x1;
int help_y = *y1;
{ KSYM_braceright, "XK_braceright", "brace right" },
{ KSYM_asciitilde, "XK_asciitilde", "~" },
- /* special (non-ASCII) keys (ISO-Latin-1) */
- { KSYM_degree, "XK_degree", "°" },
- { KSYM_Adiaeresis, "XK_Adiaeresis", "Ä" },
- { KSYM_Odiaeresis, "XK_Odiaeresis", "Ö" },
- { KSYM_Udiaeresis, "XK_Udiaeresis", "Ü" },
- { KSYM_adiaeresis, "XK_adiaeresis", "ä" },
- { KSYM_odiaeresis, "XK_odiaeresis", "ö" },
- { KSYM_udiaeresis, "XK_udiaeresis", "ü" },
+ /* special (non-ASCII) keys */
+ { KSYM_degree, "XK_degree", "degree" },
+ { KSYM_Adiaeresis, "XK_Adiaeresis", "A umlaut" },
+ { KSYM_Odiaeresis, "XK_Odiaeresis", "O umlaut" },
+ { KSYM_Udiaeresis, "XK_Udiaeresis", "U umlaut" },
+ { KSYM_adiaeresis, "XK_adiaeresis", "a umlaut" },
+ { KSYM_odiaeresis, "XK_odiaeresis", "o umlaut" },
+ { KSYM_udiaeresis, "XK_udiaeresis", "u umlaut" },
{ KSYM_ssharp, "XK_ssharp", "sharp s" },
#if defined(TARGET_SDL2)
char getCharFromKey(Key key)
{
+ static struct
+ {
+ Key key;
+ byte key_char;
+ } translate_key_char[] =
+ {
+ /* special (non-ASCII) keys (ISO-8859-1) */
+ { KSYM_degree, CHAR_BYTE_DEGREE },
+ { KSYM_Adiaeresis, CHAR_BYTE_UMLAUT_A },
+ { KSYM_Odiaeresis, CHAR_BYTE_UMLAUT_O },
+ { KSYM_Udiaeresis, CHAR_BYTE_UMLAUT_U },
+ { KSYM_adiaeresis, CHAR_BYTE_UMLAUT_a },
+ { KSYM_odiaeresis, CHAR_BYTE_UMLAUT_o },
+ { KSYM_udiaeresis, CHAR_BYTE_UMLAUT_u },
+ { KSYM_ssharp, CHAR_BYTE_SHARP_S },
+
+ /* end-of-array identifier */
+ { 0, 0 }
+ };
+
char *keyname = getKeyNameFromKey(key);
char c = 0;
c = keyname[0];
else if (strEqual(keyname, "space"))
c = ' ';
+ else
+ {
+ int i = 0;
+
+ do
+ {
+ if (key == translate_key_char[i].key)
+ {
+ c = translate_key_char[i].key_char;
+
+ break;
+ }
+ }
+ while (translate_key_char[++i].key_char);
+ }
return c;
}
node_new->key = getStringCopy(key);
node_new->content = content;
node_new->next = *node_first;
+
+ if (*node_first)
+ (*node_first)->prev = node_new;
+
*node_first = node_new;
}
if (strEqual((*node_first)->key, key))
{
checked_free((*node_first)->key);
+
if (destructor_function)
destructor_function((*node_first)->content);
+
+ if ((*node_first)->next)
+ (*node_first)->next->prev = (*node_first)->prev;
+
+ checked_free(*node_first);
+
*node_first = (*node_first)->next;
}
else
dir->dir_entry->is_directory =
(stat(dir->dir_entry->filename, &file_status) == 0 &&
- (file_status.st_mode & S_IFMT) == S_IFDIR);
+ S_ISDIR(file_status.st_mode));
return dir->dir_entry;
}
if (dir_name == NULL)
return FALSE;
- boolean success = (access(dir_name, F_OK) == 0);
+ struct stat file_status;
+ boolean success = (stat(dir_name, &file_status) == 0 &&
+ S_ISDIR(file_status.st_mode));
#if defined(PLATFORM_ANDROID)
if (!success)
return FALSE;
}
-static boolean FileCouldBeArtwork(char *basename)
+static boolean FileCouldBeArtwork(char *filename)
{
+ char *basename = getBaseNamePtr(filename);
+
return (!strEqual(basename, ".") &&
!strEqual(basename, "..") &&
!fileHasSuffix(basename, "txt") &&
- !fileHasSuffix(basename, "conf"));
+ !fileHasSuffix(basename, "conf") &&
+ !directoryExists(filename));
}
boolean FileIsGraphic(char *filename)
{
- char *basename = getBaseNamePtr(filename);
-
- return FileCouldBeArtwork(basename);
+ return FileCouldBeArtwork(filename);
}
boolean FileIsSound(char *filename)
{
- char *basename = getBaseNamePtr(filename);
-
- return FileCouldBeArtwork(basename);
+ return FileCouldBeArtwork(filename);
}
boolean FileIsMusic(char *filename)
{
- char *basename = getBaseNamePtr(filename);
-
- return FileCouldBeArtwork(basename);
+ return FileCouldBeArtwork(filename);
}
-boolean FileIsArtworkType(char *basename, int type)
+boolean FileIsArtworkType(char *filename, int type)
{
- if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(basename)) ||
- (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(basename)) ||
- (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(basename)))
+ if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(filename)) ||
+ (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(filename)) ||
+ (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(filename)))
return TRUE;
return FALSE;
/* (now also added for Windows, to create files in user data directory) */
/* ------------------------------------------------------------------------- */
-char *getErrorFilename(char *basename)
+char *getLogFilename(char *basename)
{
return getPath2(getUserGameDataDir(), basename);
}
-void openErrorFile()
+void OpenLogFiles()
{
+ int i;
+
InitUserDataDirectory();
- if ((program.error_file = fopen(program.error_filename, MODE_WRITE)) == NULL)
+ for (i = 0; i < NUM_LOGS; i++)
{
- program.error_file = stderr;
+ if ((program.log_file[i] = fopen(program.log_filename[i], MODE_WRITE))
+ == NULL)
+ {
+ program.log_file[i] = program.log_file_default[i]; // reset to default
- Error(ERR_WARN, "cannot open file '%s' for writing: %s",
- program.error_filename, strerror(errno));
- }
+ Error(ERR_WARN, "cannot open file '%s' for writing: %s",
+ program.log_filename[i], strerror(errno));
+ }
- /* error output should be unbuffered so it is not truncated in a crash */
- setbuf(program.error_file, NULL);
+ /* output should be unbuffered so it is not truncated in a crash */
+ setbuf(program.log_file[i], NULL);
+ }
}
-void closeErrorFile()
+void CloseLogFiles()
{
- if (program.error_file != stderr) /* do not close stream 'stderr' */
- fclose(program.error_file);
+ int i;
+
+ for (i = 0; i < NUM_LOGS; i++)
+ if (program.log_file[i] != program.log_file_default[i])
+ fclose(program.log_file[i]);
}
-void dumpErrorFile()
+void DumpLogFile(int nr)
{
- FILE *error_file = fopen(program.error_filename, MODE_READ);
+ FILE *log_file = fopen(program.log_filename[nr], MODE_READ);
- if (error_file != NULL)
- {
- while (!feof(error_file))
- fputc(fgetc(error_file), stderr);
+ if (log_file == NULL)
+ return;
- fclose(error_file);
- }
+ while (!feof(log_file))
+ fputc(fgetc(log_file), stdout);
+
+ fclose(log_file);
}
void NotifyUserAboutErrorFile()
char *title_text = getStringCat2(program.program_title, " Error Message");
char *error_text = getStringCat2("The program was aborted due to an error; "
"for details, see the following error file:"
- STRING_NEWLINE, program.error_filename);
+ STRING_NEWLINE,
+ program.log_filename[LOG_ERR_ID]);
MessageBox(NULL, error_text, title_text, MB_OK);
#endif