/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "glk/tads/os_frob_tads.h" #include "common/file.h" #include "common/memstream.h" namespace Glk { namespace TADS { static osfildef *openForReading(const char *fname) { Common::File f; if (f.open(fname)) return f.readStream(f.size()); Common::InSaveFile *save = g_system->getSavefileManager()->openForLoading(fname); return save; } static osfildef *openForWriting(const char *fname) { return g_system->getSavefileManager()->openForSaving(fname, false); } int osfacc(const char *fname) { return Common::File::exists(fname) ? 1 : 0; } osfildef *osfoprt(const char *fname, os_filetype_t typ) { return openForReading(fname); } osfildef *osfoprtv(const char *fname, os_filetype_t typ) { return openForReading(fname); } osfildef *osfopwt(const char *fname, os_filetype_t typ) { return openForWriting(fname); } osfildef *osfoprwt(const char *fname, os_filetype_t typ) { warning("ScummVM files can't be opened for both reading and writing simultaneously"); return openForWriting(fname); } osfildef *osfoprwtt(const char *fname, os_filetype_t typ) { warning("ScummVM files can't be opened for both reading and writing simultaneously"); return openForWriting(fname); } osfildef *osfopwb(const char *fname, os_filetype_t typ) { return openForWriting(fname); } osfildef *osfoprs(const char *fname, os_filetype_t typ) { return openForReading(fname); } osfildef *osfoprb(const char *fname, os_filetype_t typ) { return openForReading(fname); } osfildef *osfoprbv(const char *fname, os_filetype_t typ) { return openForReading(fname); } osfildef *osfoprwb(const char *fname, os_filetype_t typ) { warning("ScummVM files can't be opened for both reading and writing simultaneously"); return openForWriting(fname); } osfildef *osfoprwtb(const char *fname, os_filetype_t typ) { warning("ScummVM files can't be opened for both reading and writing simultaneously"); return openForWriting(fname); } osfildef *osfdup(osfildef *orig, const char *mode) { Common::SeekableReadStream *rs = dynamic_cast(orig); int32 currPos = rs->pos(); rs->seek(0); osfildef *result = rs->readStream(rs->size()); rs->seek(currPos); return result; } void os_settype(const char *f, os_filetype_t typ) { // No implementation } char *osfgets(char *buf, size_t count, osfildef *fp) { Common::ReadStream *rs = dynamic_cast(fp); char *ptr = buf; char c; while (!rs->eos() && --count > 0) { c = rs->readByte(); if (c == '\n' || c == '\0') break; *ptr++ = c; } *ptr++ = '\0'; return buf; } int osfputs(const char *str, osfildef *fp) { return dynamic_cast(fp)->write(str, strlen(str)) == strlen(str) ? 0 : -1; } void os_fprintz(osfildef *fp, const char *str) { dynamic_cast(fp)->write(str, strlen(str)); } void os_fprint(osfildef *fp, const char *str, size_t len) { Common::String s(str, str + MIN(len, strlen(str))); dynamic_cast(fp)->write(s.c_str(), s.size()); } int osfwb(osfildef *fp, const void *buf, size_t bufl) { return dynamic_cast(fp)->write(buf, bufl) == bufl ? 0 : 1; } int osfflush(osfildef *fp) { return dynamic_cast(fp)->flush() ? 0 : 1; } int osfgetc(osfildef *fp) { return dynamic_cast(fp)->readByte(); } int osfrb(osfildef *fp, void *buf, size_t bufl) { return dynamic_cast(fp)->read(buf, bufl) == bufl ? 0 : 1; } size_t osfrbc(osfildef *fp, void *buf, size_t bufl) { return dynamic_cast(fp)->read(buf, bufl); } long osfpos(osfildef *fp) { return dynamic_cast(fp)->pos(); } int osfseek(osfildef *fp, long pos, int mode) { return dynamic_cast(fp)->seek(pos, mode); } void osfcls(osfildef *fp) { delete fp; } int osfdel(const char *fname) { return g_system->getSavefileManager()->removeSavefile(fname) ? 0 : 1; } int os_rename_file(const char *oldname, const char *newname) { return g_system->getSavefileManager()->renameSavefile(oldname, newname); } bool os_locate(const char *fname, int flen, const char *arg0, char *buf, size_t bufsiz) { Common::String name = !flen ? Common::String(fname) : Common::String(fname, fname + flen); if (!Common::File::exists(fname)) return false; strncpy(buf, name.c_str(), bufsiz - 1); buf[bufsiz - 1] = '\0'; return true; } osfildef *os_create_tempfile(const char *fname, char *buf) { strcpy(buf, "tmpfile"); return new Common::MemoryReadWriteStream(DisposeAfterUse::YES); } int osfdel_temp(const char *fname) { // Temporary files in ScummVM are just memory streams, so there isn't a file to delete return 0; } void os_get_tmp_path(char *buf) { strcpy(buf, ""); } int os_gen_temp_filename(char *buf, size_t buflen) { error("TODO: If results from this are being passed to file open methods, will need to do further work"); } /* ------------------------------------------------------------------------ */ void os_set_pwd(const char *dir) { // No implementation } void os_set_pwd_file(const char *filename) { // No implementation } bool os_mkdir(const char *dir, int create_parents) { // Unsupported return false; } bool os_rmdir(const char *dir) { // Unsupported return false; } /* ------------------------------------------------------------------------ */ void os_defext(char *fname, const char *ext) { if (!strchr(fname, '.')) strcat(fname, ext); } void os_addext(char *fname, const char *ext) { strcat(fname, ext); } void os_remext(char *fname) { char *p = strchr(fname, '.'); if (p) *p = '\0'; } bool os_file_names_equal(const char *a, const char *b) { return !strcmp(a, b); } const char *os_get_root_name(const char *buf) { return buf; } bool os_is_file_absolute(const char *fname) { return false; } void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname) { strcpy(pathbuf, ""); } void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen, const char *path, const char *filename) { strcpy(fullpathbuf, filename); } void os_combine_paths(char *fullpathbuf, size_t pathbuflen, const char *path, const char *filename) { strcpy(fullpathbuf, filename); } bool os_get_abs_filename(char *result_buf, size_t result_buf_size, const char *filename) { strcpy(result_buf, filename); return true; } bool os_get_rel_path(char *result_buf, size_t result_buf_size, const char *basepath, const char *filename) { strcpy(result_buf, filename); return true; } bool os_is_file_in_dir(const char *filename, const char *path, bool include_subdirs, bool match_self) { assert(!include_subdirs && !match_self); return Common::File::exists(filename); } /* ------------------------------------------------------------------------ */ /* * Convert an OS filename path to URL-style format. This isn't a true URL * conversion; rather, it simply expresses a filename in Unix-style * notation, as a series of path elements separated by '/' characters. * Unlike true URLs, we don't use % encoding or a scheme prefix (file://, * etc). * * The result path never ends in a trailing '/', unless the entire result * path is "/". This is for consistency; even if the source path ends with * a local path separator, the result doesn't. * * If the local file system syntax uses '/' characters as ordinary filename * characters, these must be replaced with some other suitable character in * the result, since otherwise they'd be taken as path separators when the * URL is parsed. If possible, the substitution should be reversible with * respect to os_cvt_dir_url(), so that the same URL read back in on this * same platform will produce the same original filename. One particular * suggestion is that if the local system uses '/' to delimit what would be * a filename extension on other platforms, replace '/' with '.', since * this will provide reversibility as well as a good mapping if the URL is * read back in on another platform. * * The local equivalents of "." and "..", if they exist, are converted to * "." and ".." in the URL notation. * * Examples: * *. Windows: images\rooms\startroom.jpg -> images/rooms/startroom.jpg *. Windows: ..\startroom.jpg -> ../startroom.jpg *. Mac: :images:rooms:startroom.jpg -> images/rooms/startroom.jpg *. Mac: ::startroom.jpg -> ../startroom.jpg *. VMS: [.images.rooms]startroom.jpg -> images/rooms/startroom.jpg *. VMS: [-.images]startroom.jpg -> ../images/startroom.jpg *. Unix: images/rooms/startroom.jpg -> images/rooms/startroom.jpg *. Unix: ../images/startroom.jpg -> ../images/startroom.jpg * * If the local name is an absolute path in the local file system (e.g., * Unix /file, Windows C:\file), translate as follows. If the local * operating system uses a volume or device designator (Windows C:, VMS * SYS$DISK:, etc), make the first element of the path the exact local * syntax for the device designator: /C:/ on Windows, /SYS$DISK:/ on VMS, * etc. Include the local syntax for the device prefix. For a system like * Unix with a unified file system root ("/"), simply start with the root * directory. Examples: * *. Windows: C:\games\deep.gam -> /C:/games/deep.gam *. Windows: C:games\deep.gam -> /C:./games/deep.gam *. Windows: \\SERVER\DISK\games\deep.gam -> /\\SERVER/DISK/games/deep.gam *. Mac OS 9: Hard Disk:games:deep.gam -> /Hard Disk:/games/deep.gam *. VMS: SYS$DISK:[games]deep.gam -> /SYS$DISK:/games/deep.gam *. Unix: /games/deep.gam -> /games/deep.gam * * Rationale: it's effectively impossible to create a truly portable * representation of an absolute path. Operating systems are too different * in the way they represent root paths, and even if that were solvable, a * root path is essentially unusable across machines anyway because it * creates a dependency on the contents of a particular machine's disk. So * if we're called upon to translate an absolute path, we can forget about * trying to be truly portable and instead focus on round-trip fidelity - * i.e., making sure that applying os_cvt_url_dir() to our result recovers * the exact original path, assuming it's done on the same operating * system. The approach outlined above should achieve round-trip fidelity * when a local path is converted to a URL and back on the same machine, * since the local URL-to-path converter should recognize its own special * type of local absolute path prefix. It also produces reasonable results * on other platforms - see the os_cvt_url_dir() comments below for * examples of the decoding results for absolute paths moved to new * platforms. The result when a device-rooted absolute path is encoded on * one machine and then decoded on another will generally be a local path * with a root on the default device/volume and an outermost directory with * a name based on the original machine's device/volume name. This * obviously won't reproduce the exact original path, but since that's * impossible anyway, this is probably as good an approximation as we can * create. * * Character sets: the input could be in local or UTF-8 character sets. * The implementation shouldn't care, though - just treat bytes in the * range 0-127 as plain ASCII, and everything else as opaque. I.e., do not * quote or otherwise modify characters outside the 0-127 range. */ void os_cvt_dir_url(char *result_buf, size_t result_buf_size, const char *src_path); /* * Convert a URL-style path into a filename path expressed in the local * file system's syntax. Fills in result_buf with a file path, constructed * using the local file system syntax, that corresponds to the path in * src_url expressed in URL-style syntax. Examples: * * images/rooms/startroom.jpg -> *. Windows -> images\rooms\startroom.jpg *. Mac OS 9 -> :images:rooms:startroom.jpg *. VMS -> [.images.rooms]startroom.jpg * * The source format isn't a true URL; it's simply a series of path * elements separated by '/' characters. Unlike true URLs, our input * format doesn't use % encoding and doesn't have a scheme (file://, etc). * (Any % in the source is treated as an ordinary character and left as-is, * even if it looks like a %XX sequence. Anything that looks like a scheme * prefix is left as-is, with any // treated as path separators. * * images/file%20name.jpg -> *. Windows -> images\file%20name.jpg * * file://images/file.jpg -> *. Windows -> file_\\images\file.jpg * * Any characters in the path that are invalid in the local file system * naming rules are converted to "_", unless "_" is itself invalid, in * which case they're converted to "X". One exception is that if '/' is a * valid local filename character (rather than a path separator as it is on * Unix and Windows), it can be used as the replacement for the character * that os_cvt_dir_url uses as its replacement for '/', so that this * substitution is reversible when a URL is generated and then read back in * on this same platform. * * images/file:name.jpg -> *. Windows -> images\file_name.jpg *. Mac OS 9 -> :images:file_name.jpg *. Unix -> images/file:name.jpg * * The path elements "." and ".." are specifically defined as having their * Unix meanings: "." is an alias for the preceding path element, or the * working directory if it's the first element, and ".." is an alias for * the parent of the preceding element. When these appear as path * elements, this routine translates them to the appropriate local * conventions. "." may be translated simply by removing it from the path, * since it reiterates the previous path element. ".." may be translated * by removing the previous element - HOWEVER, if ".." appears as the first * element, it has to be retained and translated to the equivalent local * notation, since it will have to be applied later, when the result_buf * path is actually used to open a file, at which point it will combined * with the working directory or another base path. * *. /images/../file.jpg -> [Windows] file.jpg *. ../images/file.jpg -> *. Windows -> ..\images\file.jpg *. Mac OS 9 -> ::images:file.jpg *. VMS -> [-.images]file.jpg * * If the URL path is absolute (starts with a '/'), the routine inspects * the path to see if it was created by the same OS, according to the local * rules for converting absolute paths in os_cvt_dir_url() (see). If so, * we reverse the encoding done there. If it doesn't appear that the name * was created by the same operating system - that is, if reversing the * encoding doesn't produce a valid local filename - then we create a local * absolute path as follows. If the local system uses device/volume * designators, we start with the current working device/volume or some * other suitable default volume. We then add the first element of the * path, if any, as the root directory name, applying the usual "_" or "X" * substitution for any characters that aren't allowed in local names. The * rest of the path is handled in the usual fashion. * *. /images/file.jpg -> *. Windows -> \images\file.jpg *. Unix -> /images/file.jpg * *. /c:/images/file.jpg -> *. Windows -> c:\images\file.jpg *. Unix -> /c:/images/file.jpg *. VMS -> SYS$DISK:[c__.images]file.jpg * *. /Hard Disk:/images/file.jpg -> *. Windows -> \Hard Disk_\images\file.jpg *. Unix -> SYS$DISK:[Hard_Disk_.images]file.jpg * * Note how the device/volume prefix becomes the top-level directory when * moving a path across machines. It's simply not possible to reconstruct * the exact original path in such cases, since device/volume syntax rules * have little in common across systems. But this seems like a good * approximation in that (a) it produces a valid local path, and (b) it * gives the user a reasonable basis for creating a set of folders to mimic * the original source system, if they want to use that approach to port * the data rather than just changing the paths internally in the source * material. * * Character sets: use the same rules as for os_cvt_dir_url(). */ void os_cvt_url_dir(char *result_buf, size_t result_buf_size, const char *src_url); } // End of namespace TADS } // End of namespace Glk