/*************************************************************************** Kfile.c Copyright (C) 1999 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] ***************************************************************************/ #include "sci/include/engine.h" #ifdef _WIN32 #define MAX_PATHLEN MAXPATHLEN # ifndef PATH_MAX # define PATH_MAX 255 # endif # define WIN32_LEAN_AND_MEAN # include # include # include #elif defined (_DREAMCAST) # include #endif #ifndef O_BINARY #define O_BINARY 0 #endif static int _savegame_indices_nr = -1; /* means 'uninitialized' */ static struct _savegame_index_struct { int id; long timestamp; } _savegame_indices[MAX_SAVEGAME_NR]; /* This assumes modern stream implementations. It may break on DOS. */ /* Attempts to mirror a file by copying it from the resource firectory ** to the working directory. Returns NULL if the file didn't exist. ** Otherwise, the new file is then opened for reading or writing. */ static FILE * f_open_mirrored(state_t *s, char *fname) { int fd; char *buf = NULL; int fsize; chdir(s->resource_dir); fd = sci_open(fname, O_RDONLY | O_BINARY); if (!IS_VALID_FD(fd)) { chdir(s->work_dir); return NULL; } fsize = sci_fd_size(fd); if (fsize > 0) { buf = (char*)sci_malloc(fsize); read(fd, buf, fsize); } close(fd); chdir(s->work_dir); /* Visual C++ doesn't allow to specify O_BINARY with creat() */ #ifdef _MSC_VER fd = _open(fname, O_CREAT | O_BINARY | O_RDWR, S_IREAD | S_IWRITE); #else fd = creat(fname, 0600); #endif if (!IS_VALID_FD(fd) && buf) { free(buf); sciprintf("kfile.c: f_open_mirrored(): Warning: Could not create '%s' in '%s' (%d bytes to copy)\n", fname, s->work_dir, fsize); return NULL; } if (fsize) { int ret; ret = write(fd, buf, fsize); if (ret < fsize) { sciprintf("kfile.c: f_open_mirrored(): Warning: Could not write all %ld bytes to '%s' in '%s' (only wrote %ld)\n", (long)fsize, fname, s->work_dir, ret); } free(buf); } close(fd); return sci_fopen(fname, "r" FO_BINARY "+"); } #define _K_FILE_MODE_OPEN_OR_CREATE 0 #define _K_FILE_MODE_OPEN_OR_FAIL 1 #define _K_FILE_MODE_CREATE 2 void file_open(state_t *s, char *filename, int mode) { int retval = 1; /* Ignore file_handles[0] */ FILE *file = NULL; SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode); if ((mode == _K_FILE_MODE_OPEN_OR_FAIL) || (mode == _K_FILE_MODE_OPEN_OR_CREATE)) { file = sci_fopen(filename, "r" FO_BINARY "+"); /* Attempt to open existing file */ SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode); if (!file) { SCIkdebug(SCIkFILE, "Failed. Attempting to copy from resource dir...\n"); file = f_open_mirrored(s, filename); if (file) SCIkdebug(SCIkFILE, "Success!\n"); else SCIkdebug(SCIkFILE, "Not found.\n"); } } if ((!file) && ((mode == _K_FILE_MODE_OPEN_OR_CREATE) || (mode == _K_FILE_MODE_CREATE))) { file = sci_fopen(filename, "w" FO_BINARY "+"); /* Attempt to create file */ SCIkdebug(SCIkFILE, "Creating file %s with mode %d\n", filename, mode); } if (!file) { /* Failed */ SCIkdebug(SCIkFILE, "file_open() failed\n"); s->r_acc = make_reg(0, 0xffff); return; } while (s->file_handles[retval] && (retval < s->file_handles_nr)) retval++; if (retval == s->file_handles_nr) /* Hit size limit => Allocate more space */ s->file_handles = (FILE**)sci_realloc(s->file_handles, sizeof(FILE *) * ++(s->file_handles_nr)); s->file_handles[retval] = file; s->r_acc = make_reg(0, retval); } reg_t kFOpen(state_t *s, int funct_nr, int argc, reg_t *argv) { char *name = kernel_dereference_char_pointer(s, argv[0], 0); int mode = UKPV(1); file_open(s, name, mode); return s->r_acc; } void file_close(state_t *s, int handle) { SCIkdebug(SCIkFILE, "Closing file %d\n", handle); if (handle == 0) { SCIkwarn(SCIkERROR, "Attempt to close file handle 0\n"); return; } if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { SCIkwarn(SCIkERROR, "Attempt to close invalid/unused file handle %d\n", handle); return; } fclose(s->file_handles[handle]); s->file_handles[handle] = NULL; } reg_t kFClose(state_t *s, int funct_nr, int argc, reg_t *argv) { file_close(s, UKPV(0)); return s->r_acc; } void fputs_wrapper(state_t *s, int handle, int size, char *data) { SCIkdebug(SCIkFILE, "FPuts'ing \"%s\" to handle %d\n", data, handle); if (handle == 0) { SCIkwarn(SCIkERROR, "Attempt to write to file handle 0\n"); return; } if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { SCIkwarn(SCIkERROR, "Attempt to write to invalid/unused file handle %d\n", handle); return; } fwrite(data, 1, size, s->file_handles[handle]); } void fwrite_wrapper(state_t *s, int handle, char *data, int length) { SCIkdebug(SCIkFILE, "fwrite()'ing \"%s\" to handle %d\n", data, handle); if (handle == 0) { SCIkwarn(SCIkERROR, "Attempt to write to file handle 0\n"); return; } if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { SCIkwarn(SCIkERROR, "Attempt to write to invalid/unused file handle %d\n", handle); return; } fwrite(data, 1, length, s->file_handles[handle]); } reg_t kFPuts(state_t *s, int funct_nr, int argc, reg_t *argv) { int handle = UKPV(0); char *data = kernel_dereference_char_pointer(s, argv[1], 0); fputs_wrapper(s, handle, strlen(data), data); return s->r_acc; } static void fgets_wrapper(state_t *s, char *dest, int maxsize, int handle) { SCIkdebug(SCIkFILE, "FGets'ing %d bytes from handle %d\n", maxsize, handle); if (handle == 0) { SCIkwarn(SCIkERROR, "Attempt to read from file handle 0\n"); return; } if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { SCIkwarn(SCIkERROR, "Attempt to read from invalid/unused file handle %d\n", handle); return; } fgets(dest, maxsize, s->file_handles[handle]); SCIkdebug(SCIkFILE, "FGets'ed \"%s\"\n", dest); } static void fread_wrapper(state_t *s, char *dest, int bytes, int handle) { SCIkdebug(SCIkFILE, "fread()'ing %d bytes from handle %d\n", bytes, handle); if (handle == 0) { SCIkwarn(SCIkERROR, "Attempt to read from file handle 0\n"); return; } if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { SCIkwarn(SCIkERROR, "Attempt to read from invalid/unused file handle %d\n", handle); return; } s->r_acc=make_reg(0,fread(dest, 1, bytes, s->file_handles[handle])); } static void fseek_wrapper(state_t *s, int handle, int offset, int whence) { if (handle == 0) { SCIkwarn(SCIkERROR, "Attempt seek on file handle 0\n"); return; } if ((handle >= s->file_handles_nr) || (s->file_handles[handle] == NULL)) { SCIkwarn(SCIkERROR, "Attempt seek on invalid/unused file handle %d\n", handle); return; } s->r_acc=make_reg(0, fseek(s->file_handles[handle], offset, whence)); } static char * _chdir_savedir(state_t *s) { char *cwd = sci_getcwd(); char *save_dir = kernel_dereference_char_pointer(s, make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR), 0); if (chdir(save_dir) && sci_mkpath(save_dir)) { sciprintf(__FILE__": Can't chdir to savegame dir '%s' or " "create it\n", save_dir); sci_free(cwd); return NULL; } if (!cwd) cwd = strdup(s->work_dir); return cwd; /* Potentially try again */ } static void _chdir_restoredir(char *dir) { if (chdir(dir)) { sciprintf(__FILE__": Can't seem to return to previous homedir '%s'\n", dir); } free(dir); } #define TEST_DIR_OR_QUIT(dir) if (!dir) { return NULL_REG; } reg_t kFGets(state_t *s, int funct_nr, int argc, reg_t *argv) { char *dest = kernel_dereference_char_pointer(s, argv[0], 0); int maxsize = UKPV(1); int handle = UKPV(2); fgets_wrapper(s, dest, maxsize, handle); return argv[0]; } /* kGetCWD(address): ** Writes the cwd to the supplied address and returns the address in acc. */ reg_t kGetCWD(state_t *s, int funct_nr, int argc, reg_t *argv) { char *wd = sci_getcwd(); char *targetaddr = kernel_dereference_char_pointer(s, argv[0], 0); strncpy(targetaddr, wd, MAX_SAVE_DIR_SIZE - 1); targetaddr[MAX_SAVE_DIR_SIZE - 1] = 0; /* Terminate */ SCIkdebug(SCIkFILE, "Copying cwd='%s'(%d chars) to %p", wd, strlen(wd), targetaddr); free(wd); return argv[0]; } /* Returns a dynamically allocated pointer to the name of the requested save dir */ char * _k_get_savedir_name(int nr) { char suffices[] = "0123456789abcdefghijklmnopqrstuvwxyz"; char *savedir_name = (char*)sci_malloc(strlen(FREESCI_SAVEDIR_PREFIX) + 2); assert(nr >= 0); assert(nr < MAX_SAVEGAME_NR); strcpy(savedir_name, FREESCI_SAVEDIR_PREFIX); savedir_name[strlen(FREESCI_SAVEDIR_PREFIX)] = suffices[nr]; savedir_name[strlen(FREESCI_SAVEDIR_PREFIX) + 1] = 0; return savedir_name; } void delete_savegame(state_t *s, int savedir_nr) { char *workdir = _chdir_savedir(s); char *savedir = _k_get_savedir_name(savedir_nr); char buffer[256]; sciprintf("Deleting savegame '%s'\n", savedir); sprintf(buffer, "%s/%s.id", savedir, s->game_name); sci_unlink(buffer); sprintf(buffer, "%s/state", savedir); sci_unlink(buffer); sci_rmdir(savedir); free(savedir); _chdir_restoredir(workdir); } #define K_DEVICE_INFO_GET_DEVICE 0 #define K_DEVICE_INFO_GET_CURRENT_DEVICE 1 #define K_DEVICE_INFO_PATHS_EQUAL 2 #define K_DEVICE_INFO_IS_FLOPPY 3 #define K_DEVICE_INFO_GET_SAVECAT_NAME 7 #define K_DEVICE_INFO_GET_SAVEFILE_NAME 8 #ifdef _WIN32 reg_t kDeviceInfo_Win32(state_t *s, int funct_nr, int argc, reg_t *argv) { char dir_buffer [MAXPATHLEN], dir_buffer2 [MAX_PATHLEN]; int mode = UKPV(0); switch(mode) { case K_DEVICE_INFO_GET_DEVICE: { char *input_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); char *output_s = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0); GetFullPathName (input_s, sizeof (dir_buffer)-1, dir_buffer, NULL); strncpy(output_s, dir_buffer, 2); output_s [2] = 0; } break; case K_DEVICE_INFO_GET_CURRENT_DEVICE: { char *output_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); _getcwd (dir_buffer, sizeof (dir_buffer)-1); strncpy(output_s, dir_buffer, 2); output_s [2] = 0; } break; case K_DEVICE_INFO_PATHS_EQUAL: { char *path1_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); char *path2_s = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0); GetFullPathName (path1_s, sizeof (dir_buffer)-1, dir_buffer, NULL); GetFullPathName (path2_s, sizeof (dir_buffer2)-1, dir_buffer2, NULL); #ifdef _MSC_VER return make_reg(0, !stricmp (path1_s, path2_s)); #else return make_reg(0, !strcasecmp (path1_s, path2_s)); #endif } break; case K_DEVICE_INFO_IS_FLOPPY: { char *input_s = (char*)kernel_dereference_bulk_pointer(s, argv[1], 0); GetFullPathName (input_s, sizeof (dir_buffer)-1, dir_buffer, NULL); dir_buffer [3] = 0; /* leave X:\ */ return make_reg(0, GetDriveType (dir_buffer) == DRIVE_REMOVABLE); } break; /* SCI uses these in a less-than-portable way to delete savegames. ** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html ** for more information on our workaround for this. */ case K_DEVICE_INFO_GET_SAVECAT_NAME: { char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0); sprintf(output_buffer, "%s/__throwaway", s->work_dir); } break; case K_DEVICE_INFO_GET_SAVEFILE_NAME: { char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0); int savegame_id = UKPV(3); sprintf(output_buffer, "%s/__throwaway", s->work_dir); delete_savegame(s, savegame_id); } break; default: { SCIkwarn(SCIkERROR, "Unknown DeviceInfo() sub-command: %d\n", mode); } } return s->r_acc; } #else /* !_WIN32 */ reg_t kDeviceInfo_Unix(state_t *s, int funct_nr, int argc, reg_t *argv) { int mode = UKPV(0); switch(mode) { case K_DEVICE_INFO_GET_DEVICE: { char *output_s = kernel_dereference_char_pointer(s, argv[2], 0); strcpy(output_s, "/"); } break; case K_DEVICE_INFO_GET_CURRENT_DEVICE: { char *output_s = kernel_dereference_char_pointer(s, argv[1], 0); strcpy(output_s, "/"); } break; case K_DEVICE_INFO_PATHS_EQUAL: { char *path1_s = kernel_dereference_char_pointer(s, argv[1], 0); char *path2_s = kernel_dereference_char_pointer(s, argv[2], 0); #ifndef HAVE_FNMATCH_H #ifndef _DOS # warning "File matches will be unprecise!" #endif return make_reg(0, !strcmp(path1_s, path2_s)); #else return make_reg(0, fnmatch(path1_s, path2_s, FNM_PATHNAME) /* POSIX.2 */); #endif } break; case K_DEVICE_INFO_IS_FLOPPY: { return NULL_REG; /* Never */ } break; /* SCI uses these in a less-than-portable way to delete savegames. ** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html ** for more information on our workaround for this. */ case K_DEVICE_INFO_GET_SAVECAT_NAME: { char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); /* char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);*/ sprintf(output_buffer, "%s/__throwaway", s->work_dir); } break; case K_DEVICE_INFO_GET_SAVEFILE_NAME: { char *output_buffer = kernel_dereference_char_pointer(s, argv[1], 0); /* char *game_prefix = kernel_dereference_char_pointer(s, argv[2], 0);*/ int savegame_id = UKPV(3); sprintf(output_buffer, "%s/__throwaway", s->work_dir); delete_savegame(s, savegame_id); } break; default: { SCIkwarn(SCIkERROR, "Unknown DeviceInfo() sub-command: %d\n", mode); } } return s->r_acc; } #endif /* !_WIN32 */ reg_t kGetSaveDir(state_t *s, int funct_nr, int argc, reg_t *argv) { return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); } reg_t kCheckFreeSpace(state_t *s, int funct_nr, int argc, reg_t *argv) { char *path = kernel_dereference_char_pointer(s, argv[0], 0); char *testpath = (char*)sci_malloc(strlen(path) + 15); char buf[1024]; int i; int fd; int failed = 0; int pathlen; strcpy(testpath, path); strcat(testpath, "freesci.foo"); pathlen = strlen(testpath); while (IS_VALID_FD(fd = open(testpath, O_RDONLY))) { close(fd); if (testpath[pathlen - 2] == 'z') { /* Failed. */ SCIkwarn(SCIkWARNING, "Failed to find non-existing file for free space test\n"); free(testpath); return NULL_REG; } /* If this file couldn't be created, try freesci.fop, freesci.foq etc., ** then freesci.fpa, freesci.fpb. Stop at freesci.fza. ** Yes, this is extremely arbitrary and very strange. */ if (testpath[pathlen - 1] == 'z') { testpath[pathlen - 1] = 'a'; ++testpath[pathlen - 2]; } else ++testpath[pathlen - 1]; } fd = creat(testpath, 0600); if (!IS_VALID_FD(fd)) { SCIkwarn(SCIkWARNING,"Could not test for disk space: %s\n", strerror(errno)); SCIkwarn(SCIkWARNING,"Test path was '%s'\n", testpath); free(testpath); return NULL_REG; } memset(buf, 0, sizeof(buf)); for (i = 0; i < 1024; i++) /* Check for 1 MB */ if (write(fd, buf, 1024) < 1024) failed = 1; close(fd); remove(testpath); sci_free(testpath); return make_reg(0, !failed); } int _k_check_file(char *filename, int minfilesize) /* Returns 0 if the file exists and is big enough */ { return (sci_file_size(filename) < minfilesize); } int _k_find_savegame_by_name(char *game_id_file, char *name) { int savedir_nr = -1; int i; char *buf = NULL; for (i = 0; i < MAX_SAVEGAME_NR; i++) { if (!chdir((buf = _k_get_savedir_name(i)))) { char namebuf[32]; /* Save game name buffer */ FILE *idfile = sci_fopen(game_id_file, "r"); if (idfile) { fgets(namebuf, 31, idfile); if (strlen(namebuf) > 0) if (namebuf[strlen(namebuf) - 1] == '\n') namebuf[strlen(namebuf) - 1] = 0; /* Remove trailing newlines */ if (strcmp(name, namebuf) == 0) { sciprintf("Save game name matched entry %d\n", i); savedir_nr = i; } fclose(idfile); } chdir(".."); } free(buf); } return 0; } #ifdef _DREAMCAST static long get_file_mtime(int fd) { /* FIXME (Dreamcast): Not yet implemented */ return 0; } #else #define get_file_mtime(fd) get_file_mtime_Unix(fd) /* Returns the time of the specified file's last modification ** Parameters: (int) fd: The file descriptor of the file in question ** Returns : (long) An integer value describing the time of the ** file's last modification. ** The only thing that must be ensured is that ** get_file_mtime(f1) > get_file_mtime(f2) ** <=> ** if f1 was modified at a later point in time than the last time ** f2 was modified. */ static long get_file_mtime_Unix(int fd) /* returns the */ { struct stat fd_stat; fstat(fd, &fd_stat); return fd_stat.st_ctime; } #endif static int _savegame_index_struct_compare(const void *a, const void *b) { return ((struct _savegame_index_struct *)b)->timestamp - ((struct _savegame_index_struct *)a)->timestamp; } static void update_savegame_indices(char *gfname) { int i; _savegame_indices_nr = 0; for (i = 0; i < MAX_SAVEGAME_NR; i++) { char *dirname = _k_get_savedir_name(i); int fd; if (!chdir(dirname)) { if (IS_VALID_FD(fd = sci_open(gfname, O_RDONLY))) { _savegame_indices[_savegame_indices_nr].id = i; _savegame_indices[_savegame_indices_nr++].timestamp = get_file_mtime(fd); close(fd); } chdir(".."); } free(dirname); } qsort(_savegame_indices, _savegame_indices_nr, sizeof(struct _savegame_index_struct), _savegame_index_struct_compare); } int test_savegame(state_t *s, char *savegame_id, char *savegame_name, int savegame_name_length) { FILE *f; char buffer[80]; int version = -1; chdir(savegame_id); f = fopen("state", "r"); if (!f) return 0; while (!feof(f)) { char *seeker; fgets(buffer, sizeof(buffer), f); if ((seeker = strstr(buffer, "savegame_version = ")) != NULL) { seeker += strlen("savegame_version = "); version = strtol(seeker, NULL, 10); break; } } fclose(f); return version >= FREESCI_MINIMUM_SAVEGAME_VERSION && version <= FREESCI_CURRENT_SAVEGAME_VERSION; } reg_t kCheckSaveGame(state_t *s, int funct_nr, int argc, reg_t *argv) { char *game_id = kernel_dereference_char_pointer(s, argv[0], 0); int savedir_nr = UKPV(1); char *buf = NULL; char *workdir = _chdir_savedir(s); TEST_DIR_OR_QUIT(workdir); if (_savegame_indices_nr < 0) { char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1); strcpy(game_id_file_name, game_id); strcat(game_id_file_name, FREESCI_ID_SUFFIX); SCIkwarn(SCIkWARNING, "Savegame index list not initialized!\n"); update_savegame_indices(game_id_file_name); } savedir_nr = _savegame_indices[savedir_nr].id; if (savedir_nr > MAX_SAVEGAME_NR-1) { _chdir_restoredir(workdir); return NULL_REG; } s->r_acc = make_reg(0, test_savegame(s, (buf = _k_get_savedir_name(savedir_nr)), NULL, 0)); _chdir_restoredir(workdir); free(buf); return s->r_acc; } reg_t kGetSaveFiles(state_t *s, int funct_nr, int argc, reg_t *argv) { char *game_id = kernel_dereference_char_pointer(s, argv[0], 0); char *nametarget = kernel_dereference_char_pointer(s, argv[1], 0); reg_t nametarget_base = argv[1]; reg_t *nameoffsets = kernel_dereference_reg_pointer(s, argv[2], 0); int gfname_len = strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1; char *gfname = (char*)sci_malloc(gfname_len); int i; char *workdir = _chdir_savedir(s); TEST_DIR_OR_QUIT(workdir); strcpy(gfname, game_id); strcat(gfname, FREESCI_ID_SUFFIX); /* This file is used to identify in-game savegames */ update_savegame_indices(gfname); s->r_acc = NULL_REG; for (i = 0; i < _savegame_indices_nr; i++) { char *savedir_name = _k_get_savedir_name(_savegame_indices[i].id); FILE *idfile; if (!chdir(savedir_name)) { if ((idfile = sci_fopen(gfname, "r"))) { /* Valid game ID file: Assume valid game */ char namebuf[SCI_MAX_SAVENAME_LENGTH]; /* Save game name buffer */ fgets(namebuf, SCI_MAX_SAVENAME_LENGTH-1, idfile); if (strlen(namebuf) > 0) { if (namebuf[strlen(namebuf) - 1] == '\n') namebuf[strlen(namebuf) - 1] = 0; /* Remove trailing newline */ *nameoffsets = s->r_acc; /* Store savegame ID */ ++s->r_acc.offset; /* Increase number of files found */ nameoffsets++; /* Make sure the next ID string address is written to the next pointer */ strncpy(nametarget, namebuf, SCI_MAX_SAVENAME_LENGTH); /* Copy identifier string */ *(nametarget + SCI_MAX_SAVENAME_LENGTH - 1) = 0; /* Make sure it's terminated */ nametarget += SCI_MAX_SAVENAME_LENGTH; /* Increase name offset pointer accordingly */ nametarget_base.offset += SCI_MAX_SAVENAME_LENGTH; fclose(idfile); } } chdir(".."); } free(savedir_name); } free(gfname); *nametarget = 0; /* Terminate list */ _chdir_restoredir(workdir); return s->r_acc; } reg_t kSaveGame(state_t *s, int funct_nr, int argc, reg_t *argv) { char *game_id = (char*)kernel_dereference_bulk_pointer(s, argv[0], 0); char *savegame_dir; int savedir_nr = UKPV(1); int savedir_id; /* Savegame ID, derived from savedir_nr and the savegame ID list */ char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1); char *game_description = (char*)kernel_dereference_bulk_pointer(s, argv[2], 0); char *workdir = _chdir_savedir(s); char *version = argc > 3 ? strdup((char*)kernel_dereference_bulk_pointer(s, argv[3], 0)) : NULL; TEST_DIR_OR_QUIT(workdir); s->game_version = version; strcpy(game_id_file_name, game_id); strcat(game_id_file_name, FREESCI_ID_SUFFIX); update_savegame_indices(game_id_file_name); if (savedir_nr >= 0 && savedir_nr < _savegame_indices_nr) /* Overwrite */ savedir_id = _savegame_indices[savedir_nr].id; else if (savedir_nr >= 0 && savedir_nr < MAX_SAVEGAME_NR) { int i = 0; savedir_id = 0; /* First, look for holes */ while (i < _savegame_indices_nr) if (_savegame_indices[i].id == savedir_id) { ++savedir_id; i = 0; } else ++i; if (savedir_id >= MAX_SAVEGAME_NR) { sciprintf("Internal error: Free savegame ID is %d, shouldn't happen!\n", savedir_id); return NULL_REG; } /* This loop terminates when savedir_id is not in [x | ex. n. _savegame_indices[n].id = x] */ } else { sciprintf("Savegame ID %d is not allowed!\n", savedir_nr); return NULL_REG; } savegame_dir = _k_get_savedir_name(savedir_id); if (gamestate_save(s, savegame_dir)) { sciprintf("Saving the game failed.\n"); s->r_acc = NULL_REG; } else { FILE *idfile; chdir(savegame_dir); if ((idfile = sci_fopen(game_id_file_name, "w"))) { fprintf(idfile, game_description); fclose(idfile); } else { sciprintf("Creating the game ID file failed.\n"); sciprintf("You can still restore from inside the debugger with \"restore_game %s\"\n", savegame_dir); s->r_acc = NULL_REG; } chdir (".."); s->r_acc = make_reg(0, 1); } free(game_id_file_name); _chdir_restoredir(workdir); free(s->game_version); s->game_version = NULL; return s->r_acc; } reg_t kRestoreGame(state_t *s, int funct_nr, int argc, reg_t *argv) { char *game_id = (char*)kernel_dereference_bulk_pointer(s, argv[0], 0); int savedir_nr = UKPV(1); char *workdir = _chdir_savedir(s); TEST_DIR_OR_QUIT(workdir); if (_savegame_indices_nr < 0) { char *game_id_file_name = (char*)sci_malloc(strlen(game_id) + strlen(FREESCI_ID_SUFFIX) + 1); strcpy(game_id_file_name, game_id); strcat(game_id_file_name, FREESCI_ID_SUFFIX); SCIkwarn(SCIkWARNING, "Savegame index list not initialized!\n"); update_savegame_indices(game_id_file_name); } savedir_nr = _savegame_indices[savedir_nr].id; if (savedir_nr > -1) { char *savedir_name = _k_get_savedir_name(savedir_nr); state_t *newstate = gamestate_restore(s, savedir_name); free(savedir_name); if (newstate) { s->successor = newstate; script_abort_flag = SCRIPT_ABORT_WITH_REPLAY; /* Abort current game */ s->execution_stack_pos = s->execution_stack_base; } else { s->r_acc = make_reg(0, 1); sciprintf("Restoring failed (game_id = '%s').\n", game_id); } } else { s->r_acc = make_reg(0, 1); sciprintf("Savegame #%d not found!\n", savedir_nr); } _chdir_restoredir(workdir); return s->r_acc; } reg_t kValidPath(state_t *s, int funct_nr, int argc, reg_t *argv) { char *pathname = kernel_dereference_char_pointer(s, argv[0], 0); char cpath[MAXPATHLEN + 1]; getcwd(cpath, MAXPATHLEN + 1); s->r_acc = make_reg(0, !chdir(pathname)); /* Try to go there. If it works, return 1, 0 otherwise. */ chdir(cpath); return s->r_acc; } #define K_FILEIO_OPEN 0 #define K_FILEIO_CLOSE 1 #define K_FILEIO_READ_RAW 2 #define K_FILEIO_WRITE_RAW 3 #define K_FILEIO_UNLINK 4 #define K_FILEIO_READ_STRING 5 #define K_FILEIO_WRITE_STRING 6 #define K_FILEIO_SEEK 7 #define K_FILEIO_FIND_FIRST 8 #define K_FILEIO_FIND_NEXT 9 #define K_FILEIO_STAT 10 char * write_filename_to_mem(state_t *s, reg_t address, char *string) { char *mem = kernel_dereference_char_pointer(s, address, 0); if (string) { memset(mem, 0, 13); strncpy(mem, string, 12); } return string; } void next_file(state_t *s) { if (write_filename_to_mem(s, s->dirseeker_outbuffer, sci_find_next(&(s->dirseeker)))) s->r_acc = s->dirseeker_outbuffer; else s->r_acc = NULL_REG; } void first_file(state_t *s, const char *dir, char *mask, reg_t buffer) { if (!buffer.segment) { sciprintf("Warning: first_file(state,\"%s\",\"%s\", 0) invoked!\n", dir, mask); s->r_acc = NULL_REG; return; } if (strcmp(dir, ".")) { sciprintf("%s L%d: Non-local first_file: Not implemented yet\n", __FILE__, __LINE__); s->r_acc = NULL_REG; return; } /* Get rid of the old find structure */ if (s->dirseeker_outbuffer.segment) sci_finish_find(&(s->dirseeker)); s->dirseeker_outbuffer = buffer; if (write_filename_to_mem(s, s->dirseeker_outbuffer, sci_find_first(&(s->dirseeker), mask))) s->r_acc = s->dirseeker_outbuffer; else s->r_acc = NULL_REG; } reg_t kFileIO(state_t *s, int funct_nr, int argc, reg_t *argv) { int func_nr = UKPV(0); switch (func_nr) { case K_FILEIO_OPEN : { char *name = kernel_dereference_char_pointer(s, argv[1], 0); int mode = UKPV(2); file_open(s, name, mode); break; } case K_FILEIO_CLOSE : { int handle = UKPV(1); file_close(s, handle); break; } case K_FILEIO_READ_RAW : { int handle = UKPV(1); char *dest = kernel_dereference_char_pointer(s, argv[2], 0); int size = UKPV(3); fread_wrapper(s, dest, size, handle); break; } case K_FILEIO_WRITE_RAW : { int handle = UKPV(1); char *buf = kernel_dereference_char_pointer(s, argv[2], 0); int size = UKPV(3); fwrite_wrapper(s, handle, buf, size); break; } case K_FILEIO_UNLINK : { char *name = kernel_dereference_char_pointer(s, argv[1], 0); unlink(name); break; } case K_FILEIO_READ_STRING : { char *dest = kernel_dereference_char_pointer(s, argv[1], 0); int size = UKPV(2); int handle = UKPV(3); fgets_wrapper(s, dest, size, handle); return argv[1]; } case K_FILEIO_WRITE_STRING : { int handle = UKPV(1); int size = UKPV(3); char *buf = kernel_dereference_char_pointer(s, argv[2], size); if (buf) fputs_wrapper(s, handle, size, buf); break; } case K_FILEIO_SEEK : { int handle = UKPV(1); int offset = UKPV(2); int whence = UKPV(3); fseek_wrapper(s, handle, offset, whence); break; } case K_FILEIO_FIND_FIRST : { char *mask = kernel_dereference_char_pointer(s, argv[1], 0); reg_t buf = argv[2]; /* int attr = UKPV(3); */ /* We won't use this, Win32 might, though... */ #ifndef _WIN32 if (strcmp(mask, "*.*")==0) strcpy(mask, "*"); /* For UNIX */ #endif first_file(s, ".", mask, buf); break; } case K_FILEIO_FIND_NEXT : { next_file(s); break; } case K_FILEIO_STAT : { char *name = kernel_dereference_char_pointer(s, argv[1], 0); s->r_acc=make_reg(0, 1-_k_check_file(name, 0)); break; } default : SCIkwarn(SCIkERROR, "Unknown FileIO() sub-command: %d\n", func_nr); } return s->r_acc; }