Ticket #8964: sci-file.patch
File sci-file.patch, 29.1 KB (added by , 15 years ago) |
---|
-
include/engine.h
48 48 struct menubar_t; 49 49 struct kfunct_sig_pair_t; // from kernel.h 50 50 51 class DirSeeker; 51 class DirSeeker { 52 protected: 53 EngineState *_vm; 54 reg_t _outbuffer; 55 Common::StringList _savefiles; 56 Common::StringList::const_iterator _iter; 52 57 58 public: 59 DirSeeker(EngineState *s) : _vm(s) { 60 _outbuffer = NULL_REG; 61 _iter = _savefiles.begin(); 62 } 63 64 void firstFile(const char *mask, reg_t buffer); 65 void nextFile(); 66 }; 67 53 68 #define FREESCI_CURRENT_SAVEGAME_VERSION 8 54 69 #define FREESCI_MINIMUM_SAVEGAME_VERSION 8 55 70 … … 81 96 82 97 class FileHandle { 83 98 public: 84 FILE *_file; 99 Common::String _name; 100 Common::SeekableReadStream *_in; 101 Common::WriteStream *_out; 85 102 86 FileHandle() : _file(0) { 87 } 103 public: 104 FileHandle(); 105 ~FileHandle(); 88 106 89 ~FileHandle() { 90 if (_file) 91 fclose(_file); 92 } 107 void close(); 108 bool isOpen() const; 93 109 }; 94 110 95 111 struct EngineState { … … 186 202 187 203 Common::Array<FileHandle> _fileHandles; /* Array of file handles. Dynamically increased if required. */ 188 204 189 DirSeeker *dirseeker;205 DirSeeker _dirseeker; 190 206 191 207 /* VM Information */ 192 208 -
sci.cpp
339 339 return _targetName + extension; 340 340 } 341 341 342 Common::String SciEngine::getSavegamePattern() const { 343 return _targetName + ".???"; 344 } 345 346 Common::String SciEngine::wrapFilename(const Common::String &name) const { 347 return _targetName + "-" + name; 348 } 349 350 Common::String SciEngine::unwrapFilename(const Common::String &name) const { 351 Common::String prefix = name + "-"; 352 if (name.hasPrefix(prefix.c_str())) 353 return Common::String(name.c_str() + prefix.size()); 354 return name; 355 } 356 342 357 } // End of namespace Sci -
sci.h
88 88 Common::Platform getPlatform() const; 89 89 uint32 getFlags() const; 90 90 ResourceManager *getResMgr() { return _resmgr; } 91 91 92 Common::String getSavegameName(int nr) const; 93 Common::String getSavegamePattern() const; 92 94 95 /** Prepend 'TARGET-' to the given filename. */ 96 Common::String wrapFilename(const Common::String &name) const; 97 98 /** Remove the 'TARGET-' prefix of the given filename, if present. */ 99 Common::String unwrapFilename(const Common::String &name) const; 100 101 93 102 private: 94 103 const SciGameDescription *_gameDescription; 95 104 Console *_console; -
engine/game.cpp
440 440 return 0; 441 441 } 442 442 443 EngineState::EngineState() {443 EngineState::EngineState() : _dirseeker(this) { 444 444 savegame_version = 0; 445 445 446 446 widget_serial_counter = 0; … … 530 530 531 531 _fileHandles.resize(5); 532 532 533 dirseeker = 0;534 535 533 execution_stack = 0; 536 534 execution_stack_size = 0; 537 535 execution_stack_pos = 0; … … 662 660 s->bp_list = NULL; // No breakpoints defined 663 661 s->have_bp = 0; 664 662 665 s->dirseeker = 0; // Used by FileIO for FIND_FIRST, FIND_NEXT666 667 663 if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE && 668 664 s->version < SCI_VERSION(1, 001, 000)) 669 665 s->seg_manager->setExportWidth(1); -
engine/scriptdebug.cpp
1059 1059 if (!omit_check) { 1060 1060 int result = 0; 1061 1061 for (uint i = 0; i < s->_fileHandles.size(); i++) 1062 if (s->_fileHandles[i]. _file)1062 if (s->_fileHandles[i].isOpen()) 1063 1063 result++; 1064 1064 1065 1065 if (result) { -
engine/kfile.cpp
38 38 #include "sci/include/engine.h" 39 39 #include "sci/engine/kernel.h" 40 40 41 #include <errno.h>42 #include <sys/stat.h> // for S_IREAD/S_IWRITE43 44 #define SCI_INVALID_FD -145 #define IS_VALID_FD(a) ((a) != SCI_INVALID_FD) /* Tests validity of a file descriptor */46 47 // FIXME: rework sci_dir_t to use common/fs.h and remove these includes48 #include <sys/types.h>49 #ifndef _MSC_VER50 #include <dirent.h>51 #else52 #include <io.h>53 #endif54 55 // FIXME: For chdir() etc.56 #ifndef _MSC_VER57 #include <unistd.h>58 #endif59 60 #ifdef WIN3261 # define FO_BINARY "b"62 #else63 # define FO_BINARY ""64 #endif65 66 #ifdef UNIX67 #include <fnmatch.h>68 #include <sys/stat.h>69 #endif70 71 72 41 namespace Sci { 73 42 74 43 75 struct sci_dir_t { 76 #ifdef WIN32 77 long search; 78 struct _finddata_t fileinfo; 79 #else 80 DIR *dir; 81 char *mask_copy; 82 #endif 83 }; /* used by sci_find_first and friends */ 44 /* 45 * Note on how file I/O is implemented: In ScummVM, one can not create/write 46 * arbitrary data files, simply because many of our target platforms do not 47 * support this. The only files on can create are savestates. But SCI has an 48 * opcode to create and write to seemingly 'arbitrary' files. 49 * To implement that opcode, we combine the SaveFileManager with regular file 50 * code, similarly to how the SCUMM HE engine does it. 51 * 52 * To handle opening a file called "foobar", what we do is this: First, we 53 * create an 'augmented file name', by prepending the game target and a dash, 54 * so if we running game target sq1vga, the name becomes "sq1vga-foobar". 55 * Next, we check if such a file is known to the SaveFileManager. If so, we 56 * we use that for reading/writing, delete it, whatever. 57 * 58 * If no such file is present but we were only asked to *read* the file, 59 * we fallback to looking for a regular file called "foobar", and open that 60 * for reading only. 61 * 62 * There are some caveats to this: First off, SCI apparently has no way 63 * to signal that a file is supposed to be opened for reading only. For now, 64 * we hackishly just assume that this is what _K_FILE_MODE_OPEN_OR_FAIL is for. 65 * 66 * Secondly, at least in theory, a file could be opened for both reading and 67 * writing. We currently do not support this. If it turns out that we *have* 68 * to support it, we could do it as follows: Initially open the file for 69 * reading. If a write is attempted, store the file offset, close the file, 70 * if necessary create a mirror clone (i.e., clone it into a suitably named 71 * savefile), then open the file (resp. its clone for writing) and seek to the 72 * correct position. If later a read is attempted, we again close and re-open. 73 * 74 * However, before putting any effort into implementing such an error-prone 75 * scheme, we are well advised to first determine whether any game needs this 76 * at all, and for what. Based on that, we can maybe come up with a better waybill 77 * to provide this functionality. 78 */ 79 80 84 81 85 void sci_init_dir(sci_dir_t *dirent); 86 /* Initializes an sci directory search structure 87 ** Parameters: (sci_dir_t *) dirent: The entity to initialize 88 ** Returns : (void) 89 ** The entity is initialized to "empty" values, meaning that it can be 90 ** used in subsequent sci_find_first/sci_find_next constructs. In no 91 ** event should this function be used upon a structure which has been 92 ** subjected to any of the other dirent calls. 93 */ 94 95 char *sci_find_first(sci_dir_t *dirent, const char *mask); 96 /* Finds the first file matching the specified file mask 97 ** Parameters: (sci_dir_t *) dirent: Pointer to an unused dirent structure 98 ** (const char *) mask: File mask to apply 99 ** Returns : (char *) Name of the first matching file found, or NULL 100 */ 101 102 char *sci_find_next(sci_dir_t *dirent); 103 /* Finds the next file specified by an sci_dir initialized by sci_find_first() 104 ** Parameters: (sci_dir_t *) dirent: Pointer to SCI dir entity 105 ** Returns : (char *) Name of the next matching file, or NULL 106 */ 107 108 void sci_finish_find(sci_dir_t *dirent); 109 /* Completes an 'sci_find_first/next' procedure 110 ** Parameters: (sci_dir_t *) dirent: Pointer to the dirent used 111 ** Returns : (void) 112 ** In the operation sequences 113 ** sci_init_dir(x); sci_finish_find(x); 114 ** and 115 ** sci_finish_find(x); sci_finish_find(x); 116 ** the second operation is guaranteed to be a no-op. 117 */ 118 119 FILE *sci_fopen(const char *fname, const char *mode); 120 /* Opens a FILE* case-insensitively 121 ** Parameters: (const char *) fname: Name of the file to open 122 ** (const char *) mode: Mode to open it with 123 ** Returns : (FILE *) A valid file handle, or NULL on failure 124 ** Always refers to the cwd, cannot address subdirectories 125 */ 126 127 int sci_file_size(const char *fname); 128 /* Returns the filesize of a file 129 ** Parameters: (const char *) fname: Name of file to get filesize of 130 ** Returns : (int) filesize of the file, -1 on error 131 */ 132 133 134 #if defined(WIN32) 135 void sci_init_dir(sci_dir_t *dir) { 136 dir->search = -1; 82 FileHandle::FileHandle() : _in(0), _out(0) { 137 83 } 138 84 139 char *sci_find_first(sci_dir_t *dir, const char *mask) { 140 dir->search = _findfirst(mask, &(dir->fileinfo)); 141 142 if (dir->search != -1) { 143 if (dir->fileinfo.name == NULL) { 144 return NULL; 145 } 146 147 if (strcmp(dir->fileinfo.name, ".") == 0 || 148 strcmp(dir->fileinfo.name, "..") == 0) { 149 if (sci_find_next(dir) == NULL) { 150 return NULL; 151 } 152 } 153 154 return dir->fileinfo.name; 155 } else { 156 switch (errno) { 157 case ENOENT: { 158 #ifdef _DEBUG 159 printf("_findfirst errno = ENOENT: no match\n"); 160 161 if (mask) 162 printf(" in: %s\n", mask); 163 else 164 printf(" - searching in undefined directory\n"); 165 #endif 166 break; 167 } 168 case EINVAL: { 169 printf("_findfirst errno = EINVAL: invalid filename\n"); 170 break; 171 } 172 default: 173 printf("_findfirst errno = unknown (%d)", errno); 174 } 175 } 176 177 return NULL; 85 FileHandle::~FileHandle() { 86 close(); 178 87 } 179 88 180 char *sci_find_next(sci_dir_t *dir) { 181 if (dir->search == -1) 182 return NULL; 183 184 if (_findnext(dir->search, &(dir->fileinfo)) < 0) { 185 _findclose(dir->search); 186 dir->search = -1; 187 return NULL; 188 } 189 190 if (strcmp(dir->fileinfo.name, ".") == 0 || 191 strcmp(dir->fileinfo.name, "..") == 0) { 192 if (sci_find_next(dir) == NULL) { 193 return NULL; 194 } 195 } 196 197 return dir->fileinfo.name; 89 void FileHandle::close() { 90 delete _in; 91 delete _out; 92 _in = 0; 93 _out = 0; 94 _name.clear(); 198 95 } 199 96 200 void sci_finish_find(sci_dir_t *dir) { 201 if (dir->search != -1) { 202 _findclose(dir->search); 203 dir->search = -1; 204 } 97 bool FileHandle::isOpen() const { 98 return _in || _out; 205 99 } 206 100 207 #else208 101 209 void sci_init_dir(sci_dir_t *dir) {210 dir->dir = NULL;211 dir->mask_copy = NULL;212 }213 102 214 char *sci_find_first(sci_dir_t *dir, const char *mask) {215 if (dir->dir)216 closedir(dir->dir);217 218 if (!(dir->dir = opendir("."))) {219 sciprintf("%s, L%d: opendir(\".\") failed!\n", __FILE__, __LINE__);220 return NULL;221 }222 223 dir->mask_copy = sci_strdup(mask);224 225 return sci_find_next(dir);226 }227 228 #ifndef FNM_CASEFOLD229 #define FNM_CASEFOLD 0230 #warning "File searches will not be case-insensitive!"231 #endif232 233 char *sci_find_next(sci_dir_t *dir) {234 struct dirent *match;235 236 while ((match = readdir(dir->dir))) {237 if (match->d_name[0] == '.')238 continue;239 240 if (!fnmatch(dir->mask_copy, match->d_name, FNM_CASEFOLD))241 return match->d_name;242 }243 244 sci_finish_find(dir);245 246 return NULL;247 }248 249 void sci_finish_find(sci_dir_t *dir) {250 if (dir->dir) {251 closedir(dir->dir);252 dir->dir = NULL;253 free(dir->mask_copy);254 dir->mask_copy = NULL;255 }256 }257 258 #endif259 260 261 /* Returns the case-sensitive filename of a file.262 ** Expects *dir to be uninitialized and the caller to free it afterwards.263 ** Parameters: (const char *) fname: Name of file to get case-sensitive.264 ** (sci_dir_t *) dir: Directory to find file within.265 ** Returns : (char *) Case-sensitive filename of the file.266 */267 Common::String _fcaseseek(const char *fname) {268 // Expects *dir to be uninitialized and the caller to269 // free it afterwards */270 271 // Look up the file, ignoring case272 Common::ArchiveMemberList files;273 SearchMan.listMatchingMembers(files, fname);274 275 for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {276 const Common::String name = (*x)->getName();277 if (name.equalsIgnoreCase(fname))278 return name;279 }280 281 return Common::String();282 }283 284 FILE *sci_fopen(const char *fname, const char *mode) {285 Common::String name = _fcaseseek(fname);286 FILE *file = NULL;287 288 if (!name.empty())289 file = fopen(name.c_str(), mode);290 else if (strchr(mode, 'w'))291 file = fopen(fname, mode);292 293 return file;294 }295 296 int sci_file_size(const char *fname) {297 struct stat fn_stat;298 299 if (stat(fname, &fn_stat))300 return -1;301 302 return fn_stat.st_size;303 }304 305 306 307 103 static int _savegame_indices_nr = -1; // means 'uninitialized' 308 104 309 st atic struct _savegame_index_struct{105 struct SavegameDesc { 310 106 int id; 311 107 int date; 312 108 int time; 313 } _savegame_indices[MAX_SAVEGAME_NR];109 }; 314 110 315 // This assumes modern stream implementations. It may break on DOS. 111 static SavegameDesc _savegame_indices[MAX_SAVEGAME_NR]; 316 112 317 113 318 /* Attempts to mirror a file by copying it from the resource firectory 319 ** to the working directory. Returns NULL if the file didn't exist. 320 ** Otherwise, the new file is then opened for reading or writing. 321 */ 322 static FILE *f_open_mirrored(EngineState *s, char *fname) { 323 debug(3, "f_open_mirrored(%s)", fname); 114 enum { 115 _K_FILE_MODE_OPEN_OR_CREATE = 0, 116 _K_FILE_MODE_OPEN_OR_FAIL = 1, 117 _K_FILE_MODE_CREATE = 2 118 }; 324 119 325 #if 0326 Common::File file;327 if (!file.open(fname))328 return NULL;329 120 330 int fsize = file.size();331 if (fsize > 0) {332 buf = (char *)sci_malloc(fsize);333 file.read(buf, fsize);334 }335 121 336 file.close(); 122 void file_open(EngineState *s, const char *filename, int mode) { 123 const Common::String wrappedName = ((Sci::SciEngine*)g_engine)->wrapFilename(filename); 124 Common::SeekableReadStream *inFile = 0; 125 Common::WriteStream *outFile = 0; 126 Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); 337 127 338 copy the file to a savegame -> only makes sense to perform this change 339 if we at the same time change the code for loading files to look among the 340 savestates, and also change *all* file writing code to write to savestates, 341 as it should 128 if (mode == _K_FILE_MODE_OPEN_OR_FAIL) { 129 // Try to open file, abort if not possible 130 inFile = saveFileMan->openForLoading(wrappedName.c_str()); 131 // If no matching savestate exists: fall back to reading from a regular file 132 if (!inFile) 133 inFile = SearchMan.createReadStreamForMember(filename); 134 if (!inFile) 135 warning("file_open(_K_FILE_MODE_OPEN_OR_FAIL) failed to open file '%s'", filename); 136 } else if (mode == _K_FILE_MODE_CREATE) { 137 // Create the file, destroying any content it might have had 138 outFile = saveFileMan->openForSaving(wrappedName.c_str()); 139 if (!outFile) 140 warning("file_open(_K_FILE_MODE_CREATE) failed to create file '%s'", filename); 141 } else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) { 142 // Try to open file, create it if it doesn't exist 342 143 343 Also, we may have to change the filename when creating a matchin savegame, 344 e.g. prefix it with the target name 345 #endif 144 // FIXME: I am disabling this for now, as it's not quite clear what 145 // should happen if the given file already exists... open it for appending? 146 // Or (more likely), open it for reading *and* writing? We may have to 147 // clone the file for that, etc., see also the long comment at the start 148 // of this file. 149 // We really need some examples on how this is used. 150 warning("file_open(_K_FILE_MODE_OPEN_OR_CREATE) File creation currently not supported"); 151 } else { 152 error("file_open: unsupported mode %d", mode); 153 } 346 154 347 // FIXME: for now we just pretend this has failed 348 return 0; 349 } 155 if (!inFile && !outFile) { // Failed 156 debug(3, "file_open() failed"); 157 s->r_acc = make_reg(0, 0xffff); 158 return; 159 } 350 160 351 #define _K_FILE_MODE_OPEN_OR_CREATE 0352 #define _K_FILE_MODE_OPEN_OR_FAIL 1353 #define _K_FILE_MODE_CREATE 2354 161 355 void file_open(EngineState *s, char *filename, int mode) { 356 FILE *file = NULL; 162 #if 0 163 // FIXME: The old FreeSCI code for opening a file. Left as a reference, as apparently 164 // the implementation below used to work well enough. 357 165 358 166 SCIkdebug(SCIkFILE, "Opening file %s with mode %d\n", filename, mode); 359 167 if ((mode == _K_FILE_MODE_OPEN_OR_FAIL) || (mode == _K_FILE_MODE_OPEN_OR_CREATE)) { … … 378 186 s->r_acc = make_reg(0, 0xffff); 379 187 return; 380 188 } 189 #endif 381 190 382 uint retval = 1; // Ignore _fileHandles[0] 383 while ((retval < s->_fileHandles.size()) && s->_fileHandles[retval]._file) 384 retval++; 191 // Find a free file handle 192 uint handle = 1; // Ignore _fileHandles[0] 193 while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen()) 194 handle++; 385 195 386 if ( retval== s->_fileHandles.size()) { // Hit size limit => Allocate more space196 if (handle == s->_fileHandles.size()) { // Hit size limit => Allocate more space 387 197 s->_fileHandles.resize(s->_fileHandles.size() + 1); 388 198 } 389 199 390 s->_fileHandles[retval]._file = file; 200 s->_fileHandles[handle]._in = inFile; 201 s->_fileHandles[handle]._out = outFile; 202 s->_fileHandles[handle]._name = filename; 391 203 392 s->r_acc = make_reg(0, retval); 204 s->r_acc = make_reg(0, handle); 205 206 debug(3, " -> opened file '%s' with handle %d", filename, handle); 393 207 } 394 208 395 209 reg_t kFOpen(EngineState *s, int funct_nr, int argc, reg_t *argv) { 396 210 char *name = kernel_dereference_char_pointer(s, argv[0], 0); 397 211 int mode = UKPV(1); 398 212 213 debug(3, "kFOpen(%s,0x%x)", name, mode); 399 214 file_open(s, name, mode); 400 debug(3, "kFOpen(%s,0x%x) -> %d", name, mode, s->r_acc.offset);401 215 return s->r_acc; 402 216 } 403 217 404 static F ILE*getFileFromHandle(EngineState *s, uint handle) {218 static FileHandle *getFileFromHandle(EngineState *s, uint handle) { 405 219 if (handle == 0) { 406 220 SCIkwarn(SCIkERROR, "Attempt to use file handle 0\n"); 407 221 return 0; 408 222 } 409 223 410 if ((handle >= s->_fileHandles.size()) || (s->_fileHandles[handle]._file == NULL)) {224 if ((handle >= s->_fileHandles.size()) || !s->_fileHandles[handle].isOpen()) { 411 225 SCIkwarn(SCIkERROR, "Attempt to use invalid/unused file handle %d\n", handle); 412 226 return 0; 413 227 } 414 228 415 return s->_fileHandles[handle]._file;229 return &s->_fileHandles[handle]; 416 230 } 417 231 418 232 void file_close(EngineState *s, int handle) { 419 233 SCIkdebug(SCIkFILE, "Closing file %d\n", handle); 420 234 421 FILE *f = getFileFromHandle(s, handle); 422 if (!f) 423 return; 424 425 fclose(f); 426 427 s->_fileHandles[handle]._file = NULL; 235 FileHandle *f = getFileFromHandle(s, handle); 236 if (f) 237 f->close(); 428 238 } 429 239 430 240 reg_t kFClose(EngineState *s, int funct_nr, int argc, reg_t *argv) { … … 433 243 return s->r_acc; 434 244 } 435 245 436 void fputs_wrapper(EngineState *s, int handle, int size, char *data) {437 SCIkdebug(SCIkFILE, "FPuts'ing \"%s\" to handle %d\n", data, handle);438 439 FILE *f = getFileFromHandle(s, handle);440 if (f)441 fwrite(data, 1, size, f);442 }443 444 246 void fwrite_wrapper(EngineState *s, int handle, char *data, int length) { 445 247 SCIkdebug(SCIkFILE, "fwrite()'ing \"%s\" to handle %d\n", data, handle); 446 248 447 FILE *f = getFileFromHandle(s, handle); 448 if (f) 449 fwrite(data, 1, length, f); 249 FileHandle *f = getFileFromHandle(s, handle); 250 if (!f) 251 return; 252 253 if (!f->_out) { 254 warning("fgets_wrapper: Trying to write to file '%s' opened for reading", f->_name.c_str()); 255 return; 256 } 257 258 f->_out->write(data, length); 450 259 } 451 260 452 261 reg_t kFPuts(EngineState *s, int funct_nr, int argc, reg_t *argv) { 453 262 int handle = UKPV(0); 454 263 char *data = kernel_dereference_char_pointer(s, argv[1], 0); 455 264 456 f puts_wrapper(s, handle, strlen(data), data);265 fwrite_wrapper(s, handle, data, strlen(data)); 457 266 return s->r_acc; 458 267 } 459 268 460 269 static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) { 461 270 SCIkdebug(SCIkFILE, "FGets'ing %d bytes from handle %d\n", maxsize, handle); 462 271 463 F ILE*f = getFileFromHandle(s, handle);272 FileHandle *f = getFileFromHandle(s, handle); 464 273 if (!f) 465 274 return; 466 275 467 fgets(dest, maxsize, f); 276 if (!f->_in) { 277 warning("fgets_wrapper: Trying to read from file '%s' opened for writing", f->_name.c_str()); 278 return; 279 } 280 f->_in->readLine_NEW(dest, maxsize); 468 281 469 282 SCIkdebug(SCIkFILE, "FGets'ed \"%s\"\n", dest); 470 283 } … … 472 285 static void fread_wrapper(EngineState *s, char *dest, int bytes, int handle) { 473 286 SCIkdebug(SCIkFILE, "fread()'ing %d bytes from handle %d\n", bytes, handle); 474 287 475 F ILE*f = getFileFromHandle(s, handle);288 FileHandle *f = getFileFromHandle(s, handle); 476 289 if (!f) 477 290 return; 478 291 479 s->r_acc = make_reg(0, fread(dest, 1, bytes, f)); 292 if (!f->_in) { 293 warning("fread_wrapper: Trying to read from file '%s' opened for writing", f->_name.c_str()); 294 return; 295 } 296 297 s->r_acc = make_reg(0, f->_in->read(dest, bytes)); 480 298 } 481 299 482 300 static void fseek_wrapper(EngineState *s, int handle, int offset, int whence) { 483 F ILE*f = getFileFromHandle(s, handle);301 FileHandle *f = getFileFromHandle(s, handle); 484 302 if (!f) 485 303 return; 486 304 487 s->r_acc = make_reg(0, fseek(f, offset, whence)); 305 if (!f->_in) { 306 warning("fseek_wrapper: Trying to seek in file '%s' opened for writing", f->_name.c_str()); 307 return; 308 } 309 310 s->r_acc = make_reg(0, f->_in->seek(offset, whence)); 488 311 } 489 312 490 #define TEST_DIR_OR_QUIT(dir) if (!dir) { return NULL_REG; }491 492 313 reg_t kFGets(EngineState *s, int funct_nr, int argc, reg_t *argv) { 493 314 char *dest = kernel_dereference_char_pointer(s, argv[0], 0); 494 315 int maxsize = UKPV(1); … … 499 320 return argv[0]; 500 321 } 501 322 502 /* kGetCWD(address):503 ** Writes the cwd to the supplied address and returns the address in acc.504 */323 /** 324 * Writes the cwd to the supplied address and returns the address in acc. 325 */ 505 326 reg_t kGetCWD(EngineState *s, int funct_nr, int argc, reg_t *argv) { 506 327 char *targetaddr = kernel_dereference_char_pointer(s, argv[0], 0); 507 328 508 getcwd(targetaddr, MAX_SAVE_DIR_SIZE - 1); 329 // We do not let the scripts see the file system, instead pretending 330 // we are always in the same directory. 331 // TODO/FIXME: Is "/" a good value? Maybe "" or "." or "C:\" are better? 332 strcpy(targetaddr, "/"); 509 333 510 targetaddr[MAX_SAVE_DIR_SIZE - 1] = 0; // Terminate511 334 debug(3, "kGetCWD() -> %s", targetaddr); 512 335 513 336 return argv[0]; … … 522 345 saveFileMan->removeSavefile(filename.c_str()); 523 346 } 524 347 525 #define K_DEVICE_INFO_GET_DEVICE 0 526 #define K_DEVICE_INFO_GET_CURRENT_DEVICE 1 527 #define K_DEVICE_INFO_PATHS_EQUAL 2 528 #define K_DEVICE_INFO_IS_FLOPPY 3 529 #define K_DEVICE_INFO_GET_SAVECAT_NAME 7 530 #define K_DEVICE_INFO_GET_SAVEFILE_NAME 8 348 enum { 349 K_DEVICE_INFO_GET_DEVICE = 0, 350 K_DEVICE_INFO_GET_CURRENT_DEVICE = 1, 351 K_DEVICE_INFO_PATHS_EQUAL = 2, 352 K_DEVICE_INFO_IS_FLOPPY = 3, 353 K_DEVICE_INFO_GET_SAVECAT_NAME = 7, 354 K_DEVICE_INFO_GET_SAVEFILE_NAME = 8 355 }; 531 356 532 357 reg_t kDeviceInfo(EngineState *s, int funct_nr, int argc, reg_t *argv) { 533 358 int mode = UKPV(0); … … 610 435 } 611 436 612 437 static int _savegame_index_struct_compare(const void *a, const void *b) { 613 struct _savegame_index_struct *A = (struct _savegame_index_struct*)a;614 struct _savegame_index_struct *B = (struct _savegame_index_struct*)b;438 SavegameDesc *A = (SavegameDesc *)a; 439 SavegameDesc *B = (SavegameDesc *)b; 615 440 616 441 if (B->date != A->date) 617 442 return B->date - A->date; … … 645 470 } 646 471 } 647 472 648 qsort(_savegame_indices, _savegame_indices_nr, sizeof( struct _savegame_index_struct), _savegame_index_struct_compare);473 qsort(_savegame_indices, _savegame_indices_nr, sizeof(SavegameDesc), _savegame_index_struct_compare); 649 474 } 650 475 651 476 reg_t kCheckSaveGame(EngineState *s, int funct_nr, int argc, reg_t *argv) { … … 851 676 } 852 677 853 678 reg_t kValidPath(EngineState *s, int funct_nr, int argc, reg_t *argv) { 854 char *pathname = kernel_dereference_char_pointer(s, argv[0], 0); 855 char cpath[MAXPATHLEN + 1]; 856 getcwd(cpath, MAXPATHLEN + 1); 679 const char *path = kernel_dereference_char_pointer(s, argv[0], 0); 857 680 681 // FIXME: For now, we only accept the (fake) root dir "/" as a valid path. 682 s->r_acc = make_reg(0, 0 == strcmp(path, "/")); 858 683 859 s->r_acc = make_reg(0, !chdir(pathname)); // Try to go there. If it works, return 1, 0 otherwise. 860 chdir(cpath); 684 debug(3, "kValidPath(%s) -> %d", path, s->r_acc.offset); 861 685 862 debug(3, "kValidPath(%s) -> %d", pathname, s->r_acc.offset);863 864 686 return s->r_acc; 865 687 } 866 688 867 #define K_FILEIO_OPEN 0 868 #define K_FILEIO_CLOSE 1 869 #define K_FILEIO_READ_RAW 2 870 #define K_FILEIO_WRITE_RAW 3 871 #define K_FILEIO_UNLINK 4 872 #define K_FILEIO_READ_STRING 5 873 #define K_FILEIO_WRITE_STRING 6 874 #define K_FILEIO_SEEK 7 875 #define K_FILEIO_FIND_FIRST 8 876 #define K_FILEIO_FIND_NEXT 9 877 #define K_FILEIO_STAT 10 878 879 880 class DirSeeker { 881 protected: 882 EngineState *_vm; 883 reg_t _outbuffer; 884 sci_dir_t _dir; 885 886 const char *write_filename_to_mem(const char *string); 887 888 public: 889 DirSeeker(EngineState *s) : _vm(s) { 890 _outbuffer = NULL_REG; 891 sci_init_dir(&_dir); 892 } 893 894 void first_file(const char *dir, char *mask, reg_t buffer); 895 void next_file(); 689 enum { 690 K_FILEIO_OPEN = 0, 691 K_FILEIO_CLOSE = 1, 692 K_FILEIO_READ_RAW = 2, 693 K_FILEIO_WRITE_RAW = 3, 694 K_FILEIO_UNLINK = 4, 695 K_FILEIO_READ_STRING = 5, 696 K_FILEIO_WRITE_STRING = 6, 697 K_FILEIO_SEEK = 7, 698 K_FILEIO_FIND_FIRST = 8, 699 K_FILEIO_FIND_NEXT = 9, 700 K_FILEIO_FILE_EXISTS = 10 896 701 }; 897 702 898 703 899 const char *DirSeeker::write_filename_to_mem(const char *string) { 900 char *mem = kernel_dereference_char_pointer(_vm, _outbuffer, 0); 901 902 if (string) { 903 memset(mem, 0, 13); 904 strncpy(mem, string, 12); 704 void DirSeeker::firstFile(const char *mask, reg_t buffer) { 705 706 // Verify that we are given a valid buffer 707 if (!buffer.segment) { 708 warning("DirSeeker::firstFile('%s') invoked with invalid buffer", mask); 709 _vm->r_acc = NULL_REG; 710 return; 905 711 } 712 _outbuffer = buffer; 906 713 907 return string; 908 } 714 // Obtain a list of all savefiles matching the given mask 715 // TODO: Modify the mask, e.g. by prefixing "TARGET-". 716 Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); 717 _savefiles = saveFileMan->listSavefiles(mask); 909 718 910 void DirSeeker::next_file() { 911 if (write_filename_to_mem(sci_find_next(&_dir))) 912 _vm->r_acc = _outbuffer; 913 else 914 _vm->r_acc = NULL_REG; 719 // Reset the list iterator and write the first match to the output buffer, if any. 720 _iter = _savefiles.begin(); 721 nextFile(); 915 722 } 916 723 917 void DirSeeker::first_file(const char *dir, char *mask, reg_t buffer) { 918 if (!buffer.segment) { 919 sciprintf("Warning: first_file(state,\"%s\",\"%s\", 0) invoked!\n", dir, mask); 724 void DirSeeker::nextFile() { 725 if (_iter == _savefiles.end()) { 920 726 _vm->r_acc = NULL_REG; 921 727 return; 922 728 } 923 729 924 if (strcmp(dir, ".")) { 925 sciprintf("%s L%d: Non-local first_file: Not implemented yet\n", __FILE__, __LINE__); 926 _vm->r_acc = NULL_REG; 927 return; 928 } 730 char *mem = kernel_dereference_char_pointer(_vm, _outbuffer, 0); 731 memset(mem, 0, 13); 929 732 930 // Get rid of the old find structure931 if (_outbuffer.segment)932 sci_finish_find(&_dir);933 934 _outbuffer = buffer;733 // TODO: Transform the string back into a format usable by the SCI scripts. 734 // I.e., strip any TARGET- prefix. 735 const char *string = _iter->c_str(); 736 assert(string); 737 strncpy(mem, string, 12); 935 738 936 if (write_filename_to_mem(sci_find_first(&_dir, mask))) 937 _vm->r_acc = _outbuffer; 938 else 939 _vm->r_acc = NULL_REG; 739 // Return the result and advance the list iterator :) 740 _vm->r_acc = _outbuffer; 741 ++_iter; 940 742 } 941 743 744 745 942 746 reg_t kFileIO(EngineState *s, int funct_nr, int argc, reg_t *argv) { 943 747 int func_nr = UKPV(0); 944 748 … … 948 752 int mode = UKPV(2); 949 753 950 754 file_open(s, name, mode); 951 debug(3, "K_FILEIO_OPEN(%s,0x%x) -> %d", name, mode, s->r_acc.offset);755 debug(3, "K_FILEIO_OPEN(%s,0x%x)", name, mode); 952 756 break; 953 757 } 954 758 case K_FILEIO_CLOSE : { … … 980 784 char *name = kernel_dereference_char_pointer(s, argv[1], 0); 981 785 debug(3, "K_FILEIO_UNLINK(%s)", name); 982 786 983 unlink(name); 787 Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); 788 const Common::String wrappedName = ((Sci::SciEngine*)g_engine)->wrapFilename(name); 789 saveFileMan->removeSavefile(wrappedName.c_str()); 790 // TODO/FIXME: Should we return something (like, a bool indicating 791 // whether deleting the save succeeded or failed)? 984 792 break; 985 793 } 986 794 case K_FILEIO_READ_STRING : { … … 998 806 char *buf = kernel_dereference_char_pointer(s, argv[2], size); 999 807 debug(3, "K_FILEIO_WRITE_STRING(%d,%d)", handle, size); 1000 808 809 // FIXME: What is the difference between K_FILEIO_WRITE_STRING and 810 // K_FILEIO_WRITE_RAW? Normally, I would expect the difference to 811 // be that the former doesn't receive a 'size' parameter. But here 812 // it does. Are we missing something? 1001 813 if (buf) 1002 f puts_wrapper(s, handle, size, buf);814 fwrite_wrapper(s, handle, buf, size); 1003 815 break; 1004 816 } 1005 817 case K_FILEIO_SEEK : { … … 1021 833 if (strcmp(mask, "*.*") == 0) 1022 834 strcpy(mask, "*"); // For UNIX 1023 835 #endif 1024 if (!s->dirseeker) 1025 s->dirseeker = new DirSeeker(s); 1026 assert(s->dirseeker); 1027 s->dirseeker->first_file(".", mask, buf); 836 s->_dirseeker.firstFile(mask, buf); 1028 837 1029 838 break; 1030 839 } 1031 840 case K_FILEIO_FIND_NEXT : { 1032 assert(s->dirseeker);1033 841 debug(3, "K_FILEIO_FIND_NEXT()"); 1034 s-> dirseeker->next_file();842 s->_dirseeker.nextFile(); 1035 843 break; 1036 844 } 1037 case K_FILEIO_ STAT: {845 case K_FILEIO_FILE_EXISTS : { 1038 846 char *name = kernel_dereference_char_pointer(s, argv[1], 0); 1039 s->r_acc = make_reg(0, sci_file_size(name) >= 0); 1040 debug(3, "K_FILEIO_STAT(%s) -> %d", name, s->r_acc.offset); 847 // TODO: Transform the name given by the scripts to us, e.g. by 848 // prepending TARGET- 849 // TODO: We may have to also check for a regular file with the 850 // given name, using File::exists(). Really depends on *how* 851 // scripts use this opcode. Need more test data... 852 Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); 853 bool exists = !saveFileMan->listSavefiles(name).empty(); 854 855 s->r_acc = make_reg(0, exists); 856 debug(3, "K_FILEIO_FILE_EXISTS(%s) -> %d", name, s->r_acc.offset); 1041 857 break; 1042 858 } 1043 859 default :