/*************************************************************************** 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 #include "sci/include/engine.h" #ifdef HAVE_SYS_TIME_H # include #endif #ifdef _MSC_VER # include # include # include # include #endif #ifdef _WIN32 # include # include 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 #endif #ifdef _DREAMCAST # include #endif #ifdef __BEOS__ # include #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 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); }