aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/scicore/tools.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/scicore/tools.cpp')
-rw-r--r--engines/sci/scicore/tools.cpp798
1 files changed, 798 insertions, 0 deletions
diff --git a/engines/sci/scicore/tools.cpp b/engines/sci/scicore/tools.cpp
new file mode 100644
index 0000000000..8167da33b4
--- /dev/null
+++ b/engines/sci/scicore/tools.cpp
@@ -0,0 +1,798 @@
+/***************************************************************************
+ tools.c Copyright (C) 1999,2000,2001,2002 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CJR) [jameson@linuxgames.com]
+
+***************************************************************************/
+
+#define _GNU_SOURCE /* For FNM_CASEFOLD in fnmatch.h */
+
+#include <stdlib.h>
+#include "sci/include/engine.h"
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef _MSC_VER
+# include <sys/timeb.h>
+# include <windows.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# include <mmsystem.h>
+
+void usleep (long usec);
+
+# ifdef sleep
+# undef sleep
+# endif
+
+# define sleep(x) \
+ do { \
+ if (x == 0) { \
+ Sleep(0); \
+ } else { \
+ if (timeBeginPeriod(1) != TIMERR_NOERROR) \
+ fprintf(stderr, "timeBeginPeriod(1) failed\n"); \
+ Sleep(x); \
+ if (timeEndPeriod(1) != TIMERR_NOERROR) \
+ fprintf(stderr, "timeEndPeriod(1) failed\n"); \
+ } \
+ } while (0);
+#endif
+
+#if !defined(HAVE_FNMATCH) && !defined(_WIN32)
+# include <beos/fnmatch.h>
+#endif
+
+#ifdef _DREAMCAST
+# include <kos/thread.h>
+#endif
+
+#ifdef __BEOS__
+# include <be/kernel/OS.h>
+#endif
+
+#ifdef HAVE_MEMFROB
+void *memfrob(void *s, size_t n);
+#endif
+
+int script_debug_flag = 0; /* Defaulting to running mode */
+int sci_debug_flags = 0; /* Special flags */
+
+#ifndef con_file
+# define con_file 0
+#endif
+
+#define MEMTEST_HARDNESS 31
+
+int
+memtest(const char *file, int line)
+{
+ /* va_list argp; -- unused */
+ int i;
+ void *blocks[MEMTEST_HARDNESS + 1];
+ fprintf(stderr,"Memtesting in %s, L%d\n", file, line);
+
+ for (i = 0; i < MEMTEST_HARDNESS; i++) {
+ blocks[i] = sci_malloc(1 + i);
+#ifdef HAVE_MEMFROB
+ memfrob(blocks[i], 1 + i);
+#else
+ memset(blocks[i], 42, 1 + i);
+#endif
+ }
+ for (i = 0; i < MEMTEST_HARDNESS; i++)
+ free(blocks[i]);
+
+ for (i = 0; i < MEMTEST_HARDNESS; i++) {
+ blocks[i] = sci_malloc(5 + i*5);
+#ifdef HAVE_MEMFROB
+ memfrob(blocks[i], 5 + i*5);
+#else
+ memset(blocks[i], 42, 5 + i*5);
+#endif
+ }
+ for (i = 0; i < MEMTEST_HARDNESS; i++)
+ free(blocks[i]);
+
+ for (i = 0; i < MEMTEST_HARDNESS; i++) {
+ blocks[i] = sci_malloc(5 + i*100);
+#ifdef HAVE_MEMFROB
+ memfrob(blocks[i], 5 + i*100);
+#else
+ memset(blocks[i], 42, 5 + i*100);
+#endif
+ }
+ for (i = 0; i < MEMTEST_HARDNESS; i++)
+ free(blocks[i]);
+
+ for (i = 0; i < MEMTEST_HARDNESS; i++) {
+ blocks[i] = sci_malloc(5 + i*1000);
+#ifdef HAVE_MEMFROB
+ memfrob(blocks[i], 5 + i * 1000);
+#else
+ memset(blocks[i], 42, 5 + i * 1000);
+#endif
+ }
+ for (i = 0; i < MEMTEST_HARDNESS; i++)
+ free(blocks[i]);
+ fprintf(stderr,"Memtest succeeded!\n");
+ return 0;
+}
+
+void *
+memdup(void *src, int size)
+{
+ void *b = malloc(size);
+ memcpy(b, src, size);
+ return b;
+}
+
+int sci_ffs(int _mask)
+{
+ int retval = 0;
+
+ if (!_mask) return 0;
+ retval++;
+ while (! (_mask & 1))
+ {
+ retval++;
+ _mask >>= 1;
+ }
+
+ return retval;
+}
+
+
+/******************** Debug functions ********************/
+
+void
+_SCIkvprintf(FILE *file, const char *format, va_list args)
+{
+ vfprintf(file, format, args);
+ if (con_file) vfprintf(con_file, format, args);
+}
+
+void
+_SCIkprintf(FILE *file, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ _SCIkvprintf(file, format, args);
+ va_end (args);
+}
+
+
+void
+_SCIkwarn(state_t *s, const char *file, int line, int area, const char *format, ...)
+{
+ va_list args;
+
+ if (area == SCIkERROR_NR)
+ _SCIkprintf(stderr, "ERROR: ");
+ else
+ _SCIkprintf(stderr, "Warning: ");
+
+ va_start(args, format);
+ _SCIkvprintf(stderr, format, args);
+ va_end(args);
+ fflush(NULL);
+
+ if (sci_debug_flags & _DEBUG_FLAG_BREAK_ON_WARNINGS) script_debug_flag=1;
+}
+
+void
+_SCIkdebug(state_t *s, const char *file, int line, int area, const char *format, ...)
+{
+ va_list args;
+
+ if (s->debug_mode & (1 << area)) {
+ _SCIkprintf(stdout, " kernel: (%s L%d): ", file, line);
+ va_start(args, format);
+ _SCIkvprintf(stdout, format, args);
+ va_end(args);
+ fflush(NULL);
+ }
+}
+
+void
+_SCIGNUkdebug(const char *funcname, state_t *s, const char *file, int line, int area, const char *format, ...)
+{
+ va_list xargs;
+ int error = ((area == SCIkWARNING_NR) || (area == SCIkERROR_NR));
+
+ if (error || (s->debug_mode & (1 << area))) { /* Is debugging enabled for this area? */
+
+ _SCIkprintf(stderr, "FSCI: ");
+
+ if (area == SCIkERROR_NR)
+ _SCIkprintf(stderr, "ERROR in %s ", funcname);
+ else if (area == SCIkWARNING_NR)
+ _SCIkprintf(stderr, "%s: Warning ", funcname);
+ else _SCIkprintf(stderr, funcname);
+
+ _SCIkprintf(stderr, "(%s L%d): ", file, line);
+
+ va_start(xargs, format);
+ _SCIkvprintf(stderr, format, xargs);
+ va_end(xargs);
+
+ }
+}
+
+
+#if defined(HAVE_GETTIMEOFDAY)
+void
+sci_gettime(long *seconds, long *useconds)
+{
+ struct timeval tv;
+
+ assert(!gettimeofday(&tv, NULL));
+ *seconds = tv.tv_sec;
+ *useconds = tv.tv_usec;
+}
+#elif defined (_WIN32)
+
+/*WARNING(Incorrect)*/
+/* Warning: This function only retrieves the amount of mseconds since the start of
+** the Win32 kernel; it does /not/ provide the number of seconds since the epoch!
+** There are no known cases where this causes problems, though. */
+void sci_gettime(long *seconds, long *useconds)
+{
+ DWORD tm;
+
+ if (TIMERR_NOERROR != timeBeginPeriod(1))
+ {
+ fprintf(stderr, "timeBeginPeriod(1) failed in sci_gettime\n");
+ }
+
+ tm = timeGetTime();
+
+ if (TIMERR_NOERROR != timeEndPeriod(1))
+ {
+ fprintf(stderr, "timeEndPeriod(1) failed in sci_gettime\n");
+ }
+
+ *seconds = tm/1000;
+ *useconds = (tm%1000)*1000;
+}
+#else
+# error "You need to provide a microsecond resolution sci_gettime implementation for your platform!"
+#endif
+
+
+void
+sci_get_current_time(GTimeVal *val)
+{
+ long foo, bar;
+ sci_gettime(&foo, &bar);
+ val->tv_sec = foo;
+ val->tv_usec = bar;
+}
+
+
+/************* Directory entities *************/
+#if defined(_WIN32)
+/******** Dir: Win32 CODE ********/
+
+void
+sci_init_dir(sci_dir_t *dir)
+{
+ dir->search = -1;
+}
+
+char *
+sci_find_first(sci_dir_t *dir, const char *mask)
+{
+ dir->search = _findfirst(mask, &(dir->fileinfo));
+
+ if (dir->search != -1)
+ {
+ if (dir->fileinfo.name == NULL)
+ {
+ return NULL;
+ }
+
+ if (strcmp(dir->fileinfo.name, ".") == 0 ||
+ strcmp(dir->fileinfo.name, "..") == 0)
+ {
+ if (sci_find_next(dir) == NULL)
+ {
+ return NULL;
+ }
+ }
+
+ return dir->fileinfo.name;
+ }
+ else
+ {
+ switch (errno)
+ {
+ case ENOENT:
+ {
+#ifdef _DEBUG
+ printf("_findfirst errno = ENOENT: no match\n");
+
+ if (mask)
+ printf(" in: %s\n", mask);
+ else
+ printf(" - searching in undefined directory\n");
+#endif
+ break;
+ }
+ case EINVAL:
+ {
+ printf("_findfirst errno = EINVAL: invalid filename\n");
+ break;
+ }
+ default:
+ printf("_findfirst errno = unknown (%d)", errno);
+ }
+ }
+
+ return NULL;
+}
+
+char *
+sci_find_next(sci_dir_t *dir)
+{
+ if (dir->search == -1)
+ return NULL;
+
+ if (_findnext(dir->search, &(dir->fileinfo)) < 0) {
+ _findclose(dir->search);
+ dir->search = -1;
+ return NULL;
+ }
+
+ if (strcmp(dir->fileinfo.name, ".") == 0 ||
+ strcmp(dir->fileinfo.name, "..") == 0)
+ {
+ if (sci_find_next(dir) == NULL)
+ {
+ return NULL;
+ }
+ }
+
+ return dir->fileinfo.name;
+}
+
+void
+sci_finish_find(sci_dir_t *dir)
+{
+ if(dir->search != -1) {
+ _findclose(dir->search);
+ dir->search = -1;
+ }
+}
+
+#else /* !_WIN32 */
+/******** Dir: UNIX CODE ********/
+
+void
+sci_init_dir(sci_dir_t *dir)
+{
+ dir->dir = NULL;
+ dir->mask_copy = NULL;
+}
+
+char *
+sci_find_first(sci_dir_t *dir, const char *mask)
+{
+ if (dir->dir)
+ closedir(dir->dir);
+
+ if (!(dir->dir = opendir("."))) {
+ sciprintf("%s, L%d: opendir(\".\") failed!\n", __FILE__, __LINE__);
+ return NULL;
+ }
+
+ dir->mask_copy = sci_strdup(mask);
+
+ return sci_find_next(dir);
+}
+
+#ifndef FNM_CASEFOLD
+#define FNM_CASEFOLD 0
+#warning "File searches will not be case-insensitive!"
+#endif
+
+char *
+sci_find_next(sci_dir_t *dir)
+{
+ struct dirent *match;
+
+ while ((match = readdir(dir->dir))) {
+ if (match->d_name[0] == '.')
+ continue;
+
+ if (!fnmatch(dir->mask_copy, match->d_name, FNM_CASEFOLD))
+ return match->d_name;
+ }
+
+ sci_finish_find(dir);
+ return NULL;
+}
+
+void
+sci_finish_find(sci_dir_t *dir)
+{
+ if (dir->dir) {
+ closedir(dir->dir);
+ dir->dir = NULL;
+ free(dir->mask_copy);
+ dir->mask_copy = NULL;
+ }
+}
+
+#endif /* !_WIN32 */
+
+/************* /Directory entities *************/
+
+
+int
+sci_mkpath(const char *path)
+{
+ const char *path_position = path;
+ char *next_separator = NULL;
+
+ if (chdir(G_DIR_SEPARATOR_S)) { /* Go to root */
+ sciprintf("Error: Could not change to root directory '%s'!\n",
+ G_DIR_SEPARATOR_S);
+ return -1;
+ }
+
+ do {
+ if (next_separator)
+ *next_separator = G_DIR_SEPARATOR_S[0];
+ next_separator = (char *)strchr(path_position, G_DIR_SEPARATOR_S[0]);
+
+ if (next_separator)
+ *next_separator = 0;
+
+ if (*path_position) { /* Unless we're at the first slash... */
+ if (chdir(path_position)) {
+ if (scimkdir(path_position, 0700) || chdir(path_position)) {
+ sciprintf("Error: Could not create subdirectory '%s' in",
+ path_position);
+ if (next_separator)
+ *next_separator = G_DIR_SEPARATOR_S[0];
+ sciprintf(" '%s'!\n", path);
+ return -2;
+ }
+ }
+ }
+ path_position = next_separator + 1;
+
+ } while (next_separator);
+
+ return 0;
+}
+
+
+
+char *
+sci_get_homedir(void)
+{
+#ifdef _WIN32
+ char *_path_buf = (char*)malloc(MAX_PATH);
+ char *dr = getenv("HOMEDRIVE");
+ char *path = getenv("HOMEPATH");
+
+ if (!dr || !path)
+ return getenv("WINDIR");
+
+ strncpy(_path_buf, dr, 4);
+ strncat(_path_buf, path, MAX_PATH - 4);
+
+ return _path_buf;
+#elif defined(__unix__) || !defined(X_DISPLAY_MISSING) || defined (__BEOS__) || defined(MACOSX)
+ return getenv("HOME");
+#elif defined(_DREAMCAST)
+ return NULL;
+#elif defined(__amigaos4__)
+ return "/PROGDIR/";
+#else
+# error Please add a $HOME policy for your platform!
+#endif
+}
+
+
+sci_queue_t *
+sci_init_queue(sci_queue_t *queue)
+{
+ queue->start = queue->end = NULL;
+ return queue;
+}
+
+sci_queue_t *
+sci_add_to_queue(sci_queue_t *queue, void *data, int type)
+{
+ sci_queue_node_t *node = (sci_queue_node_t*)sci_malloc(sizeof(sci_queue_node_t));
+
+ node->next = NULL;
+ node->data = data;
+ node->type = type;
+
+ if (queue->start)
+ queue->start->next = node;
+
+ queue->start = node;
+
+ if (!queue->end)
+ queue->end = node;
+
+ return queue;
+}
+
+void *
+sci_get_from_queue(sci_queue_t *queue, int *type)
+{
+ sci_queue_node_t *node = queue->end;
+ if (node) {
+ void *retval = node->data;
+ if (type)
+ *type = node->type;
+
+ queue->end = node->next;
+
+ if (queue->end == NULL) /* Queue empty? */
+ queue->start = NULL;
+
+ free(node);
+ return retval;
+ }
+ return NULL;
+}
+
+
+/*-- Yielding to the scheduler --*/
+
+#ifdef HAVE_SCHED_YIELD
+# include <sched.h>
+
+void
+sci_sched_yield(void)
+{
+ sched_yield();
+}
+
+#elif defined (_DREAMCAST)
+
+void
+sci_sched_yield()
+{
+ thd_pass();
+}
+
+#elif defined (__BEOS__)
+
+void
+sci_sched_yield()
+{
+ snooze(0);
+}
+
+#elif defined (_WIN32)
+
+void
+sci_sched_yield()
+{
+ sleep(1);
+}
+
+#else
+
+void
+sci_sched_yield()
+{
+}
+
+#endif /* !HAVE_SCHED_YIELD */
+
+
+char *
+_fcaseseek(const char *fname, sci_dir_t *dir)
+/* Expects *dir to be uninitialized and the caller to
+ ** free it afterwards */
+{
+ char *buf, *iterator;
+ char _buf[14];
+ char *retval = NULL, *name;
+
+#ifdef _MSC_VER
+ return fname;
+#endif
+
+ if (strchr(fname, G_DIR_SEPARATOR)) {
+ fprintf(stderr, "_fcaseseek() does not support subdirs\n");
+ BREAKPOINT();
+ }
+
+ if (strlen(fname) > 12) /* not a DOS file? */
+ buf = (char*)sci_malloc(strlen(fname) + 1);
+ else
+ buf = _buf;
+
+ sci_init_dir(dir);
+
+ /* Replace all letters with '?' chars */
+ strcpy(buf, fname);
+ iterator = buf;
+ while (*iterator) {
+ if (isalpha(*iterator))
+ *iterator = '?';
+ iterator++;
+ }
+
+ name = sci_find_first(dir, buf);
+
+ while (name && !retval) {
+ if (!strcasecmp(fname, name))
+ retval = name;
+ else
+ name = sci_find_next(dir);
+ }
+
+ if (strlen(fname) > 12)
+ free(buf);
+
+ return retval;
+}
+
+
+FILE *
+sci_fopen(const char *fname, const char *mode)
+{
+ sci_dir_t dir;
+ char *name = _fcaseseek(fname, &dir);
+ FILE *file = NULL;
+
+ if (name)
+ file = fopen(name, mode);
+ else if (strchr(mode, 'w'))
+ file = fopen(fname, mode);
+
+ sci_finish_find(&dir); /* Free memory */
+
+ return file;
+}
+
+int
+sci_open(const char *fname, int flags)
+{
+ sci_dir_t dir;
+ char *name;
+ int file = SCI_INVALID_FD;
+ char *separator_position;
+ char *path;
+ char *caller_cwd;
+
+ sci_init_dir(&dir);
+
+ separator_position = (char *)strrchr(fname, G_DIR_SEPARATOR);
+ if (separator_position)
+ {
+ path = (char *) malloc(separator_position-fname+1);
+ path[separator_position-fname] = 0;
+ strncpy(path, fname, separator_position-fname);
+ chdir(path);
+ free(path);
+ }
+
+ name = _fcaseseek(separator_position ? separator_position + 1 : fname, &dir);
+ if (name)
+ file = open(name, flags);
+
+ sci_finish_find(&dir); /* Free memory */
+
+ caller_cwd = sci_getcwd();
+ chdir(caller_cwd);
+ free(caller_cwd);
+
+ return file;
+}
+
+char *
+sci_getcwd(void)
+{
+ int size = 0;
+ char *cwd = NULL;
+
+ while (size < 8192) {
+ size += 256;
+ cwd = (char*)sci_malloc(size);
+ if (getcwd(cwd, size-1))
+ return cwd;
+
+ sci_free(cwd);
+ }
+
+ fprintf(stderr,"Could not determine current working directory!\n");
+ return NULL;
+}
+
+#ifdef _DREAMCAST
+
+int
+sci_fd_size(int fd)
+{
+ return fs_total(fd);
+}
+
+int
+sci_file_size(const char *fname)
+{
+ int fd = fs_open(fname, O_RDONLY);
+ int retval = -1;
+
+ if (fd != 0) {
+ retval = sci_fd_size(fd);
+ fs_close(fd);
+ }
+
+ return retval;
+}
+
+#else
+
+int
+sci_fd_size(int fd)
+{
+ struct stat fd_stat;
+ if (fstat(fd, &fd_stat)) return -1;
+ return fd_stat.st_size;
+}
+
+int
+sci_file_size(const char *fname)
+{
+ struct stat fn_stat;
+ if (stat(fname, &fn_stat)) return -1;
+ return fn_stat.st_size;
+}
+
+#endif
+
+/* Simple heuristic to work around array handling peculiarity in SQ4:
+It uses StrAt() to read the individual elements, so we must determine
+whether a string is really a string or an array. */
+int is_print_str(char *str)
+{
+ int printable = 0;
+ int len = strlen(str);
+
+ if (len == 0) return 1;
+
+ while (*str)
+ {
+ if (isprint(*str)) printable++;
+ str++;
+ }
+
+ return ((float) printable / (float) len >= 0.5);
+}