From 51949bbc9e7cefc539cff4316f0726636192dfc9 Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Thu, 14 Sep 2017 17:40:22 +0100 Subject: RISCOS: Add RISC OS filesystem --- backends/fs/riscos/riscos-fs-factory.cpp | 46 +++++ backends/fs/riscos/riscos-fs-factory.h | 40 ++++ backends/fs/riscos/riscos-fs.cpp | 323 +++++++++++++++++++++++++++++++ backends/fs/riscos/riscos-fs.h | 105 ++++++++++ 4 files changed, 514 insertions(+) create mode 100644 backends/fs/riscos/riscos-fs-factory.cpp create mode 100644 backends/fs/riscos/riscos-fs-factory.h create mode 100644 backends/fs/riscos/riscos-fs.cpp create mode 100644 backends/fs/riscos/riscos-fs.h (limited to 'backends/fs') diff --git a/backends/fs/riscos/riscos-fs-factory.cpp b/backends/fs/riscos/riscos-fs-factory.cpp new file mode 100644 index 0000000000..b698bcb908 --- /dev/null +++ b/backends/fs/riscos/riscos-fs-factory.cpp @@ -0,0 +1,46 @@ +/* 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. + * + */ + +#if defined(RISCOS) + +// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. +#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h + +#include "backends/fs/riscos/riscos-fs-factory.h" +#include "backends/fs/riscos/riscos-fs.h" + +#include + +AbstractFSNode *RISCOSFilesystemFactory::makeRootFileNode() const { + return new RISCOSFilesystemNode("/"); +} + +AbstractFSNode *RISCOSFilesystemFactory::makeCurrentDirectoryFileNode() const { + char buf[MAXPATHLEN]; + return getcwd(buf, MAXPATHLEN) ? new RISCOSFilesystemNode(buf) : NULL; +} + +AbstractFSNode *RISCOSFilesystemFactory::makeFileNodePath(const Common::String &path) const { + assert(!path.empty()); + return new RISCOSFilesystemNode(path); +} +#endif diff --git a/backends/fs/riscos/riscos-fs-factory.h b/backends/fs/riscos/riscos-fs-factory.h new file mode 100644 index 0000000000..4bed2b5f08 --- /dev/null +++ b/backends/fs/riscos/riscos-fs-factory.h @@ -0,0 +1,40 @@ +/* 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. + * + */ + +#ifndef RISCOS_FILESYSTEM_FACTORY_H +#define RISCOS_FILESYSTEM_FACTORY_H + +#include "backends/fs/fs-factory.h" + +/** + * Creates RISCOSFilesystemNode objects. + * + * Parts of this class are documented in the base interface class, FilesystemFactory. + */ +class RISCOSFilesystemFactory : public FilesystemFactory { +protected: + virtual AbstractFSNode *makeRootFileNode() const; + virtual AbstractFSNode *makeCurrentDirectoryFileNode() const; + virtual AbstractFSNode *makeFileNodePath(const Common::String &path) const; +}; + +#endif /*RISCOS_FILESYSTEM_FACTORY_H*/ diff --git a/backends/fs/riscos/riscos-fs.cpp b/backends/fs/riscos/riscos-fs.cpp new file mode 100644 index 0000000000..38d150e572 --- /dev/null +++ b/backends/fs/riscos/riscos-fs.cpp @@ -0,0 +1,323 @@ +/* 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. + * + */ + +#if defined(RISCOS) + +// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. +#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h +#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir + +#include "backends/fs/riscos/riscos-fs.h" +#include "backends/fs/stdiostream.h" +#include "common/algorithm.h" + +#include +#include +#include +#include + +#include +#include +#include + +bool RISCOSFilesystemNode::exists() const { + return access(_path.c_str(), F_OK) == 0; +} + +bool RISCOSFilesystemNode::isReadable() const { + return access(_path.c_str(), R_OK) == 0; +} + +bool RISCOSFilesystemNode::isWritable() const { + return access(_path.c_str(), W_OK) == 0; +} + +RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) { + _path = p; + if (p == "/") { + _isDirectory = true; + _isValid = true; + } else { + int type = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, toRISCOS(_path).c_str()); + if (type == 0) { + _isDirectory = false; + _isValid = false; + } else if (type == 2) { + _isDirectory = true; + _isValid = true; + } else { + _isDirectory = false; + _isValid = true; + } + } +} + +Common::String RISCOSFilesystemNode::toRISCOS(Common::String &path) { + char start[PATH_MAX]; + char *end = __riscosify_std(path.c_str(), 0, start, PATH_MAX, 0); + return Common::String(start, end); +} + +Common::String RISCOSFilesystemNode::toUnix(Common::String &path) { + Common::String out = Common::String(path); + uint32 start = 0; + if (out.contains("$")) { + char *x = strstr(out.c_str(), "$"); + start = x ? x - out.c_str() : -1; + } + + for (uint32 ptr = start; ptr < out.size(); ptr += 1) { + switch (out.c_str()[ptr]) { + case '.': + out.setChar('/', ptr); + break; + case '/': + out.setChar('.', ptr); + break; + default: + break; + } + } + + if (out.contains("$")) + out = "/" + out; + + return out; +} + +AbstractFSNode *RISCOSFilesystemNode::getChild(const Common::String &n) const { + assert(!_path.empty()); + assert(_isDirectory); + + // Make sure the string contains no slashes + assert(!n.contains('/')); + + // We assume here that _path is already normalized (hence don't bother to call + // Common::normalizePath on the final path). + Common::String newPath(_path); + if (_path.lastChar() != '/') + newPath += '/'; + newPath += n; + + return makeNode(newPath); +} + +bool RISCOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const { + assert(_isDirectory); + + if (_path == "/") { + // Special case for the root dir: List all drives + char fsname[PATH_MAX] = ""; + for (int fsNum = 0; fsNum < 256; fsNum += 1) { + _swi(OS_FSControl, _INR(0,3), 33, fsNum, fsname, sizeof(fsname)); + if (strcmp(fsname, "") != 0) { + if (!(fsNum == 46 || fsNum == 53 || fsNum == 99)) { + int drives = 9; + if (fsNum == 193) + drives = 23; + + for (int discnum = 0; discnum <= drives; discnum += 1) { + const Common::String path = Common::String::format("%s::%d.$", fsname, discnum); + char outpath[PATH_MAX] = ""; + if(_swix(OS_FSControl, _INR(0,2)|_IN(5), 37, path.c_str(), outpath, sizeof(outpath)) == NULL) { + int exist; + if (_swix(OS_File, _INR(0,1)|_OUT(0), 23, outpath, &exist) != NULL || exist != 2) + continue; + + RISCOSFilesystemNode *entry = new RISCOSFilesystemNode(); + entry->_isDirectory = true; + entry->_isValid = true; + entry->_path = Common::String::format("/%s", outpath); + entry->_displayName = outpath; + myList.push_back(entry); + } + } + } + } + } + return true; + } + + int count = 0; + int read = 0; + char file[PATH_MAX]; + Common::String dir = _path; + + while (count != -1) { + _swix(OS_GBPB, _INR(0,5)|_OUTR(3,4), 9, toRISCOS(dir).c_str(), file, 1, count, sizeof(file), &read, &count); + + if (count == -1) + continue; + + // Start with a clone of this node, with the correct path set + RISCOSFilesystemNode entry(*this); + entry._displayName = file; + entry._displayName = toUnix(entry._displayName); + if (_path.lastChar() != '/') + entry._path += '/'; + entry._path += entry._displayName; + + int type = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, toRISCOS(entry._path).c_str()); + if (type == 0) { + continue; + } else if (type == 2) { + entry._isDirectory = true; + } else { + entry._isDirectory = false; + } + + // Honor the chosen mode + if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) || + (mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory)) + continue; + + myList.push_back(new RISCOSFilesystemNode(entry)); + } + + return true; +} + +AbstractFSNode *RISCOSFilesystemNode::getParent() const { + if (_path == "/") + return 0; // The filesystem root has no parent + + const char *start = _path.c_str(); + const char *end = start + _path.size(); + + // Strip of the last component. We make use of the fact that at this + // point, _path is guaranteed to be normalized + while (end > start && *(end-1) != '/') + end--; + + if (end == start) { + // This only happens if we were called with a relative path, for which + // there simply is no parent. + // TODO: We could also resolve this by assuming that the parent is the + // current working directory, and returning a node referring to that. + return 0; + } + + if (*(end-1) == '/' && end != start + 1) + end--; + + return makeNode(Common::String(start, end)); +} + +Common::SeekableReadStream *RISCOSFilesystemNode::createReadStream() { + return StdioStream::makeFromPath(getPath(), false); +} + +Common::WriteStream *RISCOSFilesystemNode::createWriteStream() { + return StdioStream::makeFromPath(getPath(), true); +} + +bool RISCOSFilesystemNode::create(bool isDirectoryFlag) { + bool success; + + if (isDirectoryFlag) { + success = _swix(OS_File, _INR(0,1), 8, toRISCOS(_path).c_str()) == NULL; + } else { + int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755); + success = fd >= 0; + + if (fd >= 0) { + close(fd); + } + } + + if (success) { + if (exists()) { + _isDirectory = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, toRISCOS(_path).c_str()) == 2; + if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file"); + return _isDirectory == isDirectoryFlag; + } + + warning("RISCOSFilesystemNode: Attempting to create a %s was a success, but access indicates there is no such %s", + isDirectoryFlag ? "directory" : "file", isDirectoryFlag ? "directory" : "file"); + return false; + } + + return false; +} + +namespace Riscos { + +bool assureDirectoryExists(const Common::String &dir, const char *prefix) { + AbstractFSNode *node; + + // Check whether the prefix exists if one is supplied. + if (prefix) { + node = new RISCOSFilesystemNode(prefix); + if (!node->isDirectory()) { + return false; + } + } + + // Obtain absolute path. + Common::String path; + if (prefix) { + path = prefix; + path += '/'; + path += dir; + } else { + path = dir; + } + + path = Common::normalizePath(path, '/'); + + const Common::String::iterator end = path.end(); + Common::String::iterator cur = path.begin(); + if (*cur == '/') + ++cur; + + do { + if (cur + 1 != end) { + if (*cur != '/') { + continue; + } + + // It is kind of ugly and against the purpose of Common::String to + // insert 0s inside, but this is just for a local string and + // simplifies the code a lot. + *cur = '\0'; + } + + node = new RISCOSFilesystemNode(path); + if (!node->create(true)) { + if (node->exists()) { + if (!node->isDirectory()) { + return false; + } + } else { + return false; + } + } + + *cur = '/'; + } while (cur++ != end); + + return true; +} + +} // End of namespace RISCOS + +#endif //#if defined(RISCOS) diff --git a/backends/fs/riscos/riscos-fs.h b/backends/fs/riscos/riscos-fs.h new file mode 100644 index 0000000000..b02f30bce7 --- /dev/null +++ b/backends/fs/riscos/riscos-fs.h @@ -0,0 +1,105 @@ +/* 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. + * + */ + +#ifndef RISCOS_FILESYSTEM_H +#define RISCOS_FILESYSTEM_H + +#include "backends/fs/abstract-fs.h" + +/** + * Implementation of the ScummVM file system API. + * + * Parts of this class are documented in the base interface class, AbstractFSNode. + */ +class RISCOSFilesystemNode : public AbstractFSNode { +protected: + Common::String _displayName; + Common::String _path; + bool _isDirectory; + bool _isValid; + + virtual AbstractFSNode *makeNode(const Common::String &path) const { + return new RISCOSFilesystemNode(path); + } + + /** + * Plain constructor, for internal use only (hence protected). + */ + RISCOSFilesystemNode() : _isDirectory(false), _isValid(false) {} +public: + /** + * Creates a RISCOSFilesystemNode for a given path. + * + * @param path the path the new node should point to. + */ + RISCOSFilesystemNode(const Common::String &path); + + virtual bool exists() const; + virtual Common::String getDisplayName() const { return _displayName; } + virtual Common::String getName() const { return _displayName; } + virtual Common::String getPath() const { return _path; } + virtual bool isDirectory() const { return _isDirectory; } + virtual bool isReadable() const; + virtual bool isWritable() const; + + virtual AbstractFSNode *getChild(const Common::String &n) const; + virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const; + virtual AbstractFSNode *getParent() const; + + virtual Common::SeekableReadStream *createReadStream(); + virtual Common::WriteStream *createWriteStream(); + virtual bool create(bool isDirectoryFlag); + +private: + /** + * Converts a Unix style path to a RISC OS style path. + * + * @param str Unix style path to convert. + * @return RISC OS style path. + */ + static Common::String toRISCOS(Common::String &path); + + /** + * Converts a RISC OS style path to a Unix style path. + * + * @param str RISC OS style path to convert. + * @return Unix style path. + */ + static Common::String toUnix(Common::String &path); + +}; + +namespace Riscos { + +/** + * Assure that a directory path exists. + * + * @param dir The path which is required to exist. + * @param prefix An (optional) prefix which should not be created if non existent. + * prefix is prepended to dir if supplied. + * @return true in case the directoy exists (or was created), false otherwise. + */ +bool assureDirectoryExists(const Common::String &dir, const char *prefix = nullptr); + +} // End of namespace RISCOS + +#endif -- cgit v1.2.3