+ 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 PrintNoLog(char *format, ...)
+{
+ FILE *file = program.log_file_default[LOG_OUT_ID];
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(file, format, ap);
+ va_end(ap);
+
+ fflush(file);
+}
+
+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);
+}
+
+
+// ----------------------------------------------------------------------------
+// generic logging and error handling functions
+// ----------------------------------------------------------------------------
+
+enum log_levels
+{
+ LOG_UNKNOWN = 0,
+ LOG_DEBUG,
+ LOG_INFO,
+ LOG_WARN,
+ LOG_ERROR,
+ LOG_FATAL
+};
+
+static char *log_tokens[] =
+{
+ "UNKNOWN",
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERROR",
+ "FATAL"
+};
+
+static void printf_log_prefix(int log_level, char *mode)
+{
+ if (log_level < 0 || log_level > LOG_FATAL)
+ return;
+
+ char *log_token = log_tokens[log_level];
+
+ if (log_level == LOG_DEBUG)
+ printf_log_nonewline("[%s] [%s] ", log_token, mode);
+ else
+ printf_log_nonewline("[%s] ", log_token);
+}
+
+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 :
+ log_level == LOG_WARN ? ANDROID_LOG_WARN :
+ log_level == LOG_ERROR ? ANDROID_LOG_ERROR :
+ log_level == LOG_FATAL ? ANDROID_LOG_FATAL :
+ ANDROID_LOG_UNKNOWN);
+#endif
+
+ static boolean last_line_was_separator = FALSE;
+ char *log_token = log_tokens[log_level];
+
+ if (strEqual(format, "===") ||
+ strEqual(format, "---"))
+ {
+ static char *mode_last = NULL;
+ char line_char[2] = { format[0], '\0' };
+ int line_length = 80 - strlen(log_token) - 3;
+
+ if (log_level == LOG_DEBUG)
+ line_length -= strlen(mode) + 3;
+
+ if (last_line_was_separator && strEqual(mode, mode_last))
+ return;
+
+ printf_log_prefix(log_level, mode);
+ printf_log_line(line_char, line_length);
+
+ if (!strEqual(mode, mode_last))
+ setString(&mode_last, mode);
+
+ last_line_was_separator = TRUE;
+
+ return;
+ }
+
+ last_line_was_separator = FALSE;
+
+ printf_log_prefix(log_level, mode);
+
+ vprintf_log(format, ap);
+}
+
+static void Log(int log_level, char *mode, char *format, ...)
+{
+ va_list ap;
+
+ 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';
+
+ return;
+ }
+
+ char *message_ptr = message + strlen(message);
+ int size_left = MAX_LINE_LEN - strlen(message);
+ va_list ap;
+
+ // append message
+ va_start(ap, format);
+ 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);
+}
+
+void Info(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vLog(LOG_INFO, NULL, format, ap);
+ va_end(ap);
+}
+
+void Warn(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ 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
+// ----------------------------------------------------------------------------