/* logging functions */
/* ------------------------------------------------------------------------- */
-#define DUPLICATE_LOG_OUTPUT_TO_STDERR TRUE
+#define DUPLICATE_LOG_OUT_TO_STDOUT TRUE
+#define DUPLICATE_LOG_ERR_TO_STDERR TRUE
#if defined(PLATFORM_ANDROID)
static void vprintf_log_nonewline(char *format, va_list ap)
{
- FILE *file = program.error_file;
+ FILE *file = program.log_file[LOG_ERR_ID];
-#if DUPLICATE_LOG_OUTPUT_TO_STDERR
- if (file != stderr)
+#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);
+ vfprintf(program.log_file_default[LOG_ERR_ID], format, ap2);
va_end(ap2);
}
static void vprintf_log(char *format, va_list ap)
{
- FILE *file = program.error_file;
+ FILE *file = program.log_file[LOG_ERR_ID];
char *newline = STRING_NEWLINE;
-#if DUPLICATE_LOG_OUTPUT_TO_STDERR
- if (file != stderr)
+#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);
}
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);
+}
+
/* ------------------------------------------------------------------------- */
/* string functions */
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;
+ }
}
return getStringCopy(getBaseNamePtr(filename));
}
+char *getBaseNameNoSuffix(char *filename)
+{
+ char *basename = getStringCopy(getBaseNamePtr(filename));
+
+ // remove trailing suffix (separated by dot or hyphen)
+ if (basename[0] != '.' && basename[0] != '-')
+ {
+ if (strchr(basename, '.') != NULL)
+ *strchr(basename, '.') = '\0';
+
+ if (strchr(basename, '-') != NULL)
+ *strchr(basename, '-') = '\0';
+ }
+
+ return basename;
+}
+
char *getBasePath(char *filename)
{
char *basepath = getStringCopy(filename);
/* command line option handling functions */
/* ------------------------------------------------------------------------- */
-void GetOptions(char *argv[],
+void GetOptions(int argc, 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];
+ char **argvplus = checked_calloc((argc + 1) * sizeof(char **));
+ char **options_left = &argvplus[1];
+
+ /* replace original "argv" with null-terminated array of string pointers */
+ while (argc--)
+ argvplus[argc] = argv[argc];
/* if the program is configured to start from current directory (default),
determine program package directory from program binary (some versions
/* when doing batch processing, always enable verbose mode (warnings) */
options.verbose = TRUE;
}
+#if defined(PLATFORM_MACOSX)
+ else if (strPrefix(option, "-psn"))
+ {
+ /* ignore process serial number when launched via GUI on Mac OS X */
+ }
+#endif
else if (*option == '-')
{
Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
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 :
/* 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;
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;
}
struct stat file_status;
boolean success = (stat(dir_name, &file_status) == 0 &&
- (file_status.st_mode & S_IFMT) == S_IFDIR);
+ S_ISDIR(file_status.st_mode));
#if defined(PLATFORM_ANDROID)
if (!success)
string_has_parameter(value, "fade") ? FADE_MODE_FADE :
string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
string_has_parameter(value, "melt") ? FADE_MODE_MELT :
+ string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
FADE_MODE_DEFAULT);
}
else if (strPrefix(suffix, ".font")) /* (may also be ".font_xyz") */
/* (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 file '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