/* 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 */
static void sleep_milliseconds(unsigned int milliseconds_delay)
{
- boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
-
- 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;
- therefore it's better to do a short interval of busy waiting
- to get our sleeping time more accurate */
-
- unsigned int base_counter = Counter(), actual_counter = Counter();
-
- while (actual_counter < base_counter + milliseconds_delay &&
- actual_counter >= base_counter)
- actual_counter = Counter();
- }
- else
- {
- SDL_Delay(milliseconds_delay);
- }
+ SDL_Delay(milliseconds_delay);
}
void Delay(unsigned int delay) /* Sleep specified number of milliseconds */
sleep_milliseconds(delay);
}
-boolean FrameReached(unsigned int *frame_counter_var,
- unsigned int frame_delay)
+boolean DelayReachedExt(unsigned int *counter_var, unsigned int delay,
+ unsigned int actual_counter)
{
- unsigned int actual_frame_counter = FrameCounter;
-
- if (actual_frame_counter >= *frame_counter_var &&
- actual_frame_counter < *frame_counter_var + frame_delay)
+ if (actual_counter >= *counter_var &&
+ actual_counter < *counter_var + delay)
return FALSE;
- *frame_counter_var = actual_frame_counter;
+ *counter_var = actual_counter;
return TRUE;
}
-boolean DelayReached(unsigned int *counter_var,
- unsigned int delay)
+boolean FrameReached(unsigned int *frame_counter_var, unsigned int frame_delay)
{
- unsigned int actual_counter = Counter();
+ return DelayReachedExt(frame_counter_var, frame_delay, FrameCounter);
+}
- if (actual_counter >= *counter_var &&
- actual_counter < *counter_var + delay)
- return FALSE;
+boolean DelayReached(unsigned int *counter_var, unsigned int delay)
+{
+ return DelayReachedExt(counter_var, delay, Counter());
+}
- *counter_var = actual_counter;
+void ResetDelayCounterExt(unsigned int *counter_var,
+ unsigned int actual_counter)
+{
+ DelayReachedExt(counter_var, 0, actual_counter);
+}
- return TRUE;
+void ResetFrameCounter(unsigned int *frame_counter_var)
+{
+ FrameReached(frame_counter_var, 0);
}
-void WaitUntilDelayReached(unsigned int *counter_var, unsigned int delay)
+void ResetDelayCounter(unsigned int *counter_var)
+{
+ DelayReached(counter_var, 0);
+}
+
+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);
return getStringCat3WithSeparator(path1, path2, path3, STRING_PATH_SEPARATOR);
}
-char *getImg2(char *path1, char *path2)
+static char *getPngOrPcxIfNotExists(char *filename)
{
- 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);
+ // switch from PNG to PCX file and vice versa, if file does not exist
+ // (backwards compatibility with PCX files used in previous versions)
- strcpy(&path2pcx[strlen(path2pcx) - 3], "pcx");
-
- free(filename);
-
- filename = getPath2(path1, path2pcx);
-
- free(path2pcx);
- }
+ if (!fileExists(filename) && strSuffix(filename, ".png"))
+ strcpy(&filename[strlen(filename) - 3], "pcx");
+ else if (!fileExists(filename) && strSuffix(filename, ".pcx"))
+ strcpy(&filename[strlen(filename) - 3], "png");
return filename;
}
-char *getImg3(char *path1, char *path2, char *path3)
+char *getImg2(char *path1, char *path2)
{
- 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 getPngOrPcxIfNotExists(getPath2(path1, path2));
+}
- return filename;
+char *getImg3(char *path1, char *path2, char *path3)
+{
+ return getPngOrPcxIfNotExists(getPath3(path1, path2, path3));
}
char *getStringCopy(const char *s)
/* 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.error_file == NULL)
+ if (program.log_file[LOG_ERR_ID] == NULL)
return;
#if defined(PLATFORM_ANDROID)
/* 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;
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)
return NULL;
}
+char *get_special_base_token(struct ArtworkListInfo *artwork_info, char *token)
+{
+ /* !!! make this dynamically configurable (init.c:InitArtworkConfig) !!! */
+ static struct ConfigTypeInfo prefix_list[] =
+ {
+ { "global.anim_1" },
+ { "global.anim_2" },
+ { "global.anim_3" },
+ { "global.anim_4" },
+ { "global.anim_5" },
+ { "global.anim_6" },
+ { "global.anim_7" },
+ { "global.anim_8" },
+
+ { NULL }
+ };
+ struct ConfigTypeInfo *suffix_list = artwork_info->suffix_list;
+ boolean prefix_found = FALSE;
+ int len_suffix = 0;
+ int i;
+
+ /* search for prefix to check if base token has to be created */
+ for (i = 0; prefix_list[i].token != NULL; i++)
+ if (strPrefix(token, prefix_list[i].token))
+ prefix_found = TRUE;
+
+ if (!prefix_found)
+ return NULL;
+
+ /* search for suffix (parameter) to determine base token length */
+ for (i = 0; suffix_list[i].token != NULL; i++)
+ if (strSuffix(token, suffix_list[i].token))
+ len_suffix = strlen(suffix_list[i].token);
+
+ return getStringCopyN(token, strlen(token) - len_suffix);
+}
+
/* This function checks if a string <s> of the format "string1, string2, ..."
exactly contains a string <s_contained>. */
strEqual(value, "up") ? MV_UP :
strEqual(value, "down") ? MV_DOWN : MV_NONE);
}
+ else if (strEqual(suffix, ".position"))
+ {
+ result = (strEqual(value, "left") ? POS_LEFT :
+ strEqual(value, "right") ? POS_RIGHT :
+ strEqual(value, "top") ? POS_TOP :
+ strEqual(value, "upper") ? POS_UPPER :
+ strEqual(value, "middle") ? POS_MIDDLE :
+ strEqual(value, "lower") ? POS_LOWER :
+ strEqual(value, "bottom") ? POS_BOTTOM :
+ strEqual(value, "any") ? POS_ANY : POS_UNDEFINED);
+ }
else if (strEqual(suffix, ".align"))
{
result = (strEqual(value, "left") ? ALIGN_LEFT :
string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
string_has_parameter(value, "centered") ? ANIM_CENTERED :
+ string_has_parameter(value, "all") ? ANIM_ALL :
ANIM_DEFAULT);
if (string_has_parameter(value, "reverse"))
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") */
SetupFileHash *setup_file_hash, *valid_file_hash;
SetupFileHash *extra_file_hash, *empty_file_hash;
char *known_token_value = KNOWN_TOKEN_VALUE;
+ char *base_token_value = UNDEFINED_FILENAME;
int i, j, k, l;
if (filename == NULL)
}
END_HASH_ITERATION(valid_file_hash, itr)
+ /* add special base tokens (using prefix match and replace) */
+ BEGIN_HASH_ITERATION(valid_file_hash, itr)
+ {
+ char *token = HASH_ITERATION_TOKEN(itr);
+ char *base_token = get_special_base_token(artwork_info, token);
+
+ if (base_token != NULL)
+ {
+ /* add base token only if it does not already exist */
+ if (getHashEntry(valid_file_hash, base_token) == NULL)
+ setHashEntry(valid_file_hash, base_token, base_token_value);
+
+ free(base_token);
+ }
+ }
+ END_HASH_ITERATION(valid_file_hash, itr)
+
/* read parameters for all known config file tokens */
for (i = 0; i < num_file_list_entries; i++)
read_token_parameters(valid_file_hash, suffix_list, &file_list[i]);
boolean unknown_tokens_found = FALSE;
boolean undefined_values_found = (hashtable_count(empty_file_hash) != 0);
- if ((setup_file_list = loadSetupFileList(filename)) == NULL)
- Error(ERR_EXIT, "loadSetupFileHash works, but loadSetupFileList fails");
+ /* list may be NULL for empty artwork config files */
+ setup_file_list = loadSetupFileList(filename);
BEGIN_HASH_ITERATION(extra_file_hash, itr)
{
/* (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