Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "backends/fs/abstract-fs.h"
const char *AbstractFSNode::lastPathComponent(const Common::String &str, const char sep) {
// TODO: Get rid of this eventually! Use Common::lastPathComponent instead
if (str.empty())
return "";
const char *start = str.c_str();
const char *cur = start + str.size() - 2;
while (cur >= start && *cur != sep) {
--cur;
}
return cur + 1;
}
Common::SeekableReadStream *AbstractFSNode::createReadStreamForAltStream(Common::AltStreamType altStreamType) {
return nullptr;
}

219
backends/fs/abstract-fs.h Normal file
View File

@@ -0,0 +1,219 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_ABSTRACT_FS_H
#define BACKENDS_ABSTRACT_FS_H
#include "common/array.h"
#include "common/str.h"
#include "common/fs.h"
class AbstractFSNode;
typedef Common::Array<AbstractFSNode *> AbstractFSList;
/**
* Abstract file system node. Private subclasses implement the actual
* functionality.
*
* Most of the methods correspond directly to methods in class FSNode,
* so if they are not documented here, look there for more information about
* the semantics.
*/
class AbstractFSNode {
protected:
friend class Common::FSNode;
typedef Common::FSNode::ListMode ListMode;
/**
* Returns the child node with the given name. When called on a non-directory
* node, it should handle this gracefully by returning 0.
* When called with a name not matching any of the files/dirs contained in this
* directory, a valid node should be returned, which returns 'false' upon calling
* the exists() method. The idea is that this node can then still can be used to
* create a new file via the createWriteStream() method.
*
* Example:
* Calling getChild() for a node with path "/foo/bar" using name="file.txt",
* would produce a new node with "/foo/bar/file.txt" as path.
*
* @note Handling calls on non-dir nodes gracefully makes it possible to
* switch to a lazy type detection scheme in the future.
*
* @param name String containing the name of the child to create a new node.
*/
virtual AbstractFSNode *getChild(const Common::String &name) const = 0;
/**
* The parent node of this directory.
* The parent of the root is the root itself.
*/
virtual AbstractFSNode *getParent() const = 0;
/**
* Returns the last component of a given path.
*
* Examples:
* /foo/bar.txt would return /bar.txt
* /foo/bar/ would return /bar/
*
* @param str String containing the path.
* @param sep character used to separate path components
* @return Pointer to the first char of the last component inside str.
*/
static const char *lastPathComponent(const Common::String &str, const char sep);
public:
/**
* Construct a FSNode object from an AbstractFSNode object.
*
* This is a helper to create Common::FSNode objects when the backend's
* FileSystemFactory cannot create the given AbstractFSNode object itself.
* All other code is supposed to use Common::FSNode's constructor itself.
*
* @param realNode Pointer to a heap allocated instance. FSNode will take
* ownership of the pointer.
*/
static Common::FSNode makeFSNode(AbstractFSNode *realNode) {
return Common::FSNode(realNode);
}
/**
* Destructor.
*/
virtual ~AbstractFSNode() {}
/*
* Indicates whether the object referred by this path exists in the filesystem or not.
*/
virtual bool exists() const = 0;
/**
* Return a list of child nodes of this directory node. If called on a node
* that does not represent a directory, false is returned.
*
* @param list List to put the contents of the directory in.
* @param mode Mode to use while listing the directory.
* @param hidden Whether to include hidden files or not in the results.
*
* @return true if successful, false otherwise (e.g. when the directory does not exist).
*/
virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const = 0;
/**
* Returns a human readable path string.
*
* @note By default, this method returns the value of getName().
*/
virtual Common::U32String getDisplayName() const = 0;
/**
* Returns the last component of the path pointed by this FSNode.
*
* Examples (POSIX):
* /foo/bar.txt would return /bar.txt
* /foo/bar/ would return /bar/
*
* @note This method is very architecture dependent, please check the concrete implementation for more information.
*/
virtual Common::String getName() const = 0;
/**
* Returns the 'path' of the current node, usable in fopen().
*/
virtual Common::String getPath() const = 0;
/**
* Indicates whether this path refers to a directory or not.
*/
virtual bool isDirectory() const = 0;
/**
* Indicates whether the object referred by this path can be read from or not.
*
* If the path refers to a directory, readability implies being able to read
* and list the directory entries.
*
* If the path refers to a file, readability implies being able to read the
* contents of the file.
*
* @return bool true if the object can be read, false otherwise.
*/
virtual bool isReadable() const = 0;
/**
* Indicates whether the object referred by this path can be written to or not.
*
* If the path refers to a directory, writability implies being able to modify
* the directory entry (i.e. rename the directory, remove it or write files inside of it).
*
* If the path refers to a file, writability implies being able to write data
* to the file.
*
* @return bool true if the object can be written to, false otherwise.
*/
virtual bool isWritable() const = 0;
/**
* Creates a SeekableReadStream instance corresponding to the file
* referred by this node. This assumes that the node actually refers
* to a readable file. If this is not the case, 0 is returned.
*
* @return pointer to the stream object, 0 in case of a failure
*/
virtual Common::SeekableReadStream *createReadStream() = 0;
/**
* Creates a SeekableReadStream instance corresponding to an alternate
* stream of the file referred by this node. This assumes that the node
* actually refers to a readable file and the alt stream exists.
* If either is not the case, 0 is returned.
*
* @return pointer to the stream object, 0 in case of a failure
*/
virtual Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType);
/**
* Creates a WriteStream instance corresponding to the file
* referred by this node. This assumes that the node actually refers
* to a readable file. If this is not the case, 0 is returned.
*
* When an atomic write stream is requested, the backend will write
* the data in a temporary file before moving it to its final destination.
*
* @param atomic Request for an atomic file write when closing.
*
* @return pointer to the stream object, 0 in case of a failure
*/
virtual Common::SeekableWriteStream *createWriteStream(bool atomic) = 0;
/**
* Creates a directory referred by this node.
*
* @return true if the directory is created successfully
*/
virtual bool createDirectory() = 0;
};
#endif //BACKENDS_ABSTRACT_FS_H

View File

@@ -0,0 +1,38 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__amigaos4__)
#include "backends/fs/amigaos/amigaos-fs-factory.h"
#include "backends/fs/amigaos/amigaos-fs.h"
AbstractFSNode *AmigaOSFilesystemFactory::makeRootFileNode() const {
return new AmigaOSFilesystemNode();
}
AbstractFSNode *AmigaOSFilesystemFactory::makeCurrentDirectoryFileNode() const {
return new AmigaOSFilesystemNode();
}
AbstractFSNode *AmigaOSFilesystemFactory::makeFileNodePath(const Common::String &path) const {
return new AmigaOSFilesystemNode(path);
}
#endif

View File

@@ -0,0 +1,39 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AMIGAOS_FILESYSTEM_FACTORY_H
#define AMIGAOS_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* Creates AmigaOSFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class AmigaOSFilesystemFactory final : public FilesystemFactory {
public:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*AMIGAOS_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,376 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__amigaos4__)
#include "backends/fs/amigaos/amigaos-fs.h"
#include "backends/fs/stdiostream.h"
#include "common/debug.h"
#include "common/util.h"
/**
* Returns the last component of a given path
*
* @param str Common::String containing the path
* @return Pointer to the first char of the last component inside str
*/
const char *lastPathComponent(const Common::String &str) {
int pathOffset = str.size();
if (pathOffset <= 0) {
debug(6, "lastPathComponent() failed -> Bad offset (Empty path received)!");
return 0;
}
const char *p = str.c_str();
while (pathOffset > 0 && (p[pathOffset - 1] == '/' || p[pathOffset - 1] == ':'))
pathOffset--;
while (pathOffset > 0 && (p[pathOffset - 1] != '/' && p[pathOffset - 1] != ':'))
pathOffset--;
return p + pathOffset;
}
AmigaOSFilesystemNode::AmigaOSFilesystemNode() {
_sDisplayName = "Available HDDs/Partitions";
_bIsValid = true;
_bIsDirectory = true;
_sPath = "";
_pFileLock = 0;
// Protection is ignored for the root volume
_nProt = 0;
}
AmigaOSFilesystemNode::AmigaOSFilesystemNode(const Common::String &p) {
int nodeOffset = p.size();
if (nodeOffset <= 0) {
debug(6, "AmigaOSFileSystemNode() failed -> Bad offset (No Path received)!");
return;
}
_sPath = p;
_sDisplayName = ::lastPathComponent(_sPath);
_pFileLock = 0;
_bIsDirectory = false;
_bIsValid = false;
// Check whether the node exists and if it's a directory
struct ExamineData * pExd = IDOS->ExamineObjectTags(EX_StringNameInput,_sPath.c_str(),TAG_END);
if (pExd) {
_nProt = pExd->Protection;
if (EXD_IS_DIRECTORY(pExd)) {
_bIsDirectory = true;
_pFileLock = IDOS->Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK);
_bIsValid = (_pFileLock != 0);
// Add a trailing slash, if needed
if (_sPath.lastChar() != '/' && _sPath.lastChar() != ':')
_sPath += '/';
} else {
_bIsValid = true;
}
IDOS->FreeDosObject(DOS_EXAMINEDATA, pExd);
}
}
AmigaOSFilesystemNode::AmigaOSFilesystemNode(BPTR pLock, const char *pDisplayName) {
int bufSize = MAXPATHLEN;
_pFileLock = 0;
while (true) {
char *n = new char[bufSize];
if (IDOS->NameFromLock(pLock, (STRPTR)n, bufSize) != DOSFALSE) {
_sPath = n;
_sDisplayName = pDisplayName ? pDisplayName : IDOS->FilePart((STRPTR)n);
delete[] n;
break;
}
if (IDOS->IoErr() != ERROR_LINE_TOO_LONG) {
_bIsValid = false;
debug(6, "IDOS->IoErr() failed -> MAXPATHLEN exceeded!");
delete[] n;
return;
}
bufSize *= 2;
delete[] n;
}
_bIsDirectory = false;
_bIsValid = false;
// Check whether the node exists and if it's a directory
struct ExamineData * pExd = IDOS->ExamineObjectTags(EX_FileLockInput,pLock,TAG_END);
if (pExd) {
_nProt = pExd->Protection;
if (EXD_IS_DIRECTORY(pExd)) {
_bIsDirectory = true;
_pFileLock = IDOS->DupLock(pLock);
_bIsValid = _pFileLock != 0;
// Add a trailing slash, if needed
if (_sPath.lastChar() != '/' && _sPath.lastChar() != ':')
_sPath += '/';
} else {
_bIsValid = true;
}
IDOS->FreeDosObject(DOS_EXAMINEDATA, pExd);
} else {
debug(6, "IDOS->ExamineObjectTags() failed -> Not a directory (or it doesn't exist)!");
}
}
// We need the custom copy constructor because of DupLock()
AmigaOSFilesystemNode::AmigaOSFilesystemNode(const AmigaOSFilesystemNode& node)
: AbstractFSNode() {
_sDisplayName = node._sDisplayName;
_bIsValid = node._bIsValid;
_bIsDirectory = node._bIsDirectory;
_sPath = node._sPath;
_pFileLock = IDOS->DupLock(node._pFileLock);
_nProt = node._nProt;
}
AmigaOSFilesystemNode::~AmigaOSFilesystemNode() {
if (_pFileLock)
IDOS->UnLock(_pFileLock);
}
bool AmigaOSFilesystemNode::exists() const {
if (_sPath.empty())
return false;
bool nodeExists = false;
BPTR pLock = IDOS->Lock(_sPath.c_str(), SHARED_LOCK);
if (pLock) {
nodeExists = true;
IDOS->UnLock(pLock);
}
return nodeExists;
}
AbstractFSNode *AmigaOSFilesystemNode::getChild(const Common::String &n) const {
if (!_bIsDirectory) {
debug(6, "AmigaOSFileSystemNode::getChild() failed -> Not a directory!");
return 0;
}
Common::String newPath(_sPath);
if (_sPath.lastChar() != '/')
newPath += '/';
newPath += n;
return new AmigaOSFilesystemNode(newPath);
}
bool AmigaOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
if (!_bIsValid) {
debug(6, "AmigaOSFileSystemNode::getChildren() failed -> Invalid node (Not a Volume)!");
return false;
}
if (!_bIsDirectory) {
debug(6, "AmigaOSFileSystemNode::getChildren() failed -> Invalid node (Not a directory)!");
return false;
}
if (isRootNode()) {
myList = listVolumes();
return true;
}
APTR context = IDOS->ObtainDirContextTags( EX_FileLockInput, _pFileLock,
EX_DoCurrentDir, TRUE, /* for softlinks */
EX_DataFields, (EXF_NAME|EXF_LINK|EXF_TYPE),
TAG_END);
if (context) {
// No need to free the value after usage, everything will be dealt with
// by the DirContext release
struct ExamineData * pExd = NULL;
AmigaOSFilesystemNode *entry;
while ((pExd = IDOS->ExamineDir(context))) {
if ((EXD_IS_FILE(pExd) && (Common::FSNode::kListFilesOnly == mode))
|| (EXD_IS_DIRECTORY(pExd) && (Common::FSNode::kListDirectoriesOnly == mode))
|| Common::FSNode::kListAll == mode
)
{
BPTR pLock = IDOS->Lock(pExd->Name, SHARED_LOCK);
if (pLock) {
entry = new AmigaOSFilesystemNode(pLock, pExd->Name);
if (entry) {
myList.push_back(entry);
}
IDOS->UnLock(pLock);
}
}
}
if (ERROR_NO_MORE_ENTRIES != IDOS->IoErr()) {
debug(6, "IDOS->ExamineDir() failed -> End of list exceeded!");
return false;
}
IDOS->ReleaseDirContext(context);
return true;
} else {
debug(6, "IDOS->ObtainDirContext() failed!");
return false;
}
}
AbstractFSNode *AmigaOSFilesystemNode::getParent() const {
if (isRootNode()) {
return new AmigaOSFilesystemNode(*this);
}
BPTR pLock = _pFileLock;
if (!_bIsDirectory) {
assert(!pLock);
pLock = IDOS->Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK);
assert(pLock);
}
AmigaOSFilesystemNode *node;
BPTR parentDir = IDOS->ParentDir(pLock);
if (parentDir) {
node = new AmigaOSFilesystemNode(parentDir);
IDOS->UnLock(parentDir);
} else
node = new AmigaOSFilesystemNode();
if (!_bIsDirectory) {
IDOS->UnLock(pLock);
}
return node;
}
bool AmigaOSFilesystemNode::isReadable() const {
if (!_bIsValid)
return false;
// Regular RWED protection flags are low-active or inverted,
// thus the negation. Moreover, a pseudo root filesystem is
// always readable, whatever the protection says
bool readable = !(_nProt & EXDF_OTR_READ) || isRootNode();
return readable;
}
bool AmigaOSFilesystemNode::isWritable() const {
if (!_bIsValid)
return false;
// Regular RWED protection flags are low-active or inverted,
// thus the negation. Moreover, a pseudo root filesystem is
// never writable (due of it's pseudo nature), whatever the protection says
bool writable = !(_nProt & EXDF_OTR_WRITE) && !isRootNode();
return writable;
}
AbstractFSList AmigaOSFilesystemNode::listVolumes() const {
AbstractFSList myList;
const uint32 kLockFlags = LDF_READ | LDF_VOLUMES;
char buffer[MAXPATHLEN];
struct DosList *dosList = IDOS->LockDosList(kLockFlags);
if (!dosList) {
debug(6, "IDOS->LockDOSList() failed! -> No Volumes found!");
return myList;
}
dosList = IDOS->NextDosEntry(dosList, LDF_VOLUMES);
while (dosList) {
if (dosList->dol_Type == DLT_VOLUME &&
dosList->dol_Name && dosList->dol_Port) {
// Copy name to buffer
IDOS->CopyStringBSTRToC(dosList->dol_Name, buffer, MAXPATHLEN);
// Volume name + '\0'
size_t volNameSize = strlen(buffer) + 1;
char *volName = new char [volNameSize];
Common::strcpy_s(volName, volNameSize, buffer);
Common::strcat_s(buffer, ":");
BPTR volumeLock = IDOS->Lock((STRPTR)buffer, SHARED_LOCK);
if (volumeLock) {
char *devName = new char [MAXPATHLEN];
// Find device name
IDOS->DevNameFromLock(volumeLock, devName, MAXPATHLEN, DN_DEVICEONLY);
snprintf(buffer, MAXPATHLEN, "%s (%s)", volName, devName);
delete[] devName;
AmigaOSFilesystemNode *entry = new AmigaOSFilesystemNode(volumeLock, buffer);
if (entry) {
myList.push_back(entry);
}
IDOS->UnLock(volumeLock);
}
delete[] volName;
}
dosList = IDOS->NextDosEntry(dosList, LDF_VOLUMES);
}
IDOS->UnLockDosList(kLockFlags);
return myList;
}
Common::SeekableReadStream *AmigaOSFilesystemNode::createReadStream() {
return StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
}
Common::SeekableWriteStream *AmigaOSFilesystemNode::createWriteStream(bool atomic) {
return StdioStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
}
bool AmigaOSFilesystemNode::createDirectory() {
Common::String createPath = _sPath;
if (createPath.lastChar() == '/') {
createPath = createPath.substr(0, createPath.size() - 1);
}
BPTR dirLock = IDOS->CreateDir(createPath.c_str());
if (dirLock) {
IDOS->UnLock(dirLock);
_bIsValid = true;
_bIsDirectory = true;
} else {
debug(6, "AmigaOSFilesystemNode::createDirectory() failed -> Directory '%s' could not be created!", createPath.c_str());
}
return _bIsValid && _bIsDirectory;
}
#endif

View File

@@ -0,0 +1,122 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AMIGAOS_FILESYSTEM_H
#define AMIGAOS_FILESYSTEM_H
#ifdef __USE_INLINE__
#undef __USE_INLINE__
#endif
#include <proto/exec.h>
#include <proto/dos.h>
#include <stdio.h>
#ifndef USE_NEWLIB
#include <strings.h>
#endif
#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 AmigaOSFilesystemNode final : public AbstractFSNode {
protected:
/**
* The main file lock.
* If this is NULL but _bIsValid is true, then this Node references
* the virtual filesystem root.
*/
BPTR _pFileLock;
Common::String _sDisplayName;
Common::String _sPath;
bool _bIsDirectory;
bool _bIsValid;
uint32 _nProt;
/**
* Creates a list with all the volumes present in the root node.
*/
virtual AbstractFSList listVolumes() const;
/**
* True if this is the pseudo root filesystem.
*/
bool isRootNode() const { return _bIsValid && _bIsDirectory && _pFileLock == 0; }
public:
/**
* Creates an AmigaOSFilesystemNode with the root node as path.
*/
AmigaOSFilesystemNode();
/**
* Creates an AmigaOSFilesystemNode for a given path.
*
* @param path Common::String with the path the new node should point to.
*/
AmigaOSFilesystemNode(const Common::String &p);
/**
* Creates an AmigaOSFilesystemNode given its lock and display name.
*
* @param pLock BPTR to the lock.
* @param pDisplayName name to be used for display, in case not supplied the FilePart() of the filename will be used.
*
* @note This shouldn't even be public as it's only internally, at best it should have been protected if not private.
*/
AmigaOSFilesystemNode(BPTR pLock, const char *pDisplayName = 0);
/**
* Copy constructor.
*
* @note Needed because it duplicates the file lock.
*/
AmigaOSFilesystemNode(const AmigaOSFilesystemNode &node);
/**
* Destructor.
*/
~AmigaOSFilesystemNode() override;
bool exists() const override;
Common::U32String getDisplayName() const override { return _sDisplayName; }
Common::String getName() const override { return _sDisplayName; }
Common::String getPath() const override { return _sPath; }
bool isDirectory() const override { return _bIsDirectory; }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
};
#endif

View File

@@ -0,0 +1,131 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "backends/platform/android/jni-android.h"
#include "backends/fs/android/android-fs-factory.h"
#include "backends/fs/android/android-posix-fs.h"
#include "backends/fs/android/android-saf-fs.h"
namespace Common {
DECLARE_SINGLETON(AndroidFilesystemFactory);
}
AndroidFilesystemFactory::AndroidFilesystemFactory() : _withSAF(false), _config(this) {
}
void AndroidFilesystemFactory::initSAF() {
_withSAF = true;
AndroidSAFFilesystemNode::initJNI();
}
AbstractFSNode *AndroidFilesystemFactory::makeRootFileNode() const {
return new AndroidPOSIXFilesystemNode(_config);
}
AbstractFSNode *AndroidFilesystemFactory::makeCurrentDirectoryFileNode() const {
// As current working directory can point outside of our data don't take any risk
return makeRootFileNode();
}
AbstractFSNode *AndroidFilesystemFactory::makeFileNodePath(const Common::String &path) const {
if (path.empty() || path.equals("/")) {
return makeRootFileNode();
}
// If SAF works, it was a SAF URL
if (_withSAF) {
// Accept /saf as it can be used to create the tree in DumpFile
if (path == AddSAFFakeNode::SAF_ADD_FAKE_PATH) {
// Not a SAF mount point
return new AddSAFFakeNode(true);
}
AbstractFSNode *node = AndroidSAFFilesystemNode::makeFromPath(path);
if (node) {
return node;
}
}
return new AndroidPOSIXFilesystemNode(path, _config);
}
AndroidFilesystemFactory::Config::Config(const AndroidFilesystemFactory *factory) : _factory(factory),
_storages(JNI::getAllStorageLocations()) {
}
void AndroidFilesystemFactory::getSAFTrees(AbstractFSList &list, bool allowSAFadd) const {
if (!_withSAF) {
// Nothing if no SAF
return;
}
Common::Array<jobject> trees = JNI::getSAFTrees();
list.reserve(trees.size() + (allowSAFadd ? 1 : 0));
for (Common::Array<jobject>::iterator it = trees.begin(); it != trees.end(); it++) {
AbstractFSNode *node = AndroidSAFFilesystemNode::makeFromTree(*it);
if (!node) {
continue;
}
list.push_back(node);
}
if (allowSAFadd) {
list.push_back(new AddSAFFakeNode(false));
}
}
bool AndroidFilesystemFactory::Config::getDrives(AbstractFSList &list, bool hidden) const {
// For SAF
_factory->getSAFTrees(list, true);
list.reserve(list.size() + _storages.size() / 2);
// For old POSIX way
for (Common::Array<Common::String>::const_iterator it = _storages.begin(); it != _storages.end(); ++it) {
const Common::String &driveName = *it;
++it;
const Common::String &drivePath = *it;
AndroidPOSIXFilesystemNode *node = new AndroidPOSIXFilesystemNode(drivePath, *this);
node->_displayName = driveName;
list.push_back(node);
}
return true;
}
bool AndroidFilesystemFactory::Config::isDrive(const Common::String &path) const {
// This function is called from DrivePOSIXFilesystemNode::isDrive
// DrivePOSIXFilesystemNode is only used for POSIX code so no need to look for SAF
for (Common::Array<Common::String>::const_iterator it = _storages.begin(); it != _storages.end(); it++) {
++it;
if (*it == path) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,63 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANDROID_FILESYSTEM_FACTORY_H
#define ANDROID_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
#include "common/singleton.h"
#include "backends/fs/posix-drives/posix-drives-fs.h"
/**
* Creates AndroidFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class AndroidFilesystemFactory final : public FilesystemFactory,
public Common::Singleton<AndroidFilesystemFactory> {
friend class Common::Singleton<SingletonBaseType>;
protected:
AndroidFilesystemFactory();
public:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
void initSAF();
bool hasSAF() const { return _withSAF; }
void getSAFTrees(AbstractFSList &list, bool allowSAFadd) const;
private:
struct Config : public DrivePOSIXFilesystemNode::Config {
Config(const AndroidFilesystemFactory *factory);
bool getDrives(AbstractFSList &list, bool hidden) const override;
bool isDrive(const Common::String &path) const override;
private:
const AndroidFilesystemFactory *_factory;
Common::Array<Common::String> _storages;
};
bool _withSAF;
Config _config;
};
#endif /*ANDROID_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,35 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANDROID_FILESYSTEM_H
#define ANDROID_FILESYSTEM_H
/**
* Common interface for Android filesystem types: SAF and POSIX
* Currently, only a remove function to delete files.
*/
class AndroidFSNode {
public:
virtual ~AndroidFSNode() {}
virtual int remove() = 0;
};
#endif

View File

@@ -0,0 +1,44 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// For remove()
#include <stdio.h>
#include <errno.h>
#include "backends/fs/android/android-fs-factory.h"
#include "backends/fs/android/android-posix-fs.h"
#include "backends/fs/android/android-saf-fs.h"
AbstractFSNode *AndroidPOSIXFilesystemNode::makeNode() const {
return new AndroidPOSIXFilesystemNode(_config);
}
AbstractFSNode *AndroidPOSIXFilesystemNode::makeNode(const Common::String &path) const {
return AndroidFilesystemFactory::instance().makeFileNodePath(path);
}
int AndroidPOSIXFilesystemNode::remove() {
if (::remove(_path.c_str()) != 0)
return errno;
setFlags();
return 0;
}

View File

@@ -0,0 +1,45 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANDROID_FILESYSTEM_ROOT_H
#define ANDROID_FILESYSTEM_ROOT_H
#include "backends/fs/posix-drives/posix-drives-fs.h"
#include "backends/fs/android/android-fs.h"
class AndroidPOSIXFilesystemNode : public DrivePOSIXFilesystemNode, public AndroidFSNode {
// To let the factory redefine the displayed name
friend class AndroidFilesystemFactory;
protected:
AbstractFSNode *makeNode() const override;
AbstractFSNode *makeNode(const Common::String &path) const override;
int remove() override;
public:
AndroidPOSIXFilesystemNode(const Common::String &path, const Config &config)
: DrivePOSIXFilesystemNode(path, config) { }
AndroidPOSIXFilesystemNode(const Config &config)
: DrivePOSIXFilesystemNode(config) { _path = "/"; }
};
#endif

View File

@@ -0,0 +1,930 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Allow use of stuff in <time.h> and abort()
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_abort
// Disable printf override in common/forbidden.h to avoid
// clashes with log.h from the Android SDK.
// That header file uses
// __attribute__ ((format(printf, 3, 4)))
// which gets messed up by our override mechanism; this could
// be avoided by either changing the Android SDK to use the equally
// legal and valid
// __attribute__ ((format(__printf__, 3, 4)))
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the Android port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
// Allow calling of fdopen
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
// Allow calling of close system call
#include <unistd.h>
#include <errno.h> // For remove error codes
#include "backends/platform/android/android.h"
#include "backends/platform/android/jni-android.h"
#include "backends/fs/android/android-fs-factory.h"
#include "backends/fs/android/android-saf-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/debug.h"
#include "common/translation.h"
#include "common/util.h"
jclass AndroidSAFFilesystemNode::_CLS_SAFFSTree = nullptr;
jmethodID AndroidSAFFilesystemNode::_MID_addNodeRef = 0;
jmethodID AndroidSAFFilesystemNode::_MID_decNodeRef = 0;
jmethodID AndroidSAFFilesystemNode::_MID_refToNode = 0;
jmethodID AndroidSAFFilesystemNode::_MID_getTreeId = 0;
jmethodID AndroidSAFFilesystemNode::_MID_pathToNode = 0;
jmethodID AndroidSAFFilesystemNode::_MID_getChildren = 0;
jmethodID AndroidSAFFilesystemNode::_MID_getChild = 0;
jmethodID AndroidSAFFilesystemNode::_MID_createDirectory = 0;
jmethodID AndroidSAFFilesystemNode::_MID_createFile = 0;
jmethodID AndroidSAFFilesystemNode::_MID_createReadStream = 0;
jmethodID AndroidSAFFilesystemNode::_MID_createWriteStream = 0;
jmethodID AndroidSAFFilesystemNode::_MID_removeNode = 0;
jmethodID AndroidSAFFilesystemNode::_MID_removeTree = 0;
jfieldID AndroidSAFFilesystemNode::_FID__treeName = 0;
jfieldID AndroidSAFFilesystemNode::_FID__root = 0;
jmethodID AndroidSAFFilesystemNode::_MID_addRef = 0;
jfieldID AndroidSAFFilesystemNode::_FID__parent = 0;
jfieldID AndroidSAFFilesystemNode::_FID__path = 0;
jfieldID AndroidSAFFilesystemNode::_FID__documentId = 0;
jfieldID AndroidSAFFilesystemNode::_FID__flags = 0;
bool AndroidSAFFilesystemNode::_JNIinit = false;
const char AndroidSAFFilesystemNode::SAF_MOUNT_POINT[] = "/saf/";
void AndroidSAFFilesystemNode::initJNI() {
if (_JNIinit) {
return;
}
JNIEnv *env = JNI::getEnv();
// We can't call error here as the backend is not built yet
#define FIND_STATIC_METHOD(prefix, name, signature) do { \
_MID_ ## prefix ## name = env->GetStaticMethodID(cls, #name, signature); \
if (_MID_ ## prefix ## name == 0) { \
LOGE("Can't find method ID " #name); \
abort(); \
} \
} while (0)
#define FIND_METHOD(prefix, name, signature) do { \
_MID_ ## prefix ## name = env->GetMethodID(cls, #name, signature); \
if (_MID_ ## prefix ## name == 0) { \
LOGE("Can't find method ID " #name); \
abort(); \
} \
} while (0)
#define FIND_FIELD(prefix, name, signature) do { \
_FID_ ## prefix ## name = env->GetFieldID(cls, #name, signature); \
if (_FID_ ## prefix ## name == 0) { \
LOGE("Can't find field ID " #name); \
abort(); \
} \
} while (0)
#define SAFFSNodeSig "Lorg/scummvm/scummvm/SAFFSTree$SAFFSNode;"
jclass cls = env->FindClass("org/scummvm/scummvm/SAFFSTree");
_CLS_SAFFSTree = (jclass)env->NewGlobalRef(cls);
FIND_STATIC_METHOD(, addNodeRef, "(J)V");
FIND_STATIC_METHOD(, decNodeRef, "(J)V");
FIND_STATIC_METHOD(, refToNode, "(J)" SAFFSNodeSig);
FIND_METHOD(, getTreeId, "()Ljava/lang/String;");
FIND_METHOD(, pathToNode, "(Ljava/lang/String;Z)" SAFFSNodeSig);
FIND_METHOD(, getChildren, "(J)[" SAFFSNodeSig);
FIND_METHOD(, getChild, "(JLjava/lang/String;)" SAFFSNodeSig);
FIND_METHOD(, createDirectory, "(JLjava/lang/String;)" SAFFSNodeSig);
FIND_METHOD(, createFile, "(JLjava/lang/String;)" SAFFSNodeSig);
FIND_METHOD(, createReadStream, "(J)I");
FIND_METHOD(, createWriteStream, "(J)I");
FIND_METHOD(, removeNode, "(J)I");
FIND_METHOD(, removeTree, "()V");
FIND_FIELD(, _treeName, "Ljava/lang/String;");
FIND_FIELD(, _root, SAFFSNodeSig);
env->DeleteLocalRef(cls);
cls = env->FindClass("org/scummvm/scummvm/SAFFSTree$SAFFSNode");
FIND_METHOD(, addRef, "()J");
FIND_FIELD(, _parent, SAFFSNodeSig);
FIND_FIELD(, _path, "Ljava/lang/String;");
FIND_FIELD(, _documentId, "Ljava/lang/String;");
FIND_FIELD(, _flags, "I");
env->DeleteLocalRef(cls);
#undef SAFFSNodeSig
#undef FIND_FIELD
#undef FIND_METHOD
#undef FIND_STATIC_METHOD
_JNIinit = true;
}
void AndroidSAFFilesystemNode::GlobalRef::Deleter::operator()(_jobject *obj) {
JNIEnv *env = JNI::getEnv();
env->DeleteGlobalRef((jobject)obj);
}
void AndroidSAFFilesystemNode::NodeRef::reset() {
if (_ref == 0) {
return;
}
JNIEnv *env = JNI::getEnv();
env->CallStaticVoidMethod(_CLS_SAFFSTree, _MID_decNodeRef, _ref);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::decNodeRef failed");
env->ExceptionDescribe();
env->ExceptionClear();
}
_ref = 0;
}
void AndroidSAFFilesystemNode::NodeRef::reset(const NodeRef &r) {
if (_ref == 0 && r._ref == 0) {
return;
}
JNIEnv *env = JNI::getEnv();
if (_ref) {
env->CallStaticVoidMethod(_CLS_SAFFSTree, _MID_decNodeRef, _ref);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::decNodeRef failed");
env->ExceptionDescribe();
env->ExceptionClear();
}
}
_ref = r._ref;
if (!_ref) {
return;
}
env->CallStaticVoidMethod(_CLS_SAFFSTree, _MID_addNodeRef, _ref);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::addNodeRef failed");
env->ExceptionDescribe();
env->ExceptionClear();
_ref = 0;
abort();
}
}
void AndroidSAFFilesystemNode::NodeRef::reset(JNIEnv *env, jobject node) {
if (_ref == 0 && node == nullptr) {
return;
}
if (_ref) {
env->CallStaticVoidMethod(_CLS_SAFFSTree, _MID_decNodeRef, _ref);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::decNodeRef failed");
env->ExceptionDescribe();
env->ExceptionClear();
}
}
if (node == nullptr) {
_ref = 0;
return;
}
_ref = env->CallLongMethod(node, _MID_addRef);
if (env->ExceptionCheck()) {
LOGE("SAFFSNode::addRef failed");
env->ExceptionDescribe();
env->ExceptionClear();
_ref = 0;
abort();
}
assert(_ref != 0);
}
jobject AndroidSAFFilesystemNode::NodeRef::localRef(JNIEnv *env) const {
if (_ref == 0) {
return nullptr;
}
jobject localRef = env->CallStaticObjectMethod(_CLS_SAFFSTree, _MID_refToNode, _ref);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::refToNode failed");
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
return localRef;
}
AndroidSAFFilesystemNode *AndroidSAFFilesystemNode::makeFromPath(const Common::String &path) {
if (!path.hasPrefix(SAF_MOUNT_POINT)) {
// Not a SAF mount point
return nullptr;
}
// Path is in the form /saf/<treeid>/<path>
size_t pos = path.findFirstOf('/', sizeof(SAF_MOUNT_POINT) - 1);
Common::String treeId;
Common::String realPath;
if (pos == Common::String::npos) {
treeId = path.substr(sizeof(SAF_MOUNT_POINT) - 1);
} else {
treeId = path.substr(sizeof(SAF_MOUNT_POINT) - 1, pos - sizeof(SAF_MOUNT_POINT) + 1);
realPath = path.substr(pos);
}
jobject safTree = JNI::findSAFTree(treeId);
if (!safTree) {
LOGW("AndroidSAFFilesystemNode::makeFromPath: tree id %s not found", treeId.c_str());
return nullptr;
}
JNIEnv *env = JNI::getEnv();
jstring pathObj = env->NewStringUTF(realPath.c_str());
jobject node = env->CallObjectMethod(safTree, _MID_pathToNode, pathObj, false);
env->DeleteLocalRef(pathObj);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::pathToNode failed");
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(safTree);
return nullptr;
}
if (node) {
AndroidSAFFilesystemNode *ret = new AndroidSAFFilesystemNode(GlobalRef(env, safTree), node);
env->DeleteLocalRef(node);
env->DeleteLocalRef(safTree);
return ret;
}
// Node doesn't exist: we will try to make a node from the parent and
// if it works we will create a non-existent node
pos = realPath.findLastOf('/');
if (pos == Common::String::npos || pos == 0) {
// No / in path or at root, no parent and we have a tree: it's all good
if (pos == 0) {
realPath = realPath.substr(1);
}
AndroidSAFFilesystemNode *parent = makeFromTree(safTree);
AndroidSAFFilesystemNode *ret = static_cast<AndroidSAFFilesystemNode *>(parent->getChild(realPath));
delete parent;
// safTree has already been released by makeFromTree
return ret;
}
Common::String baseName(realPath.substr(pos + 1));
realPath.erase(pos);
pathObj = env->NewStringUTF(realPath.c_str());
node = env->CallObjectMethod(safTree, _MID_pathToNode, pathObj, false);
env->DeleteLocalRef(pathObj);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::pathToNode failed");
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(safTree);
return nullptr;
}
if (node) {
AndroidSAFFilesystemNode *parent = new AndroidSAFFilesystemNode(GlobalRef(env, safTree), node);
env->DeleteLocalRef(node);
env->DeleteLocalRef(safTree);
AndroidSAFFilesystemNode *ret = static_cast<AndroidSAFFilesystemNode *>(parent->getChild(baseName));
delete parent;
return ret;
}
env->DeleteLocalRef(safTree);
return nullptr;
}
AndroidSAFFilesystemNode *AndroidSAFFilesystemNode::makeFromTree(jobject safTree) {
assert(safTree);
JNIEnv *env = JNI::getEnv();
jobject node = env->GetObjectField(safTree, _FID__root);
if (!node) {
env->DeleteLocalRef(safTree);
return nullptr;
}
AndroidSAFFilesystemNode *ret = new AndroidSAFFilesystemNode(GlobalRef(env, safTree), node);
env->DeleteLocalRef(node);
env->DeleteLocalRef(safTree);
return ret;
}
AndroidSAFFilesystemNode::AndroidSAFFilesystemNode(const GlobalRef &safTree, jobject safNode) :
_flags(0) {
JNIEnv *env = JNI::getEnv();
_safTree = safTree;
assert(_safTree != nullptr);
_safNode.reset(env, safNode);
cacheData(env, safNode);
}
AndroidSAFFilesystemNode::AndroidSAFFilesystemNode(const GlobalRef &safTree, jobject safParent,
const Common::String &path, const Common::String &name) : _flags(0) {
JNIEnv *env = JNI::getEnv();
_safTree = safTree;
_safParent.reset(env, safParent);
// In this case _path is the parent
_path = path;
_newName = name;
}
AndroidSAFFilesystemNode::AndroidSAFFilesystemNode(const GlobalRef &safTree,
const NodeRef &safParent, const Common::String &path,
const Common::String &name) : _flags(0) {
_safTree = safTree;
_safParent = safParent;
// In this case _path is the parent
_path = path;
_newName = name;
}
Common::String AndroidSAFFilesystemNode::getName() const {
if (!_safNode || !_safParent) {
// _newName is for non-existent paths or root node pretty name
return _newName;
}
return lastPathComponent(_path, '/');
}
Common::String AndroidSAFFilesystemNode::getPath() const {
assert(_safTree != nullptr);
if (_safNode) {
return _path;
}
// When no node, it means _path is the parent node
return _path + "/" + _newName;
}
AbstractFSNode *AndroidSAFFilesystemNode::getChild(const Common::String &n) const {
assert(_safTree != nullptr);
assert(_safNode);
// Make sure the string contains no slashes
assert(!n.contains('/'));
JNIEnv *env = JNI::getEnv();
jstring name = env->NewStringUTF(n.c_str());
jobject child = env->CallObjectMethod(_safTree, _MID_getChild, _safNode.get(), name);
env->DeleteLocalRef(name);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::getChild failed");
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
if (child) {
AndroidSAFFilesystemNode *ret = new AndroidSAFFilesystemNode(_safTree, child);
env->DeleteLocalRef(child);
return ret;
}
return new AndroidSAFFilesystemNode(_safTree, _safNode, _path, n);
}
bool AndroidSAFFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode,
bool hidden) const {
assert(_flags & DIRECTORY);
assert(_safTree != nullptr);
if (!_safNode) {
return false;
}
JNIEnv *env = JNI::getEnv();
jobjectArray array =
(jobjectArray)env->CallObjectMethod(_safTree, _MID_getChildren, _safNode.get());
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::getChildren failed");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
if (!array) {
// Fetching children failed: a log error has already been produced in Java code
return false;
}
myList.clear();
jsize size = env->GetArrayLength(array);
myList.reserve(size);
for (jsize i = 0; i < size; ++i) {
jobject node = env->GetObjectArrayElement(array, i);
myList.push_back(new AndroidSAFFilesystemNode(_safTree, node));
env->DeleteLocalRef(node);
}
env->DeleteLocalRef(array);
return true;
}
AbstractFSNode *AndroidSAFFilesystemNode::getParent() const {
assert(_safTree != nullptr);
// No need to check for _safNode: if node doesn't exist yet parent is its parent
JNIEnv *env = JNI::getEnv();
if (!_safParent) {
return AndroidFilesystemFactory::instance().makeRootFileNode();
}
jobject parent = _safParent.localRef(env);
assert(parent);
AndroidSAFFilesystemNode *ret = new AndroidSAFFilesystemNode(_safTree, parent);
env->DeleteLocalRef(parent);
return ret;
}
Common::SeekableReadStream *AndroidSAFFilesystemNode::createReadStream() {
assert(_safTree != nullptr);
if (!_safNode) {
return nullptr;
}
JNIEnv *env = JNI::getEnv();
jint fd = env->CallIntMethod(_safTree, _MID_createReadStream, _safNode.get());
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::createReadStream failed");
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
if (fd == -1) {
return nullptr;
}
FILE *f = fdopen(fd, "r");
if (!f) {
close(fd);
return nullptr;
}
return new PosixIoStream(f);
}
Common::SeekableWriteStream *AndroidSAFFilesystemNode::createWriteStream(bool atomic) {
assert(_safTree != nullptr);
JNIEnv *env = JNI::getEnv();
if (!_safNode) {
assert(_safParent);
jstring name = env->NewStringUTF(_newName.c_str());
// TODO: Add atomic support if possible
jobject child = env->CallObjectMethod(_safTree, _MID_createFile, _safParent.get(), name);
env->DeleteLocalRef(name);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::createFile failed");
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
if (!child) {
return nullptr;
}
_safNode.reset(env, child);
cacheData(env, child);
env->DeleteLocalRef(child);
}
jint fd = env->CallIntMethod(_safTree, _MID_createWriteStream, _safNode.get());
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::createWriteStream failed");
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
if (fd == -1) {
return nullptr;
}
FILE *f = fdopen(fd, "w");
if (!f) {
close(fd);
return nullptr;
}
return new PosixIoStream(f);
}
bool AndroidSAFFilesystemNode::createDirectory() {
assert(_safTree != nullptr);
if (_safNode) {
return _flags & DIRECTORY;
}
assert(_safParent);
JNIEnv *env = JNI::getEnv();
jstring name = env->NewStringUTF(_newName.c_str());
jobject child = env->CallObjectMethod(_safTree, _MID_createDirectory, _safParent.get(), name);
env->DeleteLocalRef(name);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::createDirectory failed");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
if (!child) {
return false;
}
_safNode.reset(env, child);
cacheData(env, child);
env->DeleteLocalRef(child);
return true;
}
int AndroidSAFFilesystemNode::remove() {
assert(_safTree != nullptr);
if (!_safNode) {
return ENOENT;
}
if (!_safParent) {
// It's the root of the tree: we can't delete it
return EPERM;
}
if (isDirectory()) {
// Don't delete folders (yet?)
return EPERM;
}
JNIEnv *env = JNI::getEnv();
jint result = env->CallIntMethod(_safTree, _MID_removeNode, _safNode.get());
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::removeNode failed");
env->ExceptionDescribe();
env->ExceptionClear();
return EIO;
}
if (result) {
return result;
}
_safNode.reset();
// Create the parent node to fetch informations needed to make us a non-existent node
jobject jparent = _safParent.localRef(env);
if (!jparent)
return EIO;
AndroidSAFFilesystemNode *parent = new AndroidSAFFilesystemNode(_safTree, jparent);
env->DeleteLocalRef(jparent);
size_t pos = _path.findLastOf('/');
if (pos == Common::String::npos) {
_newName = _path;
} else {
_newName = _path.substr(pos + 1);
}
_path = parent->_path;
delete parent;
return 0;
}
void AndroidSAFFilesystemNode::removeTree() {
assert(!_safParent);
JNIEnv *env = JNI::getEnv();
env->CallVoidMethod(_safTree, _MID_removeTree);
if (env->ExceptionCheck()) {
LOGE("SAFFSTree::removeTree failed");
env->ExceptionDescribe();
env->ExceptionClear();
}
}
void AndroidSAFFilesystemNode::cacheData(JNIEnv *env, jobject node) {
_flags = env->GetIntField(node, _FID__flags);
jobject safParent = env->GetObjectField(node, _FID__parent);
_safParent.reset(env, safParent);
if (!_safParent) {
jstring nameObj = (jstring)env->GetObjectField(_safTree, _FID__treeName);
const char *nameP = env->GetStringUTFChars(nameObj, 0);
if (nameP != 0) {
_newName = Common::String(nameP);
env->ReleaseStringUTFChars(nameObj, nameP);
}
env->DeleteLocalRef(nameObj);
}
Common::String workingPath;
jstring pathObj = (jstring)env->GetObjectField(node, _FID__path);
const char *path = env->GetStringUTFChars(pathObj, 0);
if (path == nullptr) {
env->DeleteLocalRef(pathObj);
error("SAFFSNode::_path is null");
return;
}
workingPath = Common::String(path);
env->ReleaseStringUTFChars(pathObj, path);
env->DeleteLocalRef(pathObj);
jstring idObj = (jstring)env->CallObjectMethod(_safTree, _MID_getTreeId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
env->ReleaseStringUTFChars(pathObj, path);
env->DeleteLocalRef(pathObj);
error("SAFFSTree::getTreeId failed");
return;
}
if (!idObj) {
error("SAFFSTree::getTreeId returned null");
return;
}
const char *id = env->GetStringUTFChars(idObj, 0);
if (id == nullptr) {
error("Failed to get string from SAFFSTree::getTreeId");
env->DeleteLocalRef(idObj);
return;
}
_path = Common::String(SAF_MOUNT_POINT);
_path += id;
_path += workingPath;
env->ReleaseStringUTFChars(idObj, id);
env->DeleteLocalRef(idObj);
}
const char AddSAFFakeNode::SAF_ADD_FAKE_PATH[] = "/saf";
AddSAFFakeNode::~AddSAFFakeNode() {
delete _proxied;
}
Common::U32String AddSAFFakeNode::getDisplayName() const {
// I18N: This is displayed in the file browser to let the user choose a new folder for Android Storage Attached Framework
return Common::U32String::format("\x01<%s>", _("Add a new folder").c_str());
}
Common::String AddSAFFakeNode::getName() const {
return Common::String::format("\x01<%s>", _("Add a new folder").encode().c_str());
}
AbstractFSNode *AddSAFFakeNode::getChild(const Common::String &name) const {
if (_fromPath) {
// When starting from /saf try to get the tree node
return AndroidSAFFilesystemNode::makeFromPath(Common::String(AndroidSAFFilesystemNode::SAF_MOUNT_POINT) + name);
}
// We can't call getChild as it's protected
return nullptr;
}
AbstractFSNode *AddSAFFakeNode::getParent() const {
// We are always just below the root and getParent is protected
return AndroidFilesystemFactory::instance().makeRootFileNode();
}
bool AddSAFFakeNode::exists() const {
if (_fromPath) {
// /saf always exists when created as a path
return true;
}
if (!_proxied) {
makeProxySAF();
}
if (!_proxied) {
return false;
}
return _proxied->exists();
}
bool AddSAFFakeNode::getChildren(AbstractFSList &list, ListMode mode, bool hidden) const {
if (_fromPath) {
// When built from path, /saf lists all SAF node but never proposes to add one
if (mode == Common::FSNode::kListFilesOnly) {
// All directories
return true;
}
AndroidFilesystemFactory::instance().getSAFTrees(list, false);
return true;
}
if (!_proxied) {
makeProxySAF();
}
if (!_proxied) {
return false;
}
return _proxied->getChildren(list, mode, hidden);
}
Common::String AddSAFFakeNode::getPath() const {
if (_fromPath) {
return SAF_ADD_FAKE_PATH;
}
if (!_proxied) {
makeProxySAF();
}
if (!_proxied) {
return "";
}
return _proxied->getPath();
}
bool AddSAFFakeNode::isReadable() const {
if (_fromPath) {
return true;
}
if (!_proxied) {
makeProxySAF();
}
if (!_proxied) {
return false;
}
return _proxied->isReadable();
}
bool AddSAFFakeNode::isWritable() const {
if (_fromPath) {
return false;
}
if (!_proxied) {
makeProxySAF();
}
if (!_proxied) {
return false;
}
return _proxied->isWritable();
}
int AddSAFFakeNode::remove() {
return EPERM;
}
void AddSAFFakeNode::makeProxySAF() const {
assert(!_fromPath);
if (_proxied) {
return;
}
// I18N: This may be displayed in the Android UI used to add a Storage Attach Framework authorization
jobject saftree = JNI::getNewSAFTree(true, "", _("Choose a new folder"));
if (!saftree) {
return;
}
_proxied = AndroidSAFFilesystemNode::makeFromTree(saftree);
}

View File

@@ -0,0 +1,273 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ANDROID_SAF_FILESYSTEM_H
#define ANDROID_SAF_FILESYSTEM_H
#include <jni.h>
#include "backends/fs/abstract-fs.h"
#include "common/ptr.h"
#include "backends/fs/android/android-fs.h"
/**
* Implementation of the ScummVM file system API.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class AndroidSAFFilesystemNode final : public AbstractFSNode, public AndroidFSNode {
protected:
/**
* A class managing a global reference.
*
* This handles the reference management and avoids duplicating them in JNI.
*/
class GlobalRef final : public Common::SharedPtr<_jobject> {
struct Deleter {
void operator()(_jobject *obj);
};
public:
GlobalRef() : Common::SharedPtr<_jobject>() {}
GlobalRef(const GlobalRef &ref) : Common::SharedPtr<_jobject>(ref) {}
GlobalRef(JNIEnv *env, jobject jobj) : Common::SharedPtr<_jobject>(jobj ? env->NewGlobalRef(jobj) : nullptr, Deleter()) {
// Make sure NewGlobalRef succeeded
assert((jobj == nullptr) == (get() == nullptr));
}
GlobalRef &operator=(const GlobalRef &r) {
Common::SharedPtr<_jobject>::reset(r);
return *this;
}
operator jobject() {
return Common::SharedPtr<_jobject>::get();
}
operator jobject() const {
return Common::SharedPtr<_jobject>::get();
}
};
/**
* A class managing our SAFFSNode references.
*
* Reference counting is managed by SAFFSNode in Java and this class uses
* RAII to call the reference counting methods at the appropriate time.
*/
class NodeRef final {
private:
jlong _ref;
public:
NodeRef() : _ref(0) {}
~NodeRef() { reset(); }
NodeRef(const NodeRef &r) { reset(r); }
NodeRef(JNIEnv *env, jobject node) { reset(env, node); }
void reset();
void reset(const NodeRef &r);
void reset(JNIEnv *env, jobject node);
NodeRef &operator=(const NodeRef &r) {
reset(r);
return *this;
}
bool operator==(const NodeRef &r) const {
return _ref == r._ref;
}
bool operator!=(const NodeRef &r) const {
return _ref != r._ref;
}
explicit operator bool() const {
return _ref != 0;
}
jlong get() const { return _ref; }
jobject localRef(JNIEnv *env) const;
};
// SAFFSTree
static jclass _CLS_SAFFSTree;
static jmethodID _MID_addNodeRef;
static jmethodID _MID_decNodeRef;
static jmethodID _MID_refToNode;
static jmethodID _MID_getTreeId;
static jmethodID _MID_pathToNode;
static jmethodID _MID_getChildren;
static jmethodID _MID_getChild;
static jmethodID _MID_createDirectory;
static jmethodID _MID_createFile;
static jmethodID _MID_createReadStream;
static jmethodID _MID_createWriteStream;
static jmethodID _MID_removeNode;
static jmethodID _MID_removeTree;
static jfieldID _FID__treeName;
static jfieldID _FID__root;
// SAFFSNode
static jmethodID _MID_addRef;
static jfieldID _FID__parent;
static jfieldID _FID__path;
static jfieldID _FID__documentId;
static jfieldID _FID__flags;
static bool _JNIinit;
protected:
static const int DIRECTORY = 1;
static const int WRITABLE = 2;
static const int READABLE = 4;
GlobalRef _safTree;
// When 0, node doesn't exist yet
// In this case _path is the parent path, _newName the node name and _safParent the parent SAF object
NodeRef _safNode;
Common::String _path;
int _flags;
NodeRef _safParent;
// Used when creating a new node
// Also used for root node to store its pretty name
Common::String _newName;
public:
static const char SAF_MOUNT_POINT[];
/**
* Init JNI parts related to SAF
* Called by AndroidFilesystemFactory::AndroidFilesystemFactory()
*/
static void initJNI();
/**
* Creates an AndroidSAFFilesystemNode given its absolute path
*
* @param path Path of the node
*/
static AndroidSAFFilesystemNode *makeFromPath(const Common::String &path);
/**
* Creates an AndroidSAFFilesystemNode given its tree object
* @param safTree SAF root in Java side. Must be a local reference and must not be used after this call.
*
*/
static AndroidSAFFilesystemNode *makeFromTree(jobject safTree);
bool exists() const override { return (bool)_safNode; }
Common::U32String getDisplayName() const override { return Common::U32String(getName()); }
Common::String getName() const override;
Common::String getPath() const override;
bool isDirectory() const override { return _flags & DIRECTORY; }
bool isReadable() const override { return _flags & READABLE; }
bool isWritable() const override { return _flags & WRITABLE; }
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
int remove() override;
/**
* Removes the SAF tree.
* Only works on the root node
*/
void removeTree();
protected:
/**
* Creates an AndroidSAFFilesystemNode given its tree and its node
*
* @param safTree SAF root in Java side
* @param safNode SAF node in Java side
*/
AndroidSAFFilesystemNode(const GlobalRef &safTree, jobject safNode);
/**
* Creates an non-existent AndroidSAFFilesystemNode given its tree, parent node and name
*
* @param safTree SAF root in Java side
* @param safParent SAF parent node in Java side
* @param path Parent path
* @param name Item name
*/
AndroidSAFFilesystemNode(const GlobalRef &safTree, jobject safParent,
const Common::String &path, const Common::String &name);
/**
* Creates an non-existent AndroidSAFFilesystemNode given its tree, parent node and name
*
* @param safTree SAF root in Java side
* @param safParent SAF parent node reference in Java side
* @param path Parent path
* @param name Item name
*/
AndroidSAFFilesystemNode(const GlobalRef &safTree, const NodeRef &safParent,
const Common::String &path, const Common::String &name);
void cacheData(JNIEnv *env, jobject node);
};
class AddSAFFakeNode final : public AbstractFSNode, public AndroidFSNode {
protected:
AbstractFSNode *getChild(const Common::String &name) const override;
AbstractFSNode *getParent() const override;
public:
static const char SAF_ADD_FAKE_PATH[];
AddSAFFakeNode(bool fromPath) : _proxied(nullptr), _fromPath(fromPath) { }
~AddSAFFakeNode() override;
bool exists() const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
// I18N: This is displayed in the file browser to let the user choose a new folder for Android Storage Attached Framework
Common::U32String getDisplayName() const override;
Common::String getName() const override;
Common::String getPath() const override;
bool isDirectory() const override { return true; }
bool isReadable() const override;
bool isWritable() const override;
Common::SeekableReadStream *createReadStream() override { return nullptr; }
Common::SeekableWriteStream *createWriteStream(bool atomic) override { return nullptr; }
bool createDirectory() override { return false; }
int remove() override;
private:
void makeProxySAF() const;
bool _fromPath;
mutable AbstractFSNode *_proxied;
};
#endif

View File

@@ -0,0 +1,76 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/atari/atari-fs-factory.h"
#include "backends/fs/atari/atari-fs.h"
#include <mint/osbind.h>
#include <unistd.h> // getcwd
AtariFilesystemFactory::AtariFilesystemFactory() {
_fileHashMap["cryomni3.dat"] = "cryomni3d.dat";
_fileHashMap["neverhoo.dat"] = "neverhood.dat";
_fileHashMap["supernov.dat"] = "supernova.dat";
_fileHashMap["teenagen.dat"] = "teenagent.dat";
uint32 drvMap = Drvmap();
for (int i = 0; i < 26; i++) {
if (drvMap & 1) {
char driveRoot[] = "A:";
driveRoot[0] += i;
addDrive(driveRoot);
}
drvMap >>= 1;
}
}
AbstractFSNode *AtariFilesystemFactory::makeRootFileNode() const {
return new AtariFilesystemNode(_config, _fileHashMap);
}
AbstractFSNode *AtariFilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
if (getcwd(buf, MAXPATHLEN) != NULL) {
if (buf[0] == '/') {
// de-mintlib'ize the path
if (buf[1] == 'd' && buf[2] == 'e' && buf[3] == 'v') {
// /dev/<drive>/<path> -> /<drive>/<path>
strcpy(buf, &buf[4]);
}
// /<drive>/<path> -> <DRIVE>:/<path>
buf[0] = toupper(buf[1]);
buf[1] = ':';
}
return new AtariFilesystemNode(buf, _config, _fileHashMap);
} else {
return nullptr;
}
}
AbstractFSNode *AtariFilesystemFactory::makeFileNodePath(const Common::String &path) const {
assert(!path.empty());
return new AtariFilesystemNode(path, _config, _fileHashMap);
}

View File

@@ -0,0 +1,45 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ATARI_FILESYSTEM_FACTORY_H
#define ATARI_FILESYSTEM_FACTORY_H
#include "backends/fs/posix-drives/posix-drives-fs-factory.h"
#include "backends/fs/atari/atari-fs.h"
/**
* Creates AtariFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class AtariFilesystemFactory final : public DrivesPOSIXFilesystemFactory {
public:
AtariFilesystemFactory();
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
private:
AtariFilesystemNode::FileHashMap _fileHashMap;
};
#endif /*ATARI_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,36 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/atari/atari-fs.h"
void AtariFilesystemNode::setFlags() {
DrivePOSIXFilesystemNode::setFlags();
if (!_displayNameChecked) {
if (_fileHashMap.contains(_displayName))
_displayName = _fileHashMap[_displayName];
_displayNameChecked = true;
}
}

View File

@@ -0,0 +1,64 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ATARI_FILESYSTEM_H
#define ATARI_FILESYSTEM_H
#include "backends/fs/posix-drives/posix-drives-fs.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
/**
* Implementation of the ScummVM file system API based on DrivePOSIX with translation to 8+3 filenames.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class AtariFilesystemNode final : public DrivePOSIXFilesystemNode {
protected:
AbstractFSNode *makeNode() const override {
return new AtariFilesystemNode(_config, _fileHashMap);
}
AbstractFSNode *makeNode(const Common::String &path) const override {
return new AtariFilesystemNode(path, _config, _fileHashMap);
}
public:
typedef Common::HashMap<Common::String, Common::String, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileHashMap;
AtariFilesystemNode(const Config &config, const FileHashMap &fileHashMap)
: DrivePOSIXFilesystemNode(config)
, _fileHashMap(fileHashMap) {
}
AtariFilesystemNode(const Common::String &path, const Config &config, const FileHashMap &fileHashMap)
: DrivePOSIXFilesystemNode(path, config)
, _fileHashMap(fileHashMap) {
}
protected:
void setFlags() override;
private:
const FileHashMap &_fileHashMap;
bool _displayNameChecked = false;
};
#endif

View File

@@ -0,0 +1,87 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(POSIX)
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#define FORBIDDEN_SYMBOL_EXCEPTION_exit //Needed for IRIX's unistd.h
#define FORBIDDEN_SYMBOL_EXCEPTION_random
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom
#include <unistd.h>
#include "backends/fs/chroot/chroot-fs-factory.h"
#include "backends/fs/chroot/chroot-fs.h"
ChRootFilesystemFactory::ChRootFilesystemFactory(const Common::String &root)
: _root(root) {
}
AbstractFSNode *ChRootFilesystemFactory::makeRootFileNode() const {
return new ChRootFilesystemNode(_root, "/");
}
AbstractFSNode *ChRootFilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
if (getcwd(buf, MAXPATHLEN) == NULL) {
return NULL;
}
Common::String curPath(buf);
if (curPath.hasPrefix(_root + Common::String("/"))) {
return new ChRootFilesystemNode(_root, buf + _root.size());
}
for (auto it = _virtualDrives.begin() ; it != _virtualDrives.end() ; ++it) {
if (curPath.hasPrefix(it->_value + Common::String("/")))
return new ChRootFilesystemNode(it->_value, buf + it->_value.size(), it->_key);
}
return new ChRootFilesystemNode(_root, "/");
}
AbstractFSNode *ChRootFilesystemFactory::makeFileNodePath(const Common::String &path) const {
assert(!path.empty());
size_t driveEnd = path.findFirstOf('/');
if (driveEnd != Common::String::npos && driveEnd > 0) {
auto it = _virtualDrives.find(path.substr(0, driveEnd));
if (it != _virtualDrives.end())
return new ChRootFilesystemNode(it->_value, path.substr(driveEnd), it->_key);
}
return new ChRootFilesystemNode(_root, path);
}
void ChRootFilesystemFactory::addVirtualDrive(const Common::String &name, const Common::String &path) {
_virtualDrives[name] = path;
}
Common::String ChRootFilesystemFactory::getSystemFullPath(const Common::String& path) const {
size_t driveEnd = path.findFirstOf('/');
if (driveEnd != Common::String::npos && driveEnd > 0) {
auto it = _virtualDrives.find(path.substr(0, driveEnd));
if (it != _virtualDrives.end())
return it->_value + path.substr(driveEnd);
}
return _root + path;
}
#endif

View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_FS_CHROOT_CHROOT_FS_FACTORY_H
#define BACKENDS_FS_CHROOT_CHROOT_FS_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* FIXME: Warning, using this factory in your backend may silently break some
* features. Instances are, for example, the FluidSynth code, and the POSIX
* plugin code.
*/
class ChRootFilesystemFactory final : public FilesystemFactory {
public:
explicit ChRootFilesystemFactory(const Common::String &root);
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
void addVirtualDrive(const Common::String &name, const Common::String &path);
Common::String getSystemFullPath(const Common::String& path) const override;
private:
const Common::String _root;
Common::StringMap _virtualDrives;
};
#endif /* BACKENDS_FS_CHROOT_CHROOT_FS_FACTORY_H */

View File

@@ -0,0 +1,124 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(POSIX)
#include "backends/fs/chroot/chroot-fs.h"
ChRootFilesystemNode::ChRootFilesystemNode(const Common::String &root, POSIXFilesystemNode *node, const Common::String &drive) {
_root = Common::normalizePath(root, '/');
_drive = drive;
_realNode = node;
}
ChRootFilesystemNode::ChRootFilesystemNode(const Common::String &root, const Common::String &path, const Common::String &drive) {
_root = Common::normalizePath(root, '/');
_drive = drive;
_realNode = new POSIXFilesystemNode(addPathComponent(root, path));
}
ChRootFilesystemNode::~ChRootFilesystemNode() {
delete _realNode;
}
bool ChRootFilesystemNode::exists() const {
return _realNode->exists();
}
Common::U32String ChRootFilesystemNode::getDisplayName() const {
return _realNode->getDisplayName();
}
Common::String ChRootFilesystemNode::getName() const {
return _realNode->getName();
}
Common::String ChRootFilesystemNode::getPath() const {
Common::String path = _realNode->getPath();
if (path.size() > _root.size())
return _drive + Common::String(path.c_str() + _root.size());
return _drive + "/";
}
bool ChRootFilesystemNode::isDirectory() const {
return _realNode->isDirectory();
}
bool ChRootFilesystemNode::isReadable() const {
return _realNode->isReadable();
}
bool ChRootFilesystemNode::isWritable() const {
// Assume virtual drives are not writable
if (!_drive.empty())
return false;
return _realNode->isWritable();
}
AbstractFSNode *ChRootFilesystemNode::getChild(const Common::String &n) const {
return new ChRootFilesystemNode(_root, (POSIXFilesystemNode *)_realNode->getChild(n), _drive);
}
bool ChRootFilesystemNode::getChildren(AbstractFSList &list, ListMode mode, bool hidden) const {
AbstractFSList tmp;
if (!_realNode->getChildren(tmp, mode, hidden)) {
return false;
}
for (AbstractFSList::iterator i=tmp.begin(); i!=tmp.end(); ++i) {
list.push_back(new ChRootFilesystemNode(_root, (POSIXFilesystemNode *) *i, _drive));
}
return true;
}
AbstractFSNode *ChRootFilesystemNode::getParent() const {
if (getPath() == _drive + "/")
return nullptr;
return new ChRootFilesystemNode(_root, (POSIXFilesystemNode *)_realNode->getParent(), _drive);
}
Common::SeekableReadStream *ChRootFilesystemNode::createReadStream() {
return _realNode->createReadStream();
}
Common::SeekableWriteStream *ChRootFilesystemNode::createWriteStream(bool atomic) {
return _realNode->createWriteStream(atomic);
}
bool ChRootFilesystemNode::createDirectory() {
return _realNode->createDirectory();
}
Common::String ChRootFilesystemNode::addPathComponent(const Common::String &path, const Common::String &component) {
const char sep = '/';
if (path.lastChar() == sep && component.firstChar() == sep) {
return Common::String::format("%s%s", path.c_str(), component.c_str() + 1);
}
if (path.lastChar() == sep || component.firstChar() == sep) {
return Common::String::format("%s%s", path.c_str(), component.c_str());
}
return Common::String::format("%s%c%s", path.c_str(), sep, component.c_str());
}
#endif

View File

@@ -0,0 +1,58 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_FS_CHROOT_CHROOT_FS_H
#define BACKENDS_FS_CHROOT_CHROOT_FS_H
#include "backends/fs/posix/posix-fs.h"
class ChRootFilesystemNode final : public AbstractFSNode {
Common::String _root;
Common::String _drive;
POSIXFilesystemNode *_realNode;
ChRootFilesystemNode(const Common::String &root, POSIXFilesystemNode *, const Common::String &drive);
public:
ChRootFilesystemNode(const Common::String &root, const Common::String &path, const Common::String &drive = Common::String());
~ChRootFilesystemNode() override;
bool exists() const override;
Common::U32String getDisplayName() const override;
Common::String getName() const override;
Common::String getPath() const override;
bool isDirectory() const override;
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
private:
static Common::String addPathComponent(const Common::String &path, const Common::String &component);
};
#endif /* BACKENDS_FS_CHROOT_CHROOT_FS_H */

View File

@@ -0,0 +1,43 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__DS__)
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/devoptab/devoptab-fs-factory.h"
#include <sys/iosupport.h>
DevoptabFilesystemFactory::DevoptabFilesystemFactory() {
// skip in, out and err
for (uint8 i = 3; i < STD_MAX; ++i) {
const devoptab_t *dt = devoptab_list[i];
if (!dt || !dt->name || !dt->open_r || !dt->diropen_r)
continue;
addDrive(Common::String(dt->name) + ":/");
}
}
#endif

View File

@@ -0,0 +1,37 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DEVOPTAB_FILESYSTEM_FACTORY_H
#define DEVOPTAB_FILESYSTEM_FACTORY_H
#include "backends/fs/posix-drives/posix-drives-fs-factory.h"
/**
* A FilesystemFactory implementation for filesystems with a special
* top-level directory listing all registered devoptab devices but
* that otherwise implement the POSIX APIs.
*/
class DevoptabFilesystemFactory final : public DrivesPOSIXFilesystemFactory {
public:
DevoptabFilesystemFactory();
};
#endif

View File

@@ -0,0 +1,205 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef EMSCRIPTEN
#include "backends/fs/emscripten/cloud-fs.h"
#include "backends/cloud/downloadrequest.h"
#include "backends/fs/fs-factory.h"
#include "backends/fs/posix/posix-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/system.h"
Common::HashMap<Common::String, AbstractFSList> CloudFilesystemNode::_cloudFolders = Common::HashMap<Common::String, AbstractFSList>();
CloudFilesystemNode::CloudFilesystemNode(const Common::String &p) : _isDirectory(false), _isValid(false), _path(p), _storageFileId(nullptr) {
debug(5, "CloudFilesystemNode::CloudFilesystemNode(Common::String %s)", p.c_str());
assert(p.size() > 0);
// Normalize the path (that is, remove unneeded slashes etc.)
_path = Common::normalizePath(_path, '/');
_displayName = Common::lastPathComponent(_path, '/');
if (_path == CLOUD_FS_PATH) { // need special case for handling the root of the cloud-filesystem
_displayName = "[" + Common::lastPathComponent(_path, '/') + "]";
_isDirectory = true;
_isValid = true;
return;
} else { // we need to peek in the parent folder to see if file exists and if it's a directory
AbstractFSNode *parent = getParent();
AbstractFSList tmp = AbstractFSList();
parent->getChildren(tmp, Common::FSNode::kListAll, true);
for (AbstractFSList::iterator i = tmp.begin(); i != tmp.end(); ++i) {
CloudFilesystemNode *child = (CloudFilesystemNode *)*i;
if (child->getPath() == _path) {
_isDirectory = child->isDirectory();
_isValid = true;
_storageFileId = child->_storageFileId;
break;
}
}
return;
}
}
AbstractFSNode *CloudFilesystemNode::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);
}
void CloudFilesystemNode::directoryListedCallback(const Cloud::Storage::ListDirectoryResponse &response) {
debug(5, "CloudFilesystemNode::directoryListedCallback %s", _path.c_str());
Common::Array<Cloud::StorageFile> storageFiles = response.value;
AbstractFSList *dirList = new AbstractFSList();
for (Common::Array<Cloud::StorageFile>::iterator i = storageFiles.begin(); i != storageFiles.end(); ++i) {
Cloud::StorageFile storageFile = *i;
CloudFilesystemNode *file_node = new CloudFilesystemNode();
file_node->_isDirectory = storageFile.isDirectory();
file_node->_path = _path + "/" + storageFile.name();
file_node->_isValid = true;
file_node->_displayName = "" + storageFile.name();
file_node->_storageFileId = storageFile.id();
dirList->push_back(file_node);
}
_cloudFolders[_path] = *dirList;
}
void CloudFilesystemNode::directoryListedErrorCallback(const Networking::ErrorResponse &_error) {
// _workingRequest = nullptr; // TODO: HANDLE THIS SOMEWHERE
error("Response %ld: %s", _error.httpResponseCode, _error.response.c_str());
}
void CloudFilesystemNode::fileDownloadedCallback(const Cloud::Storage::BoolResponse &response) {
// _workingRequest = nullptr; // TODO: HANDLE THIS SOMEWHERE
debug(5, "CloudFilesystemNode::fileDownloadedCallback %s", _path.c_str());
}
void CloudFilesystemNode::fileDownloadedErrorCallback(const Networking::ErrorResponse &_error) {
// _workingRequest = nullptr; // TODO: HANDLE THIS SOMEWHERE
error("Response %ld: %s", _error.httpResponseCode, _error.response.c_str());
}
bool CloudFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
assert(_isDirectory);
if (!_cloudFolders.contains(_path)) {
debug(5, "CloudFilesystemNode::getChildren Fetching Children: %s", _path.c_str());
Common::String _cloud_path = _path.substr(sizeof(CLOUD_FS_PATH), _path.size() - sizeof(CLOUD_FS_PATH));
CloudMan.listDirectory(
_cloud_path,
new Common::Callback<CloudFilesystemNode, const Cloud::Storage::ListDirectoryResponse &>((CloudFilesystemNode *)this, &CloudFilesystemNode::directoryListedCallback),
new Common::Callback<CloudFilesystemNode, const Networking::ErrorResponse &>((CloudFilesystemNode *)this, &CloudFilesystemNode::directoryListedErrorCallback),
false);
while (!_cloudFolders.contains(_path)) {
g_system->delayMillis(10);
}
debug(5, "CloudFilesystemNode::getChildren %s size %u", _path.c_str(), _cloudFolders[_path].size());
}
for (AbstractFSList::iterator i = _cloudFolders[_path].begin(); i != _cloudFolders[_path].end(); ++i) {
// TODO: Respect mode and hidden getChildren parameters
CloudFilesystemNode *node = (CloudFilesystemNode *)*i;
// we need to copy node here as FSNode will take ownership of the pointer and destroy it after use
myList.push_back(new CloudFilesystemNode(*node));
}
return true;
}
AbstractFSNode *CloudFilesystemNode::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;
}
Common::String _parent_path = Common::normalizePath(Common::String(start, end), '/');
FilesystemFactory *factory = g_system->getFilesystemFactory();
return factory->makeFileNodePath(_parent_path);
}
Common::SeekableReadStream *CloudFilesystemNode::createReadStream() {
debug(5, "CloudFilesystemNode::createReadStream() %s", _path.c_str());
Common::String fsCachePath = Common::normalizePath("/.cache/" + _path, '/');
POSIXFilesystemNode *cacheFile = new POSIXFilesystemNode(fsCachePath);
if (!cacheFile->exists()) {
Cloud::Storage *_storage = CloudMan.getCurrentStorage();
Networking::Request *_workingRequest = _storage->downloadById(
_storageFileId,
Common::Path(fsCachePath),
new Common::Callback<CloudFilesystemNode, const Cloud::Storage::BoolResponse &>(this, &CloudFilesystemNode::fileDownloadedCallback),
new Common::Callback<CloudFilesystemNode, const Networking::ErrorResponse &>(this, &CloudFilesystemNode::fileDownloadedErrorCallback));
while (_workingRequest->state() != Networking::RequestState::FINISHED) {
g_system->delayMillis(10);
}
debug(5, "CloudFilesystemNode::createReadStream() file written %s", fsCachePath.c_str());
}
return PosixIoStream::makeFromPath(fsCachePath, StdioStream::WriteMode_Read);
}
Common::SeekableWriteStream *CloudFilesystemNode::createWriteStream(bool atomic) {
return 0;
}
bool CloudFilesystemNode::createDirectory() {
return false;
}
bool CloudFilesystemNode::exists() const {
return _isValid;
}
bool CloudFilesystemNode::isReadable() const {
return exists();
}
bool CloudFilesystemNode::isWritable() const {
return false;
}
#endif // #if defined(EMSCRIPTEN)

View File

@@ -0,0 +1,95 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CLOUD_FILESYSTEM_H
#define CLOUD_FILESYSTEM_H
#include "backends/fs/abstract-fs.h"
#ifdef USE_CLOUD
#include "backends/cloud/cloudmanager.h"
#include "backends/cloud/storage.h"
#include "backends/cloud/storagefile.h"
#include "backends/networking/http/connectionmanager.h"
#include "backends/networking/http/httpjsonrequest.h"
#include "backends/networking/http/request.h"
#endif
#include "backends/fs/posix/posix-fs.h"
/**
* Implementation of the ScummVM file system API based on POSIX.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class CloudFilesystemNode : public AbstractFSNode {
#define CLOUD_FS_PATH "/cloud"
protected:
static Common::HashMap<Common::String, AbstractFSList> _cloudFolders;
Common::String _displayName;
Common::String _path;
Common::String _storageFileId;
bool _isDirectory;
bool _isValid;
/**
* Plain constructor, for internal use only (hence protected).
*/
CloudFilesystemNode() : _isDirectory(false), _isValid(false), _storageFileId(nullptr) {}
virtual AbstractFSNode *makeNode(const Common::String &path) const {
return new CloudFilesystemNode(path);
}
/**
* Callbacks for network calls (file download and directory listing)
*/
void directoryListedCallback(const Cloud::Storage::ListDirectoryResponse &response);
void directoryListedErrorCallback(const Networking::ErrorResponse &error);
void fileDownloadedCallback(const Cloud::Storage::BoolResponse &response);
void fileDownloadedErrorCallback(const Networking::ErrorResponse &error);
public:
/**
* Creates a CloudFilesystemNode for a given path.
*
* @param path the path the new node should point to.
*/
CloudFilesystemNode(const Common::String &path);
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
bool isDirectory() const override { return _isDirectory; }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
};
#endif

View File

@@ -0,0 +1,95 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef EMSCRIPTEN
#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#include "backends/fs/emscripten/emscripten-fs-factory.h"
#include "backends/fs/emscripten/emscripten-posix-fs.h"
#include "backends/fs/emscripten/http-fs.h"
#include "common/debug.h"
#ifdef USE_CLOUD
#include "backends/fs/emscripten/cloud-fs.h"
#endif
#include <emscripten.h>
EM_ASYNC_JS(void, _initSettings, (const char *pathPtr), {
try {
const path = UTF8ToString(pathPtr);
const settingsPath = path + "/scummvm.ini";
// Mount the filesystem
FS.mount(IDBFS, { autoPersist: true }, path);
// Sync the filesystem
await new Promise((resolve, reject) => {
FS.syncfs(true, (err) => err ? reject(err) : resolve());
});
// Check if settings file exists and download if needed
if (!FS.analyzePath(settingsPath).exists) {
const response = await fetch("scummvm.ini");
if (response.ok) {
const text = await response.text();
FS.writeFile(settingsPath, text);
}
}
} catch (err) {
console.error("Error initializing files:", err);
alert("Error initializing files: " + err);
throw err;
}
});
EmscriptenFilesystemFactory::EmscriptenFilesystemFactory() {
_initSettings(getenv("HOME"));
_httpNodes = new Common::HashMap<Common::String, HTTPFilesystemNode *>();
}
AbstractFSNode *EmscriptenFilesystemFactory::makeCurrentDirectoryFileNode() const {
// getcwd() defaults to root on emscripten and ScummVM doesn't use setcwd()
return makeRootFileNode();
}
AbstractFSNode *EmscriptenFilesystemFactory::makeRootFileNode() const {
return new EmscriptenPOSIXFilesystemNode("/");
}
AbstractFSNode *EmscriptenFilesystemFactory::makeFileNodePath(const Common::String &path) const {
debug(5, "EmscriptenFilesystemFactory::makeFileNodePath(%s)", path.c_str());
assert(!path.empty());
if (path.hasPrefix(DATA_PATH)) {
if (!_httpNodes->contains(path)) {
// finding a node by path requires a http request to the server, so we cache the nodes
_httpNodes->setVal(path, new HTTPFilesystemNode(path));
}
return new HTTPFilesystemNode(*(_httpNodes->getVal(path)));
#ifdef USE_CLOUD
} else if (path.hasPrefix(CLOUD_FS_PATH) && CloudMan.isStorageEnabled()) {
return new CloudFilesystemNode(path);
#endif
} else {
return new EmscriptenPOSIXFilesystemNode(path);
}
}
#endif

View File

@@ -0,0 +1,45 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef EMSCRIPTEN_FILESYSTEM_FACTORY_H
#define EMSCRIPTEN_FILESYSTEM_FACTORY_H
#include "backends/fs/emscripten/http-fs.h"
#include "backends/fs/fs-factory.h"
#include "common/singleton.h"
/**
* Creates POSIXFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class EmscriptenFilesystemFactory : public FilesystemFactory {
public:
EmscriptenFilesystemFactory();
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
private:
Common::HashMap<Common::String, HTTPFilesystemNode *> *_httpNodes;
};
#endif /*EMSCRIPTEN_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,58 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
#include <stdio.h>
#include "backends/fs/emscripten/emscripten-fs-factory.h"
#include "backends/fs/emscripten/emscripten-posix-fs.h"
#include "backends/fs/emscripten/http-fs.h"
#include "backends/fs/posix/posix-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/system.h"
#ifdef USE_CLOUD
#include "backends/cloud/cloudmanager.h"
#include "backends/fs/emscripten/cloud-fs.h"
#endif
AbstractFSNode *EmscriptenPOSIXFilesystemNode::makeNode(const Common::String &path) const {
return g_system->getFilesystemFactory()->makeFileNodePath(path);
}
EmscriptenPOSIXFilesystemNode::EmscriptenPOSIXFilesystemNode(const Common::String &path) : POSIXFilesystemNode(path) {}
EmscriptenPOSIXFilesystemNode::EmscriptenPOSIXFilesystemNode() : POSIXFilesystemNode() {}
bool EmscriptenPOSIXFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
if (_path == "/") {
HTTPFilesystemNode *data_entry = new HTTPFilesystemNode(DATA_PATH);
myList.push_back(data_entry);
#ifdef USE_CLOUD
if (CloudMan.isStorageEnabled()) {
CloudFilesystemNode *cloud_entry = new CloudFilesystemNode(CLOUD_FS_PATH);
myList.push_back(cloud_entry);
}
#endif
}
return POSIXFilesystemNode::getChildren(myList, mode, hidden);
}

View File

@@ -0,0 +1,36 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef EMSCRIPTEN_FILESYSTEM_H
#define EMSCRIPTEN_FILESYSTEM_H
#include "backends/fs/posix/posix-fs.h"
class EmscriptenPOSIXFilesystemNode : public POSIXFilesystemNode {
public:
EmscriptenPOSIXFilesystemNode(const Common::String &path);
EmscriptenPOSIXFilesystemNode();
bool getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const override;
AbstractFSNode *makeNode(const Common::String &path) const override;
};
#endif

View File

@@ -0,0 +1,272 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
#ifdef EMSCRIPTEN
#include "backends/fs/emscripten/http-fs.h"
#include "backends/cloud/downloadrequest.h"
#include "backends/fs/fs-factory.h"
#include "backends/fs/posix/posix-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/debug.h"
#include "common/formats/json.h"
#include <emscripten.h>
HTTPFilesystemNode::HTTPFilesystemNode(const Common::String &path, const Common::String &displayName, const Common::String &baseUrl, bool isValid, bool isDirectory, int size) : _path(path), _displayName(displayName), _url(baseUrl), _isValid(isValid), _isDirectory(isDirectory), _size(size) {
debug(5, "HTTPFilesystemNode::HTTPFilesystemNode(%s, %s)", path.c_str(), baseUrl.c_str());
assert(path.size() > 0);
assert(isDirectory || size >= 0 || !isValid);
_children = new AbstractFSList();
}
HTTPFilesystemNode::HTTPFilesystemNode(const Common::String &p) : _path(p), _isValid(false), _isDirectory(false), _size(-1) {
debug(5, "HTTPFilesystemNode::HTTPFilesystemNode(%s)", p.c_str());
assert(p.size() > 0);
_children = new AbstractFSList();
// Normalize the path (that is, remove unneeded slashes etc.)
_path = Common::normalizePath(_path, '/');
_displayName = Common::lastPathComponent(_path, '/');
if (_path == DATA_PATH) { // need special case for handling the root of the http-filesystem
_isDirectory = true;
_isValid = true;
_url = _path;
} else { // we need to peek in the parent folder to see if the node exists and if it's a directory
AbstractFSNode *parent = getParent();
AbstractFSList tmp = AbstractFSList();
parent->getChildren(tmp, Common::FSNode::kListAll, true);
for (AbstractFSList::iterator i = tmp.begin(); i != tmp.end(); ++i) {
AbstractFSNode *child = *i;
if (child->getPath() == _path) {
_isDirectory = child->isDirectory();
_isValid = true;
_url = ((HTTPFilesystemNode *)child)->_url;
_size = ((HTTPFilesystemNode *)child)->_size;
break;
}
}
}
assert(_isDirectory || _size >= 0 || !_isValid);
debug(5, "HTTPFilesystemNode::HTTPFilesystemNode(%s) - %s isValid %s isDirectory %s", p.c_str(), _url.c_str(), _isValid ? "True" : "false", _isDirectory ? "True" : "false");
}
AbstractFSNode *HTTPFilesystemNode::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);
}
EM_ASYNC_JS(char *, _httpFsFetchIndex, (const char *url), {
globalThis['httpFsIndexCache'] = globalThis['httpFsIndexCache'] || {};
returnString = "";
url = UTF8ToString(url);
console.debug("Downloading %s", url);
if (globalThis['httpFsIndexCache'][url]) {
console.debug("Cache hit for %s", url);
returnString = globalThis['httpFsIndexCache'][url];
} else {
try {
const response = await fetch(url);
if (response.ok) {
returnString = await response.text();
globalThis['httpFsIndexCache'][url] = returnString;
}
} catch (error) {
console.error(error);
}
}
var size = lengthBytesUTF8(returnString) + 1;
var ret = Module._malloc(size);
stringToUTF8Array(returnString, HEAP8, ret, size);
return ret;
});
EM_ASYNC_JS(bool, _httpFsFetchFile, (const char *url, byte *dataPtr, int dataSize), {
returnBytes = new Uint8Array();
url = UTF8ToString(url);
console.debug("Downloading %s", url);
try {
const response = await fetch(url);
if (response.ok) {
returnBytes = await response.bytes();
if (returnBytes.length == dataSize) {
Module.writeArrayToMemory(returnBytes, dataPtr);
return true;
}
} else {
console.error("HTTPFilesystemNode::_httpFsFetchFile: %s", response.statusText);
}
} catch (error) {
console.error(error);
}
return false;
});
bool HTTPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
if (!_isValid) {
return false;
}
assert(_isDirectory);
if (_children->size() == 0) {
// if we don't have a children list yet, we need to fetch it from the server
debug(5, "HTTPFilesystemNode::getChildren Fetching Children: %s at %s", _path.c_str(), _url.c_str());
Common::String url = _url + "/index.json";
char *response = _httpFsFetchIndex(url.c_str());
if (strcmp(response, "") == 0) {
return false;
}
Common::JSONObject jsonObj = Common::JSON::parse(response)->asObject();
// add dummy element so we know that we fetched the list
_children->push_back(new HTTPFilesystemNode(_path, ".", _url, false, false, 0));
for (typename Common::HashMap<Common::String, Common::JSONValue *>::iterator i = jsonObj.begin(); i != jsonObj.end(); ++i) {
Common::String name = i->_key;
bool isDir = false;
int size = -1;
Common::String baseUrl = _url + "/" + name;
if (i->_value->isObject()) {
isDir = true;
if (i->_value->asObject().contains("baseUrl")) {
debug(5, "HTTPFilesystemNode::directoryListedCallback - Directory with baseUrl %s", name.c_str());
baseUrl = i->_value->asObject()["baseUrl"]->asString();
}
} else if (i->_value->isIntegerNumber()) {
size = i->_value->asIntegerNumber();
}
HTTPFilesystemNode *file_node = new HTTPFilesystemNode(_path + "/" + name, name, baseUrl, true, isDir, size);
_children->push_back(file_node);
}
}
for (AbstractFSList::iterator i = (*_children).begin(); i != (*_children).end(); ++i) {
HTTPFilesystemNode *node = (HTTPFilesystemNode *)*i;
if (node->_isValid && (mode == Common::FSNode::kListAll ||
(mode == Common::FSNode::kListFilesOnly && !node->_isDirectory) ||
(mode == Common::FSNode::kListDirectoriesOnly && node->_isDirectory))) {
// we need to copy node here as FSNode will take ownership of the pointer and destroy it after use
HTTPFilesystemNode *file_node = new HTTPFilesystemNode(*node);
myList.push_back(file_node);
} else {
debug(5, "HTTPFilesystemNode::getChildren - skipping %s", node->_path.c_str());
}
}
return true;
}
AbstractFSNode *HTTPFilesystemNode::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;
}
Common::String _parent_path = Common::normalizePath(Common::String(start, end), '/');
FilesystemFactory *factory = g_system->getFilesystemFactory();
return factory->makeFileNodePath(_parent_path);
}
Common::SeekableReadStream *HTTPFilesystemNode::createReadStream() {
debug(5, "*HTTPFilesystemNode::createReadStream() %s (size %d) ", _path.c_str(), _size);
Common::String fsCachePath = Common::normalizePath("/.cache/" + _path, '/');
POSIXFilesystemNode *cacheFile = new POSIXFilesystemNode(fsCachePath);
// todo: this should not be cached on the filesystem, but in memory
// and support range requests
// port https://github.com/emscripten-core/emscripten/blob/main/src/lib/libwasmfs_fetch.js over
if (!cacheFile->exists() && _size > 0) {
byte *buffer = new byte[_size];
bool success = _httpFsFetchFile(_url.c_str(), buffer, _size);
if (success) {
Common::DumpFile *_localFile = new Common::DumpFile();
if (!_localFile->open(Common::Path(fsCachePath), true)) {
error("Storage: unable to open file to download into");
return 0;
}
debug(5, "HTTPFilesystemNode::createReadStream() file downloaded %s", _path.c_str());
_localFile->write(buffer, _size);
_localFile->close();
free(buffer);
} else {
warning("Storage: unable to download file %s", _url.c_str());
free(buffer);
return 0;
}
} else if (_size == 0) {
debug(5, "HTTPFilesystemNode::createReadStream() file empty %s", _path.c_str());
Common::DumpFile *_localFile = new Common::DumpFile();
if (!_localFile->open(Common::Path(fsCachePath), true)) {
warning("Storage: unable to open file to download into");
return 0;
}
_localFile->close();
}
return PosixIoStream::makeFromPath(fsCachePath, StdioStream::WriteMode_Read);
}
Common::SeekableWriteStream *HTTPFilesystemNode::createWriteStream(bool atomic) {
return 0;
}
bool HTTPFilesystemNode::createDirectory() {
return false;
}
bool HTTPFilesystemNode::exists() const {
return _isValid;
}
bool HTTPFilesystemNode::isReadable() const {
return exists();
}
bool HTTPFilesystemNode::isWritable() const {
return false;
}
#endif // #if defined(EMSCRIPTEN)

View File

@@ -0,0 +1,78 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef HTTP_FILESYSTEM_H
#define HTTP_FILESYSTEM_H
#include "backends/fs/abstract-fs.h"
#include "backends/fs/posix/posix-fs.h"
/**
* Implementation of the ScummVM file system API based on POSIX.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class HTTPFilesystemNode : public AbstractFSNode {
protected:
static Common::HashMap<Common::String, AbstractFSList> _httpFolders;
Common::String _displayName;
Common::String _path;
Common::String _url;
AbstractFSList *_children;
bool _isDirectory;
bool _isValid;
int _size;
/**
* Full constructor, for internal use only (hence protected).
*/
HTTPFilesystemNode(const Common::String &path, const Common::String &displayName, const Common::String &baseUrl, bool isValid, bool isDirectory, int size);
virtual AbstractFSNode *makeNode(const Common::String &path) const {
return new HTTPFilesystemNode(path);
}
public:
/**
* Creates a HTTPFilesystemNode for a given path.
*
* @param path the path the new node should point to.
*/
HTTPFilesystemNode(const Common::String &path);
~HTTPFilesystemNode() {}
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
bool isDirectory() const override { return _isDirectory; }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
};
#endif

77
backends/fs/fs-factory.h Normal file
View File

@@ -0,0 +1,77 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef FILESYSTEM_FACTORY_H
#define FILESYSTEM_FACTORY_H
#include "common/str.h"
#include "backends/fs/abstract-fs.h"
/**
* Creates concrete FSNode objects depending on the current architecture.
*/
class FilesystemFactory {
public:
/**
* Destructor.
*/
virtual ~FilesystemFactory() {}
/**
* Returns a node representing the "current directory".
* If your system does not support this concept, you can either try to
* emulate it or simply return some "sensible" default directory node,
* e.g. the same value as getRoot() returns.
*/
virtual AbstractFSNode *makeCurrentDirectoryFileNode() const = 0;
/**
* Construct a node based on a path; the path is in the same format as it
* would be for calls to fopen().
*
* Furthermore getNodeForPath(oldNode.path()) should create a new node
* identical to oldNode. Hence, we can use the "path" value for persistent
* storage e.g. in the config file.
*
* @param path The path string to create a FSNode for.
*/
virtual AbstractFSNode *makeFileNodePath(const Common::String &path) const = 0;
/**
* Returns a special node representing the filesystem root.
* The starting point for any file system browsing.
*
* On Unix, this will be simply the node for / (the root directory).
* On Windows, it will be a special node which "contains" all drives (C:, D:, E:).
*/
virtual AbstractFSNode *makeRootFileNode() const = 0;
/**
* Returns a path suitable for systen functions such as fopen() for the given ScummVM path,
*
* In most cases the returned path is the same as the given path, but it may be different, for
* example when the application is sandboxed and ScummVM path are relative to the saandbox
* root.
*/
virtual Common::String getSystemFullPath(const Common::String& path) const { return path; }
};
#endif /*FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,49 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
// Also with clock() in sys/time.h in some macOS SDKs.
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#define FORBIDDEN_SYMBOL_EXCEPTION_exit //Needed for IRIX's unistd.h
#define FORBIDDEN_SYMBOL_EXCEPTION_random
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom
#include "backends/fs/kolibrios/kolibrios-fs-factory.h"
#include "backends/fs/kolibrios/kolibrios-fs.h"
#include <unistd.h>
AbstractFSNode *KolibriOSFilesystemFactory::makeRootFileNode() const {
return new KolibriOSFilesystemNode("/");
}
AbstractFSNode *KolibriOSFilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
_ksys_getcwd(buf, MAXPATHLEN);
return new KolibriOSFilesystemNode(buf);
}
AbstractFSNode *KolibriOSFilesystemFactory::makeFileNodePath(const Common::String &path) const {
assert(!path.empty());
return new KolibriOSFilesystemNode(path);
}

View File

@@ -0,0 +1,39 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef KOLIBRIOS_FILESYSTEM_FACTORY_H
#define KOLIBRIOS_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* Creates KolibriOSFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class KolibriOSFilesystemFactory : public FilesystemFactory {
protected:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*KOLIBRIOS_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,288 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
#include "backends/fs/kolibrios/kolibrios-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/algorithm.h"
#include "common/debug.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ksys.h>
#include "common/pack-start.h" // START STRUCT PACKING
struct kol_readdir_result_entry {
ksys_file_info_t bdfe;
char fileName[520];
} PACKED_STRUCT;
struct kol_readdir_result {
ksys_dir_entry_header_t header;
kol_readdir_result_entry entries[0];
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
namespace {
// opendir/readdir are buggy. And they encourage using syscalls. Whatever
kol_readdir_result *kol_readdir(const char *path, uint32 max_blocks) {
ksys_file_status_t ret_result;
int result_buffer_size = sizeof (ksys_dir_entry_header_t)
+ max_blocks * sizeof (kol_readdir_result_entry);
kol_readdir_result *result_buffer = (kol_readdir_result *) malloc (result_buffer_size);
if (!result_buffer)
return nullptr;
memset(result_buffer, 0, result_buffer_size);
ret_result = _ksys_file_read_dir(path, 0, KSYS_FILE_UTF8, max_blocks, result_buffer);
// KSYS_FS_ERR_SUCCESS (0) is returned for normal dirs, KSYS_FS_ERR_EOF (6) for virtual directories
if (ret_result.status != 0 && ret_result.status != KSYS_FS_ERR_EOF) {
free (result_buffer);
return nullptr;
}
return result_buffer;
}
kol_readdir_result *kol_readdir(const char *path) {
kol_readdir_result *res = kol_readdir(path, 2);
if (!res)
return nullptr;
uint32 tot_blocks = res->header.files;
free(res);
return kol_readdir(path, tot_blocks);
}
bool getFileAttrs(Common::String path, uint32& attrs) {
ksys_file_info_t info;
memset(&info, 0, sizeof(info));
info.attr = KSYS_FILE_ATTR_DIR;
if(_ksys_file_info(path.c_str(), &info)) {
attrs = 0;
return false;
}
attrs = info.attr;
return true;
}
} // namespace
bool KolibriOSFilesystemNode::exists() const {
uint32 attrs;
return getFileAttrs(_path, attrs);
}
bool KolibriOSFilesystemNode::isReadable() const {
uint32 attrs;
return getFileAttrs(_path, attrs);
}
bool KolibriOSFilesystemNode::isWritable() const {
uint32 attrs;
return getFileAttrs(_path, attrs) && !(attrs & 0x01);
}
void KolibriOSFilesystemNode::setFlags() {
_isValid = getFileAttrs(_path, _attributes);
}
KolibriOSFilesystemNode::KolibriOSFilesystemNode(const Common::String &p) {
assert(p.size() > 0);
_path = p;
// Normalize the path (that is, remove unneeded slashes etc.)
_path = Common::normalizePath(_path, '/');
_displayName = Common::lastPathComponent(_path, '/');
setFlags();
}
AbstractFSNode *KolibriOSFilesystemNode::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 KolibriOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
kol_readdir_result *res = kol_readdir(_path.c_str());
if (!res)
return false;
for (int i = 0; i < (int) res->header.blocks; i++)
{
// Skip 'invisible' files if necessary
if (res->entries[i].fileName[0] == '.' && !hidden) {
continue;
}
// Skip '.' and '..' to avoid cycles
if ((res->entries[i].fileName[0] == '.' && res->entries[i].fileName[1] == 0)
|| (res->entries[i].fileName[0] == '.' && res->entries[i].fileName[1] == '.' && res->entries[i].fileName[2] == 0)) {
continue;
}
// Start with a clone of this node, with the correct path set
KolibriOSFilesystemNode entry(*this);
entry._displayName = res->entries[i].fileName;
if (_path.lastChar() != '/')
entry._path += '/';
entry._path += entry._displayName;
entry._isValid = true;
entry._attributes = res->entries[i].bdfe.attr;
// Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && entry.isDirectory()) ||
(mode == Common::FSNode::kListDirectoriesOnly && !entry.isDirectory()))
continue;
myList.push_back(new KolibriOSFilesystemNode(entry));
}
free(res);
return true;
}
AbstractFSNode *KolibriOSFilesystemNode::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;
}
return makeNode(Common::String(start, end));
}
Common::SeekableReadStream *KolibriOSFilesystemNode::createReadStream() {
return PosixIoStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
}
Common::SeekableWriteStream *KolibriOSFilesystemNode::createWriteStream(bool atomic) {
return PosixIoStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
}
bool KolibriOSFilesystemNode::createDirectory() {
if (mkdir(_path.c_str(), 0755) == 0)
setFlags();
return isDirectory();
}
namespace KolibriOS {
bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
// Check whether the prefix exists if one is supplied.
if (prefix) {
uint32 attrs;
if (!getFileAttrs(prefix, attrs) || !KolibriOSFilesystemNode::isDirectory(attrs)) {
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[0] == '/' && cur[1] >= 1 && cur[1] <= 3 && cur[2] == '/')
cur += 2;
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';
}
if (mkdir(path.c_str(), 0755) != 0) {
uint32 attrs;
if (!getFileAttrs(path, attrs) || !KolibriOSFilesystemNode::isDirectory(attrs))
return false;
}
*cur = '/';
} while (cur++ != end);
return true;
}
} // End of namespace KolibriOS

View File

@@ -0,0 +1,93 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef KOLIBRIOS_FS_H
#define KOLIBRIOS_FS_H
#include "backends/fs/abstract-fs.h"
/**
* Implementation of the ScummVM file system API based on KolibriOS.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class KolibriOSFilesystemNode : public AbstractFSNode {
public:
/**
* Creates a KolibriOSFilesystemNode for a given path.
*
* @param path the path the new node should point to.
*/
KolibriOSFilesystemNode(const Common::String &path);
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
static bool isDirectory(uint32 attrs) { return attrs & 0x18; }
bool isDirectory() const override { return isDirectory(_attributes); }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
protected:
Common::String _displayName;
Common::String _path;
uint32 _attributes;
bool _isValid;
virtual AbstractFSNode *makeNode(const Common::String &path) const {
return new KolibriOSFilesystemNode(path);
}
/**
* Plain constructor, for internal use only (hence protected).
*/
KolibriOSFilesystemNode() : _attributes(0), _isValid(false) {}
/**
* Tests and sets the _isValid and _isDirectory flags, using the stat() function.
*/
virtual void setFlags();
};
namespace KolibriOS {
/**
* 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 directory exists (or was created), false otherwise.
*/
bool assureDirectoryExists(const Common::String &dir, const char *prefix = nullptr);
} // End of namespace KolibriOS
#endif

View File

@@ -0,0 +1,38 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__MORPHOS__)
#include "backends/fs/morphos/morphos-fs-factory.h"
#include "backends/fs/morphos/morphos-fs.h"
AbstractFSNode *MorphOSFilesystemFactory::makeRootFileNode() const {
return new MorphOSFilesystemNode();
}
AbstractFSNode *MorphOSFilesystemFactory::makeCurrentDirectoryFileNode() const {
return new MorphOSFilesystemNode();
}
AbstractFSNode *MorphOSFilesystemFactory::makeFileNodePath(const Common::String &path) const {
return new MorphOSFilesystemNode(path);
}
#endif

View File

@@ -0,0 +1,39 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MORPHOS_FILESYSTEM_FACTORY_H
#define MORPHOS_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* Creates MorphOSFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class MorphOSFilesystemFactory final : public FilesystemFactory {
public:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*MORPHOS_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,377 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__MORPHOS__)
#include "backends/fs/morphos/morphos-fs.h"
#include "backends/fs/stdiostream.h"
#include "common/debug.h"
#include "common/util.h"
/**
* Returns the last component of a given path.
*
* @param str Common::String containing the path.
* @return Pointer to the first char of the last component inside str.
*/
const char *lastPathComponent(const Common::String &str) {
int offset = str.size();
if (offset <= 0) {
debug(6, "Bad offset");
return 0;
}
const char *p = str.c_str();
while (offset > 0 && (p[offset-1] == '/' || p[offset-1] == ':'))
offset--;
while (offset > 0 && (p[offset-1] != '/' && p[offset-1] != ':'))
offset--;
return p + offset;
}
MorphOSFilesystemNode::MorphOSFilesystemNode() {
_sDisplayName = "Available HDDs/Partitions";
_bIsValid = true;
_bIsDirectory = true;
_sPath = "";
_pFileLock = 0;
_nProt = 0; // Protection is ignored for the root volume
}
MorphOSFilesystemNode::MorphOSFilesystemNode(const Common::String &p) {
int offset = p.size();
if (offset <= 0) {
debug(6, "Bad offset");
return;
}
_sPath = p;
_sDisplayName = ::lastPathComponent(_sPath);
_pFileLock = 0;
_bIsDirectory = false;
_bIsValid = false;
struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL);
if (fib == NULL) {
debug(6,"Failed...");
return;
}
BPTR pLock = Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK);
if (pLock) {
if (Examine(pLock, fib) != DOSFALSE) {
if (fib->fib_EntryType > 0) {
_bIsDirectory = true;
_pFileLock = DupLock(pLock);
_bIsValid = (_pFileLock != 0);
const char c = _sPath.lastChar();
if (c != '/' && c != ':')
_sPath += '/';
} else {
_bIsDirectory = false;
_bIsValid = false;
}
}
UnLock(pLock);
}
FreeDosObject(DOS_FIB, fib);
}
MorphOSFilesystemNode::MorphOSFilesystemNode(BPTR pLock, const char *pDisplayName) {
int bufSize = MAXPATHLEN;
_pFileLock = 0;
while (true) {
char *n = new char[bufSize];
if (NameFromLock(pLock, (STRPTR)n, bufSize) != DOSFALSE) {
_sPath = n;
_sDisplayName = pDisplayName ? pDisplayName : FilePart((STRPTR)n);
delete[] n;
break;
}
if (IoErr() != ERROR_LINE_TOO_LONG) {
_bIsValid = false;
debug(6, "IoErr() != ERROR_LINE_TOO_LONG");
delete[] n;
return;
}
bufSize *= 2;
delete[] n;
}
_bIsValid = false;
_bIsDirectory = false;
FileInfoBlock *fib = (FileInfoBlock*) AllocDosObject(DOS_FIB, NULL);
if (fib == NULL) {
debug(6,"Failed...");
return;
}
if (Examine(pLock, fib) != DOSFALSE) {
_bIsDirectory = fib->fib_EntryType >0;
if (_bIsDirectory) {
if (fib->fib_EntryType != ST_ROOT)
_sPath += '/';
_pFileLock = DupLock(pLock);
if (_pFileLock) _bIsValid = true;
} else {
_bIsValid = true;
}
}
FreeDosObject(DOS_FIB, fib);
}
// We need the custom copy constructor because of DupLock()
MorphOSFilesystemNode::MorphOSFilesystemNode(const MorphOSFilesystemNode& node)
: AbstractFSNode() {
_sDisplayName = node._sDisplayName;
_bIsValid = node._bIsValid;
_bIsDirectory = node._bIsDirectory;
_sPath = node._sPath;
_pFileLock = DupLock(node._pFileLock);
_nProt = node._nProt;
}
MorphOSFilesystemNode::~MorphOSFilesystemNode() {
if (_pFileLock)
UnLock(_pFileLock);
}
bool MorphOSFilesystemNode::exists() const {
if (_sPath.empty())
return false;
bool nodeExists = false;
BPTR pLock = Lock(_sPath.c_str(), SHARED_LOCK);
if (pLock) {
nodeExists = true;
UnLock(pLock);
}
return nodeExists;
}
AbstractFSNode *MorphOSFilesystemNode::getChild(const Common::String &n) const {
if (!_bIsDirectory) {
debug(6, "Not a directory");
return 0;
}
Common::String newPath(_sPath);
if (_sPath.lastChar() != '/')
newPath += '/';
newPath += n;
return new MorphOSFilesystemNode(newPath);
}
bool MorphOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
if (!_bIsValid) {
debug(6, "Invalid node");
return false; // Empty list
}
if (!_bIsDirectory) {
debug(6, "Not a directory");
return false; // Empty list
}
if (isRootNode()) {
debug(6, "Root node");
myList = listVolumes();
return true;
}
FileInfoBlock *fib = (FileInfoBlock*) AllocDosObject(DOS_FIB, NULL);
if (fib == NULL) {
debug(6, "Failed to allocate memory for FileInfoBLock");
return false;
}
if (Examine(_pFileLock, fib) != DOSFALSE) {
MorphOSFilesystemNode *entry;
while (ExNext(_pFileLock, fib) != DOSFALSE) {
Common::String full_path = NULL;
BPTR pLock;
if ((mode == Common::FSNode::kListAll) || (fib->fib_EntryType > 0 && (Common::FSNode::kListDirectoriesOnly == mode)) || (fib->fib_EntryType < 0 && (Common::FSNode::kListFilesOnly == mode ))) {
full_path = _sPath;
full_path += fib->fib_FileName;
pLock = Lock(full_path.c_str(), SHARED_LOCK);
if (pLock) {
entry = new MorphOSFilesystemNode( pLock, fib->fib_FileName );
if (entry) {
myList.push_back(entry);
}
UnLock(pLock);
}
}
}
if (ERROR_NO_MORE_ENTRIES != IoErr() ) {
debug(6, "An error occurred during ExamineDir");
return false;
}
}
FreeDosObject(DOS_FIB, fib);
return true;
}
AbstractFSNode *MorphOSFilesystemNode::getParent() const {
if (isRootNode()) {
debug(6, "Root node");
return new MorphOSFilesystemNode(*this);
}
BPTR pLock = _pFileLock;
if (!_bIsDirectory) {
assert(!pLock);
pLock = Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK);
assert(pLock);
}
MorphOSFilesystemNode *node;
BPTR parentDir = ParentDir( pLock );
if (parentDir) {
node = new MorphOSFilesystemNode(parentDir);
UnLock(parentDir);
} else
node = new MorphOSFilesystemNode();
if (!_bIsDirectory) {
UnLock(pLock);
}
return node;
}
AbstractFSList MorphOSFilesystemNode::listVolumes() const {
AbstractFSList myList;
DosList *dosList;
const ULONG kLockFlags = LDF_READ | LDF_VOLUMES;
char buffer[MAXPATHLEN];
dosList = LockDosList(kLockFlags);
if (dosList == NULL) {
debug(6, "Cannot lock the DOS list");
return myList;
}
dosList = NextDosEntry(dosList, LDF_VOLUMES);
MorphOSFilesystemNode *entry;
while (dosList) {
if (dosList->dol_Type == DLT_VOLUME &&
dosList->dol_Name &&
dosList->dol_Task) {
CONST_STRPTR volName = (CONST_STRPTR)BADDR(dosList->dol_Name)+1;
CONST_STRPTR devName = (CONST_STRPTR)((struct Task *)dosList->dol_Task->mp_SigTask)->tc_Node.ln_Name;
BPTR volumeLock;
Common::strcpy_s(buffer, volName);
Common::strcat_s(buffer, ":");
volumeLock = Lock(buffer, SHARED_LOCK);
if (volumeLock) {
Common::sprintf_s(buffer, "%s (%s)", volName, devName);
entry = new MorphOSFilesystemNode(volumeLock, buffer);
if (entry) {
myList.push_back(entry);
}
UnLock(volumeLock);
}
}
dosList = NextDosEntry(dosList, LDF_VOLUMES);
}
UnLockDosList(kLockFlags);
return myList;
}
Common::SeekableReadStream *MorphOSFilesystemNode::createReadStream() {
StdioStream *readStream = StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
if (readStream) {
readStream->setBufferSize(8192);
}
return readStream;
}
Common::SeekableWriteStream *MorphOSFilesystemNode::createWriteStream(bool atomic) {
return StdioStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
}
bool MorphOSFilesystemNode::createDirectory() {
Common::String createPath = _sPath;
if (createPath.lastChar() == '/') {
createPath.deleteLastChar();
}
BPTR dirLock = CreateDir(createPath.c_str());
if (dirLock) {
UnLock(dirLock);
_bIsValid = true;
_bIsDirectory = true;
} else {
debug(6, "MorphOSFilesystemNode::createDirectory() failed -> Directory '%s' could not be created!", createPath.c_str());
}
return _bIsValid && _bIsDirectory;
}
#endif //defined(__MORPHOS__)

View File

@@ -0,0 +1,123 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MORPHOS_FILESYSTEM_H
#define MORPHOS_FILESYSTEM_H
#ifdef __USE_INLINE__
#undef __USE_INLINE__
#endif
#include <proto/exec.h>
#include <proto/dos.h>
#include <stdio.h>
#ifndef USE_NEWLIB
#include <strings.h>
#endif
#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 MorphOSFilesystemNode final : public AbstractFSNode {
protected:
/**
* The main file lock.
* If this is NULL but _bIsValid is true, then this Node references
* the virtual filesystem root.
*/
BPTR _pFileLock;
Common::String _sDisplayName;
Common::String _sPath;
bool _bIsDirectory;
bool _bIsValid;
uint32 _nProt;
/**
* Creates a list with all the volumes present in the root node.
*/
virtual AbstractFSList listVolumes() const;
/**
* True if this is the pseudo root filesystem.
*/
bool isRootNode() const { return _bIsValid && _bIsDirectory && _pFileLock == 0; }
public:
/**
* Creates a MorphOSFilesystemNode with the root node as path.
*/
MorphOSFilesystemNode();
/**
* Creates a MorphOSFilesystemNode for a given path.
*
* @param path Common::String with the path the new node should point to.
*/
MorphOSFilesystemNode(const Common::String &p);
/**
* Creates a MorphOSFilesystemNode given its lock and display name.
*
* @param pLock BPTR to the lock.
* @param pDisplayName name to be used for display, in case not supplied the FilePart() of the filename will be used.
*
* @note This shouldn't even be public as it's only internally, at best it should have been protected if not private.
*/
MorphOSFilesystemNode(BPTR pLock, const char *pDisplayName = 0);
/**
* Copy constructor.
*
* @note Needed because it duplicates the file lock.
*/
MorphOSFilesystemNode(const MorphOSFilesystemNode &node);
/**
* Destructor.
*/
~MorphOSFilesystemNode() override;
bool exists() const override;
Common::U32String getDisplayName() const override { return _sDisplayName; }
Common::String getName() const override { return _sDisplayName; }
Common::String getPath() const override { return _sPath; }
bool isDirectory() const override { return _bIsDirectory; }
bool isReadable() const override { return true; }
bool isWritable() const override { return true; }
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
};
#endif

View File

@@ -0,0 +1,44 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef __N64__
#include <n64utils.h>
#include <romfs.h>
#include "backends/fs/n64/n64-fs-factory.h"
#include "backends/fs/n64/n64-fs.h"
AbstractFSNode *N64FilesystemFactory::makeRootFileNode() const {
return new N64FilesystemNode();
}
AbstractFSNode *N64FilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
return romfs_getcwd(buf, MAXPATHLEN) ? new N64FilesystemNode(Common::String(buf), false) : NULL;
}
AbstractFSNode *N64FilesystemFactory::makeFileNodePath(const Common::String &path) const {
assert(!path.empty());
return new N64FilesystemNode(path, false);
}
#endif

View File

@@ -0,0 +1,38 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef N64_FILESYSTEM_FACTORY_H
#define N64_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* Creates N64FilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class N64FilesystemFactory final : public FilesystemFactory {
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*N64_FILESYSTEM_FACTORY_H*/

167
backends/fs/n64/n64-fs.cpp Normal file
View File

@@ -0,0 +1,167 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef __N64__
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#include "backends/fs/n64/n64-fs.h"
#include "backends/fs/n64/romfsstream.h"
#include <romfs.h>
#include <sys/param.h>
#include <unistd.h>
#include <n64utils.h>
#define ROOT_PATH "/"
N64FilesystemNode::N64FilesystemNode() {
_isDirectory = true;
_displayName = "Root";
_isValid = true;
_path = ROOT_PATH;
}
N64FilesystemNode::N64FilesystemNode(const Common::String &p, bool verify) {
assert(p.size() > 0);
_path = p;
_displayName = lastPathComponent(_path, '/');
_isValid = true;
_isDirectory = true;
// Check if it's a dir
ROMFILE *tmpfd = romfs_open(p.c_str(), "r");
if (tmpfd) {
_isDirectory = (tmpfd->type == 0 || tmpfd->type == 1);
romfs_close(tmpfd);
}
}
bool N64FilesystemNode::exists() const {
int ret = -1;
ret = romfs_access(_path.c_str(), F_OK);
return ret == 0;
}
bool N64FilesystemNode::isReadable() const {
int ret = -1;
ret = romfs_access(_path.c_str(), R_OK);
return ret == 0;
}
// We can't write on ROMFS!
bool N64FilesystemNode::isWritable() const {
return false;
}
AbstractFSNode *N64FilesystemNode::getChild(const Common::String &n) const {
// FIXME: Pretty lame implementation! We do no error checking to speak
// of, do not check if this is a special node, etc.
assert(_isDirectory);
Common::String newPath(_path);
if (_path.lastChar() != '/')
newPath += '/';
newPath += n;
return new N64FilesystemNode(newPath, true);
}
bool N64FilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
assert(_isDirectory);
ROMDIR *dirp = romfs_opendir(_path.c_str());
romfs_dirent *dp;
if (dirp == NULL)
return false;
// loop over dir entries using readdir
while ((dp = romfs_readdir(dirp)) != NULL) {
// Skip 'invisible' files if necessary
if (dp->entryname[0] == '.' && !hidden) {
free(dp);
continue;
}
// Skip '.' and '..' to avoid cycles
if ((dp->entryname[0] == '.' && dp->entryname[1] == 0) || (dp->entryname[0] == '.' && dp->entryname[1] == '.')) {
free(dp);
continue;
}
// Start with a clone of this node, with the correct path set
N64FilesystemNode entry(*this);
entry._displayName = dp->entryname;
if (_path.lastChar() != '/')
entry._path += '/';
entry._path += entry._displayName;
// Force validity for now...
entry._isValid = 1;
entry._isDirectory = (dp->type == 0 || dp->type == 1);
// Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
(mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory)) {
free(dp);
continue;
}
myList.push_back(new N64FilesystemNode(entry));
free(dp);
}
romfs_closedir(dirp);
return true;
}
AbstractFSNode *N64FilesystemNode::getParent() const {
if (_path == ROOT_PATH)
return 0;
const char *start = _path.c_str();
const char *end = lastPathComponent(_path, '/');
return new N64FilesystemNode(Common::String(start, end - start), false);
}
Common::SeekableReadStream *N64FilesystemNode::createReadStream() {
return RomfsStream::makeFromPath(getPath(), false);
}
Common::SeekableWriteStream *N64FilesystemNode::createWriteStream(bool atomic) {
// TODO: Add atomic support if possible
return RomfsStream::makeFromPath(getPath(), true);
}
bool N64FilesystemNode::createDirectory() {
return _isValid && _isDirectory;
}
#endif //#ifdef __N64__

78
backends/fs/n64/n64-fs.h Normal file
View File

@@ -0,0 +1,78 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef N64_FILESYSTEM_H
#define N64_FILESYSTEM_H
#include "backends/fs/abstract-fs.h"
/**
* Implementation of the ScummVM file system API based on N64 Hkz romfs.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class N64FilesystemNode final : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _path;
bool _isDirectory;
bool _isValid;
public:
/**
* Creates a N64FilesystemNode with the root node as path.
*/
N64FilesystemNode();
/**
* Creates a N64FilesystemNode for a given path.
*
* @param path Common::String with the path the new node should point to.
* @param verify true if the isValid and isDirectory flags should be verified during the construction.
*/
N64FilesystemNode(const Common::String &p, bool verify = true);
bool exists() const override;
Common::U32String getDisplayName() const override {
return _displayName;
}
Common::String getName() const override {
return _displayName;
}
Common::String getPath() const override {
return _path;
}
bool isDirectory() const override {
return _isDirectory;
}
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
};
#endif

View File

@@ -0,0 +1,84 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef __N64__
#include <romfs.h>
#include "backends/fs/n64/romfsstream.h"
RomfsStream::RomfsStream(void *handle) : _handle(handle) {
assert(handle);
}
RomfsStream::~RomfsStream() {
romfs_close((ROMFILE *)_handle);
}
bool RomfsStream::err() const {
return romfs_error((ROMFILE *)_handle) != 0;
}
void RomfsStream::clearErr() {
romfs_clearerr((ROMFILE *)_handle);
}
bool RomfsStream::eos() const {
return romfs_eof((ROMFILE *)_handle) != 0;
}
int64 RomfsStream::pos() const {
return romfs_tell((ROMFILE *)_handle);
}
int64 RomfsStream::size() const {
int64 oldPos = romfs_tell((ROMFILE *)_handle);
romfs_seek((ROMFILE *)_handle, 0, SEEK_END);
int64 length = romfs_tell((ROMFILE *)_handle);
romfs_seek((ROMFILE *)_handle, oldPos, SEEK_SET);
return length;
}
bool RomfsStream::seek(int64 offs, int whence) {
return romfs_seek((ROMFILE *)_handle, offs, whence) >= 0;
}
uint32 RomfsStream::read(void *ptr, uint32 len) {
return romfs_read((byte *)ptr, 1, len, (ROMFILE *)_handle);
}
uint32 RomfsStream::write(const void *ptr, uint32 len) {
return romfs_write(ptr, 1, len, (ROMFILE *)_handle);
}
bool RomfsStream::flush() {
return romfs_flush((ROMFILE *)_handle) == 0;
}
RomfsStream *RomfsStream::makeFromPath(const Common::String &path, bool writeMode) {
ROMFILE *handle = romfs_open(path.c_str(), writeMode ? "wb" : "rb");
if (handle)
return new RomfsStream(handle);
return 0;
}
#endif /* __N64__ */

View File

@@ -0,0 +1,58 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_FS_ROMFSSTREAM_H
#define BACKENDS_FS_ROMFSSTREAM_H
#include "common/scummsys.h"
#include "common/noncopyable.h"
#include "common/stream.h"
#include "common/str.h"
class RomfsStream final : public Common::SeekableReadStream, public Common::SeekableWriteStream, public Common::NonCopyable {
protected:
/** File handle to the actual file. */
void *_handle;
public:
/**
* Given a path, invokes fopen on that path and wrap the result in a
* RomfsStream instance.
*/
static RomfsStream *makeFromPath(const Common::String &path, bool writeMode);
RomfsStream(void *handle);
~RomfsStream() override;
bool err() const override;
void clearErr() override;
bool eos() const override;
uint32 write(const void *dataPtr, uint32 dataSize) override;
bool flush() override;
int64 pos() const override;
int64 size() const override;
bool seek(int64 offs, int whence = SEEK_SET) override;
uint32 read(void *dataPtr, uint32 dataSize) override;
};
#endif

View File

@@ -0,0 +1,70 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(POSIX) || defined(PSP2) || defined(__DS__)
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/posix-drives/posix-drives-fs-factory.h"
#include "backends/fs/posix-drives/posix-drives-fs.h"
#include <unistd.h>
void DrivesPOSIXFilesystemFactory::addDrive(const Common::String &name) {
_config.drives.push_back(Common::normalizePath(name, '/'));
}
void DrivesPOSIXFilesystemFactory::configureBuffering(DrivePOSIXFilesystemNode::BufferingMode bufferingMode, uint32 bufferSize) {
_config.bufferingMode = bufferingMode;
_config.bufferSize = bufferSize;
}
AbstractFSNode *DrivesPOSIXFilesystemFactory::makeRootFileNode() const {
return new DrivePOSIXFilesystemNode(_config);
}
AbstractFSNode *DrivesPOSIXFilesystemFactory::makeCurrentDirectoryFileNode() const {
#if defined(PSP2) // The Vita does not have getcwd
return makeRootFileNode();
#else
char buf[MAXPATHLEN];
return getcwd(buf, MAXPATHLEN) ? new DrivePOSIXFilesystemNode(buf, _config) : makeRootFileNode();
#endif
}
AbstractFSNode *DrivesPOSIXFilesystemFactory::makeFileNodePath(const Common::String &path) const {
assert(!path.empty());
return new DrivePOSIXFilesystemNode(path, _config);
}
bool DrivesPOSIXFilesystemFactory::StaticDrivesConfig::getDrives(AbstractFSList &list, bool hidden) const {
for (uint i = 0; i < drives.size(); i++) {
list.push_back(_factory->makeFileNodePath(drives[i]));
}
return true;
}
bool DrivesPOSIXFilesystemFactory::StaticDrivesConfig::isDrive(const Common::String &path) const {
DrivesArray::const_iterator drive = Common::find(drives.begin(), drives.end(), path);
return drive != drives.end();
}
#endif

View File

@@ -0,0 +1,76 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef POSIX_DRIVES_FILESYSTEM_FACTORY_H
#define POSIX_DRIVES_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
#include "backends/fs/posix-drives/posix-drives-fs.h"
/**
* A FilesystemFactory implementation for filesystems with a special
* top-level directory with hard-coded entries but that otherwise
* implement the POSIX APIs.
*
* For used with paths like these:
* - 'sdcard:/games/scummvm.ini'
* - 'hdd1:/usr/bin'
*/
class DrivesPOSIXFilesystemFactory : public FilesystemFactory {
public:
DrivesPOSIXFilesystemFactory() : _config(this) { }
/**
* Add a drive to the top-level directory
*/
void addDrive(const Common::String &name);
/**
* Configure file stream buffering
*
* @param bufferingMode select the buffering implementation to use
* @param bufferSize the size of the IO buffer in bytes. A buffer size of 0 means the default size should be used
*/
void configureBuffering(DrivePOSIXFilesystemNode::BufferingMode bufferingMode, uint32 bufferSize);
protected:
// FilesystemFactory API
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
typedef Common::Array<Common::String> DrivesArray;
struct StaticDrivesConfig : public DrivePOSIXFilesystemNode::Config {
StaticDrivesConfig(const DrivesPOSIXFilesystemFactory *factory) : _factory(factory) { }
bool getDrives(AbstractFSList &list, bool hidden) const override;
bool isDrive(const Common::String &path) const override;
DrivesArray drives;
private:
const DrivesPOSIXFilesystemFactory *_factory;
};
StaticDrivesConfig _config;
};
#endif

View File

@@ -0,0 +1,193 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(POSIX) || defined(PSP2) || defined(__DS__)
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/posix-drives/posix-drives-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/algorithm.h"
#include "common/bufferedstream.h"
#include <dirent.h>
DrivePOSIXFilesystemNode::DrivePOSIXFilesystemNode(const Common::String &path, const Config &config) :
POSIXFilesystemNode(path),
_isPseudoRoot(false),
_config(config) {
if (isDrive(path)) {
_isDirectory = true;
_isValid = true;
// FIXME: Add a setting for deciding whether drive paths need to end with a slash
if (!_path.hasSuffix("/")) {
_path += "/";
}
return;
}
}
DrivePOSIXFilesystemNode::DrivePOSIXFilesystemNode(const Config &config) :
_isPseudoRoot(true),
_config(config) {
_isDirectory = true;
_isValid = false;
}
void DrivePOSIXFilesystemNode::configureStream(StdioStream *stream) {
if (!stream) {
return;
}
if (_config.bufferingMode != kBufferingModeStdio) {
// Disable stdio buffering
stream->setBufferSize(0);
} else if (_config.bufferSize) {
stream->setBufferSize(_config.bufferSize);
}
}
Common::SeekableReadStream *DrivePOSIXFilesystemNode::createReadStream() {
StdioStream *readStream = PosixIoStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
configureStream(readStream);
if (_config.bufferingMode == kBufferingModeScummVM) {
uint32 bufferSize = _config.bufferSize != 0 ? _config.bufferSize : 1024;
return Common::wrapBufferedSeekableReadStream(readStream, bufferSize, DisposeAfterUse::YES);
}
return readStream;
}
Common::SeekableWriteStream *DrivePOSIXFilesystemNode::createWriteStream(bool atomic) {
StdioStream *writeStream = PosixIoStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
configureStream(writeStream);
if (_config.bufferingMode == kBufferingModeScummVM) {
uint32 bufferSize = _config.bufferSize != 0 ? _config.bufferSize : 1024;
return Common::wrapBufferedWriteStream(writeStream, bufferSize);
}
return writeStream;
}
DrivePOSIXFilesystemNode *DrivePOSIXFilesystemNode::getChildWithKnownType(const Common::String &n, bool isDirectoryFlag) const {
assert(_isDirectory);
// Make sure the string contains no slashes
assert(!n.contains('/'));
Common::String newPath(_path);
if (_path.lastChar() != '/')
newPath += '/';
newPath += n;
DrivePOSIXFilesystemNode *child = reinterpret_cast<DrivePOSIXFilesystemNode *>(makeNode());
child->_path = newPath;
child->_isValid = true;
child->_isPseudoRoot = false;
child->_isDirectory = isDirectoryFlag;
child->_displayName = n;
return child;
}
AbstractFSNode *DrivePOSIXFilesystemNode::getChild(const Common::String &n) const {
DrivePOSIXFilesystemNode *child = getChildWithKnownType(n, false);
child->setFlags();
return child;
}
bool DrivePOSIXFilesystemNode::getChildren(AbstractFSList &list, AbstractFSNode::ListMode mode, bool hidden) const {
assert(_isDirectory);
if (_isPseudoRoot) {
return _config.getDrives(list, hidden);
} else {
DIR *dirp = opendir(_path.c_str());
struct dirent *dp;
if (!dirp)
return false;
while ((dp = readdir(dirp)) != nullptr) {
// Skip 'invisible' files if necessary
if (dp->d_name[0] == '.' && !hidden) {
continue;
}
// Skip '.' and '..' to avoid cycles
if ((dp->d_name[0] == '.' && dp->d_name[1] == 0) || (dp->d_name[0] == '.' && dp->d_name[1] == '.')) {
continue;
}
AbstractFSNode *child = nullptr;
#if !defined(SYSTEM_NOT_SUPPORTING_D_TYPE)
if (dp->d_type == DT_DIR || dp->d_type == DT_REG) {
child = getChildWithKnownType(dp->d_name, dp->d_type == DT_DIR);
} else
#endif
{
child = getChild(dp->d_name);
}
// Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && child->isDirectory()) ||
(mode == Common::FSNode::kListDirectoriesOnly && !child->isDirectory())) {
delete child;
continue;
}
list.push_back(child);
}
closedir(dirp);
return true;
}
}
AbstractFSNode *DrivePOSIXFilesystemNode::getParent() const {
if (_isPseudoRoot) {
return nullptr;
}
if (isDrive(_path)) {
return makeNode();
}
return POSIXFilesystemNode::getParent();
}
bool DrivePOSIXFilesystemNode::isDrive(const Common::String &path) const {
Common::String normalizedPath = Common::normalizePath(path, '/');
return _config.isDrive(normalizedPath);
}
#endif //#if defined(POSIX)

View File

@@ -0,0 +1,85 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef POSIX_DRIVES_FILESYSTEM_H
#define POSIX_DRIVES_FILESYSTEM_H
#include "backends/fs/posix/posix-fs.h"
class StdioStream;
/**
* POSIX file system node where the top-level directory is a hardcoded
* list of drives.
*/
class DrivePOSIXFilesystemNode : public POSIXFilesystemNode {
protected:
virtual AbstractFSNode *makeNode() const {
return new DrivePOSIXFilesystemNode(_config);
}
AbstractFSNode *makeNode(const Common::String &path) const override {
return new DrivePOSIXFilesystemNode(path, _config);
}
public:
enum BufferingMode {
/** IO buffering is fully disabled */
kBufferingModeDisabled,
/** IO buffering is enabled and uses the libc implemenation */
kBufferingModeStdio,
/** IO buffering is enabled and uses ScummVM's buffering stream wraappers */
kBufferingModeScummVM
};
struct Config {
// Use the default stdio buffer size
Config() : bufferingMode(kBufferingModeStdio), bufferSize(0) { }
virtual ~Config() { }
virtual bool getDrives(AbstractFSList &list, bool hidden) const = 0;
virtual bool isDrive(const Common::String &path) const = 0;
BufferingMode bufferingMode;
uint32 bufferSize;
};
DrivePOSIXFilesystemNode(const Common::String &path, const Config &config);
DrivePOSIXFilesystemNode(const Config &config);
// AbstractFSNode API
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
protected:
const Config &_config;
private:
bool _isPseudoRoot;
DrivePOSIXFilesystemNode *getChildWithKnownType(const Common::String &n, bool isDirectoryFlag) const;
bool isDrive(const Common::String &path) const;
void configureStream(StdioStream *stream);
};
#endif

View File

@@ -0,0 +1,67 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(POSIX) || defined(PLAYSTATION3) || defined(__DS__)
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
// Also with clock() in sys/time.h in some macOS SDKs.
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#define FORBIDDEN_SYMBOL_EXCEPTION_exit //Needed for IRIX's unistd.h
#define FORBIDDEN_SYMBOL_EXCEPTION_random
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom
#include "backends/fs/posix/posix-fs-factory.h"
#include "backends/fs/posix/posix-fs.h"
#include <unistd.h>
AbstractFSNode *POSIXFilesystemFactory::makeRootFileNode() const {
return new POSIXFilesystemNode("/");
}
AbstractFSNode *POSIXFilesystemFactory::makeCurrentDirectoryFileNode() const {
#if defined(__ANDROID__)
// Keep this here if we still want to maintain support for the Android SDL port, since this affects that too
//
// For Android it does not make sense to have "." in Search Manager as a current directory file node, so we skip it here
// Otherwise this can potentially lead to a crash since, in Android getcwd() returns the root path "/"
// and when SearchMan is used (eg. SearchSet::createReadStreamForMember) and it tries to search root path (and calls POSIXFilesystemNode::getChildren())
// then a JNI call is made (JNI::getAllStorageLocations()) which leads to a crash if done from the wrong context -- and is also useless overhead as a call in that case.
// This fixes the error case: Loading "Beneath A Steel Sky" with Adlib or FluidSynth audio once, exiting to Launcher via in-game ScummVM menu and re-launching the game.
// Don't return NULL here, since it causes crash with other engines (eg. Blade Runner)
// Returning '.' here will cause POSIXFilesystemNode::getChildren() to ignore it
//
// We also skip adding the '.' directory to SearchManager (in archive.cpp, SearchManager::clear())
char buf[MAXPATHLEN] = {'.', '\0'};
return new POSIXFilesystemNode(buf);
#else
char buf[MAXPATHLEN];
return getcwd(buf, MAXPATHLEN) ? new POSIXFilesystemNode(buf) : NULL;
#endif
}
AbstractFSNode *POSIXFilesystemFactory::makeFileNodePath(const Common::String &path) const {
assert(!path.empty());
return new POSIXFilesystemNode(path);
}
#endif

View File

@@ -0,0 +1,39 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef POSIX_FILESYSTEM_FACTORY_H
#define POSIX_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* Creates POSIXFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class POSIXFilesystemFactory : public FilesystemFactory {
protected:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*POSIX_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,368 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(POSIX) || defined(PLAYSTATION3) || defined(PSP2) || defined(__DS__)
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
// Also with clock() in sys/time.h in some macOS SDKs.
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
#define FORBIDDEN_SYMBOL_EXCEPTION_exit //Needed for IRIX's unistd.h
#define FORBIDDEN_SYMBOL_EXCEPTION_random
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom
#include "backends/fs/posix/posix-fs.h"
#include "backends/fs/posix/posix-iostream.h"
#include "common/algorithm.h"
#include <sys/param.h>
#include <sys/stat.h>
#ifdef MACOSX
#include <sys/types.h>
#endif
#ifdef PSP2
#include <psp2/io/stat.h>
#define mkdir sceIoMkdir
#endif
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef __OS2__
#define INCL_DOS
#include <os2.h>
#endif
bool POSIXFilesystemNode::exists() const {
return access(_path.c_str(), F_OK) == 0;
}
bool POSIXFilesystemNode::isReadable() const {
return access(_path.c_str(), R_OK) == 0;
}
bool POSIXFilesystemNode::isWritable() const {
return access(_path.c_str(), W_OK) == 0;
}
void POSIXFilesystemNode::setFlags() {
struct stat st;
_isValid = (0 == stat(_path.c_str(), &st));
_isDirectory = _isValid ? S_ISDIR(st.st_mode) : false;
}
POSIXFilesystemNode::POSIXFilesystemNode(const Common::String &p) {
assert(p.size() > 0);
// Expand "~/" to the value of the HOME env variable
if (p.hasPrefix("~/") || p == "~") {
const char *home = getenv("HOME");
if (home != NULL && strlen(home) < MAXPATHLEN) {
_path = home;
// Skip over the tilda.
if (p.size() > 1)
_path += p.c_str() + 1;
}
} else {
_path = p;
}
#ifdef __OS2__
// On OS/2, 'X:/' is a root of drive X, so we should not remove that last
// slash.
if (!(_path.size() == 3 && _path.hasSuffix(":/")))
#endif
// Normalize the path (that is, remove unneeded slashes etc.)
_path = Common::normalizePath(_path, '/');
_displayName = Common::lastPathComponent(_path, '/');
// TODO: should we turn relative paths into absolute ones?
// Pro: Ensures the "getParent" works correctly even for relative dirs.
// Contra: The user may wish to use (and keep!) relative paths in his
// config file, and converting relative to absolute paths may hurt him...
//
// An alternative approach would be to change getParent() to work correctly
// if "_path" is the empty string.
#if 0
if (!_path.hasPrefix("/")) {
char buf[MAXPATHLEN+1];
getcwd(buf, MAXPATHLEN);
Common::strcat_s(buf, "/");
_path = buf + _path;
}
#endif
// TODO: Should we enforce that the path is absolute at this point?
//assert(_path.hasPrefix("/"));
setFlags();
}
AbstractFSNode *POSIXFilesystemNode::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 POSIXFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
assert(_isDirectory);
#ifdef __OS2__
if (_path == "/") {
// Special case for the root dir: List all DOS drives
ULONG ulDrvNum;
ULONG ulDrvMap;
DosQueryCurrentDisk(&ulDrvNum, &ulDrvMap);
for (int i = 0; i < 26; i++) {
if (ulDrvMap & 1) {
char drive_root[] = "A:/";
drive_root[0] += i;
POSIXFilesystemNode *entry = new POSIXFilesystemNode();
entry->_isDirectory = true;
entry->_isValid = true;
entry->_path = drive_root;
entry->_displayName = "[" + Common::String(drive_root, 2) + "]";
myList.push_back(entry);
}
ulDrvMap >>= 1;
}
return true;
}
#endif
DIR *dirp = opendir(_path.c_str());
struct dirent *dp;
if (dirp == NULL)
return false;
// loop over dir entries using readdir
while ((dp = readdir(dirp)) != NULL) {
// Skip 'invisible' files if necessary
if (dp->d_name[0] == '.' && !hidden) {
continue;
}
// Skip '.' and '..' to avoid cycles
if ((dp->d_name[0] == '.' && dp->d_name[1] == 0) || (dp->d_name[0] == '.' && dp->d_name[1] == '.')) {
continue;
}
// Start with a clone of this node, with the correct path set
POSIXFilesystemNode entry(*this);
entry._displayName = dp->d_name;
if (_path.lastChar() != '/')
entry._path += '/';
entry._path += entry._displayName;
#if defined(SYSTEM_NOT_SUPPORTING_D_TYPE)
/* TODO: d_type is not part of POSIX, so it might not be supported
* on some of our targets. For those systems where it isn't supported,
* add this #elif case, which tries to use stat() instead.
*
* The d_type method is used to avoid costly recurrent stat() calls in big
* directories.
*/
entry.setFlags();
#else
switch (dp->d_type) {
case DT_DIR:
case DT_REG:
entry._isValid = true;
entry._isDirectory = (dp->d_type == DT_DIR);
break;
case DT_LNK:
entry._isValid = true;
struct stat st;
if (stat(entry._path.c_str(), &st) == 0)
entry._isDirectory = S_ISDIR(st.st_mode);
else
entry._isDirectory = false;
break;
case DT_UNKNOWN:
default:
// Fall back to stat()
//
// It's important NOT to limit this to DT_UNKNOWN, because d_type can
// be unreliable on some OSes and filesystems; a confirmed example is
// macOS 10.4, where d_type can hold bogus values when iterating over
// the files of a cddafs mount point (as used by MacOSXAudioCDManager).
entry.setFlags();
break;
}
#endif
// Skip files that are invalid for some reason (e.g. because we couldn't
// properly stat them).
if (!entry._isValid)
continue;
// Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
(mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory))
continue;
myList.push_back(new POSIXFilesystemNode(entry));
}
closedir(dirp);
return true;
}
AbstractFSNode *POSIXFilesystemNode::getParent() const {
if (_path == "/")
return 0; // The filesystem root has no parent
#ifdef __OS2__
if (_path.size() == 3 && _path.hasSuffix(":/"))
// This is a root directory of a drive
return makeNode("/"); // return a virtual root for a list of drives
#endif
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;
}
return makeNode(Common::String(start, end));
}
Common::SeekableReadStream *POSIXFilesystemNode::createReadStream() {
return PosixIoStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
}
Common::SeekableReadStream *POSIXFilesystemNode::createReadStreamForAltStream(Common::AltStreamType altStreamType) {
#ifdef MACOSX
if (altStreamType == Common::AltStreamType::MacResourceFork) {
// Check the actual fork on a Mac computer
return PosixIoStream::makeFromPath(getPath() + "/..namedfork/rsrc", StdioStream::WriteMode_Read);
}
#endif
return nullptr;
}
Common::SeekableWriteStream *POSIXFilesystemNode::createWriteStream(bool atomic) {
return PosixIoStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
}
bool POSIXFilesystemNode::createDirectory() {
if (mkdir(_path.c_str(), 0755) == 0)
setFlags();
return _isValid && _isDirectory;
}
namespace Posix {
bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
struct stat sb;
// Check whether the prefix exists if one is supplied.
if (prefix) {
if (stat(prefix, &sb) != 0) {
return false;
} else if (!S_ISDIR(sb.st_mode)) {
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';
}
if (mkdir(path.c_str(), 0755) != 0) {
if (errno == EEXIST) {
if (stat(path.c_str(), &sb) != 0) {
return false;
} else if (!S_ISDIR(sb.st_mode)) {
return false;
}
} else {
return false;
}
}
*cur = '/';
} while (cur++ != end);
return true;
}
} // End of namespace Posix
#endif //#if defined(POSIX)

View File

@@ -0,0 +1,94 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef POSIX_FILESYSTEM_H
#define POSIX_FILESYSTEM_H
#include "backends/fs/abstract-fs.h"
/**
* Implementation of the ScummVM file system API based on POSIX.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class POSIXFilesystemNode : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _path;
bool _isDirectory;
bool _isValid;
virtual AbstractFSNode *makeNode(const Common::String &path) const {
return new POSIXFilesystemNode(path);
}
/**
* Plain constructor, for internal use only (hence protected).
*/
POSIXFilesystemNode() : _isDirectory(false), _isValid(false) {}
public:
/**
* Creates a POSIXFilesystemNode for a given path.
*
* @param path the path the new node should point to.
*/
POSIXFilesystemNode(const Common::String &path);
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
bool isDirectory() const override { return _isDirectory; }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
protected:
/**
* Tests and sets the _isValid and _isDirectory flags, using the stat() function.
*/
virtual void setFlags();
};
namespace Posix {
/**
* 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 Posix
#endif

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/posix/posix-iostream.h"
#include <sys/stat.h>
PosixIoStream::PosixIoStream(void *handle) :
StdioStream(handle) {
}
int64 PosixIoStream::size() const {
int fd = fileno((FILE *)_handle);
if (fd == -1) {
return StdioStream::size();
}
// Using fstat to obtain the file size is generally faster than fseek / ftell
// because it does not affect the IO buffer.
struct stat st;
if (fstat(fd, &st) == -1) {
return StdioStream::size();
}
return st.st_size;
}

View File

@@ -0,0 +1,42 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_FS_POSIX_POSIXIOSTREAM_H
#define BACKENDS_FS_POSIX_POSIXIOSTREAM_H
#include "backends/fs/stdiostream.h"
/**
* A file input / output stream using POSIX interfaces
*/
class PosixIoStream final : public StdioStream {
public:
static StdioStream *makeFromPath(const Common::String &path, StdioStream::WriteMode writeMode) {
return StdioStream::makeFromPathHelper(path, writeMode, [](void *handle) -> StdioStream * {
return new PosixIoStream(handle);
});
}
PosixIoStream(void *handle);
int64 size() const override;
};
#endif

View File

@@ -0,0 +1,26 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "backends/fs/ps3/ps3-fs-factory.h"
AbstractFSNode *PS3FilesystemFactory::makeCurrentDirectoryFileNode() const {
return makeRootFileNode();
}

View File

@@ -0,0 +1,36 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PS3_FILESYSTEM_FACTORY_H
#define PS3_FILESYSTEM_FACTORY_H
#include "backends/fs/posix/posix-fs-factory.h"
/**
* Creates PS3FilesystemFactory objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class PS3FilesystemFactory final : public POSIXFilesystemFactory {
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
};
#endif /*PS3_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,69 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__PSP__)
// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
// __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
// __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#include "backends/fs/psp/psp-fs-factory.h"
#include "backends/fs/psp/psp-fs.h"
#include "backends/platform/psp/powerman.h"
#include <unistd.h>
namespace Common {
DECLARE_SINGLETON(PSPFilesystemFactory);
}
AbstractFSNode *PSPFilesystemFactory::makeRootFileNode() const {
return new PSPFilesystemNode();
}
AbstractFSNode *PSPFilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
char *ret = 0;
PowerMan.beginCriticalSection();
ret = getcwd(buf, MAXPATHLEN);
PowerMan.endCriticalSection();
return (ret ? new PSPFilesystemNode(buf) : NULL);
}
AbstractFSNode *PSPFilesystemFactory::makeFileNodePath(const Common::String &path) const {
return new PSPFilesystemNode(path, true);
}
#endif

View File

@@ -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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_FILESYSTEM_FACTORY_H
#define PSP_FILESYSTEM_FACTORY_H
#include "common/singleton.h"
#include "backends/fs/fs-factory.h"
/**
* Creates PSPFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class PSPFilesystemFactory final : public FilesystemFactory, public Common::Singleton<PSPFilesystemFactory> {
public:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
protected:
PSPFilesystemFactory() {}
private:
friend class Common::Singleton<SingletonBaseType>;
};
#endif /*PSP_FILESYSTEM_FACTORY_H*/

261
backends/fs/psp/psp-fs.cpp Normal file
View File

@@ -0,0 +1,261 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__PSP__)
// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
// __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
// __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#include "backends/fs/psp/psp-fs.h"
#include "backends/fs/psp/psp-stream.h"
#include "common/bufferedstream.h"
#include "engines/engine.h"
#include <sys/stat.h>
#include <unistd.h>
#include <pspkernel.h>
#define ROOT_PATH "ms0:/"
//#define __PSP_PRINT_TO_FILE__
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
PSPFilesystemNode::PSPFilesystemNode() {
_isDirectory = true;
_displayName = "Root";
_isValid = true;
_path = ROOT_PATH;
}
PSPFilesystemNode::PSPFilesystemNode(const Common::String &p, bool verify) {
DEBUG_ENTER_FUNC();
assert(p.size() > 0);
_path = p;
_displayName = lastPathComponent(_path, '/');
_isValid = true;
_isDirectory = true;
PSP_DEBUG_PRINT_FUNC("path [%s]\n", _path.c_str());
if (verify) {
struct stat st;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
_isValid = (0 == stat(_path.c_str(), &st));
PowerMan.endCriticalSection();
_isDirectory = S_ISDIR(st.st_mode);
}
}
bool PSPFilesystemNode::exists() const {
DEBUG_ENTER_FUNC();
int ret = 0;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n"); // Make sure to block in case of suspend
PSP_DEBUG_PRINT_FUNC("path [%s]\n", _path.c_str());
ret = access(_path.c_str(), F_OK);
PowerMan.endCriticalSection();
return (ret == 0);
}
bool PSPFilesystemNode::isReadable() const {
DEBUG_ENTER_FUNC();
int ret = 0;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n"); // Make sure to block in case of suspend
PSP_DEBUG_PRINT_FUNC("path [%s]\n", _path.c_str());
ret = access(_path.c_str(), R_OK);
PowerMan.endCriticalSection();
return (ret == 0);
}
bool PSPFilesystemNode::isWritable() const {
DEBUG_ENTER_FUNC();
int ret = 0;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n"); // Make sure to block in case of suspend
PSP_DEBUG_PRINT_FUNC("path [%s]\n", _path.c_str());
ret = access(_path.c_str(), W_OK);
PowerMan.endCriticalSection();
return ret == 0;
}
AbstractFSNode *PSPFilesystemNode::getChild(const Common::String &n) const {
DEBUG_ENTER_FUNC();
// FIXME: Pretty lame implementation! We do no error checking to speak
// of, do not check if this is a special node, etc.
assert(_isDirectory);
Common::String newPath(_path);
if (_path.lastChar() != '/')
newPath += '/';
newPath += n;
PSP_DEBUG_PRINT_FUNC("child [%s]\n", newPath.c_str());
AbstractFSNode *node = new PSPFilesystemNode(newPath, true);
return node;
}
bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
DEBUG_ENTER_FUNC();
assert(_isDirectory);
//TODO: honor the hidden flag
bool ret = true;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n"); // Make sure to block in case of suspend
PSP_DEBUG_PRINT_FUNC("Current path[%s]\n", _path.c_str());
int dfd = sceIoDopen(_path.c_str());
if (dfd > 0) {
SceIoDirent dir;
memset(&dir, 0, sizeof(dir));
while (sceIoDread(dfd, &dir) > 0) {
// Skip 'invisible files
if (dir.d_name[0] == '.')
continue;
PSPFilesystemNode entry;
entry._isValid = true;
entry._displayName = dir.d_name;
Common::String newPath(_path);
if (newPath.lastChar() != '/')
newPath += '/';
newPath += dir.d_name;
entry._path = newPath;
entry._isDirectory = dir.d_stat.st_attr & FIO_SO_IFDIR;
PSP_DEBUG_PRINT_FUNC("Child[%s], %s\n", entry._path.c_str(), entry._isDirectory ? "dir" : "file");
// Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
(mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory))
continue;
myList.push_back(new PSPFilesystemNode(entry));
}
sceIoDclose(dfd);
ret = true;
} else { // dfd <= 0
ret = false;
}
PowerMan.endCriticalSection();
return ret;
}
AbstractFSNode *PSPFilesystemNode::getParent() const {
DEBUG_ENTER_FUNC();
if (_path == ROOT_PATH)
return 0;
PSP_DEBUG_PRINT_FUNC("current[%s]\n", _path.c_str());
const char *start = _path.c_str();
const char *end = lastPathComponent(_path, '/');
AbstractFSNode *node = new PSPFilesystemNode(Common::String(start, end - start), false);
return node;
}
Common::SeekableReadStream *PSPFilesystemNode::createReadStream() {
const uint32 READ_BUFFER_SIZE = 1024;
Common::SeekableReadStream *stream = PspIoStream::makeFromPath(getPath(), false);
return Common::wrapBufferedSeekableReadStream(stream, READ_BUFFER_SIZE, DisposeAfterUse::YES);
}
Common::SeekableWriteStream *PSPFilesystemNode::createWriteStream(bool atomic) {
const uint32 WRITE_BUFFER_SIZE = 1024;
// TODO: Add atomic support if possible
Common::SeekableWriteStream *stream = PspIoStream::makeFromPath(getPath(), true);
return Common::wrapBufferedWriteStream(stream, WRITE_BUFFER_SIZE);
}
bool PSPFilesystemNode::createDirectory() {
DEBUG_ENTER_FUNC();
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n"); // Make sure to block in case of suspend
PSP_DEBUG_PRINT_FUNC("path [%s]\n", _path.c_str());
if (sceIoMkdir(_path.c_str(), 0777) == 0) {
struct stat st;
_isValid = (0 == stat(_path.c_str(), &st));
_isDirectory = S_ISDIR(st.st_mode);
}
PowerMan.endCriticalSection();
return _isValid && _isDirectory;
}
#endif //#ifdef __PSP__

70
backends/fs/psp/psp-fs.h Normal file
View File

@@ -0,0 +1,70 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_FILESYSTEM_H
#define PSP_FILESYSTEM_H
#include "backends/fs/abstract-fs.h"
/**
* Implementation of the ScummVM file system API based on PSPSDK API.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class PSPFilesystemNode : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _path;
bool _isDirectory;
bool _isValid;
public:
/**
* Creates a PSPFilesystemNode with the root node as path.
*/
PSPFilesystemNode();
/**
* Creates a PSPFilesystemNode for a given path.
*
* @param path Common::String with the path the new node should point to.
* @param verify true if the isValid and isDirectory flags should be verified during the construction.
*/
PSPFilesystemNode(const Common::String &p, bool verify = true);
virtual bool exists() const;
virtual Common::U32String 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::SeekableWriteStream *createWriteStream(bool atomic);
virtual bool createDirectory();
};
#endif

View File

@@ -0,0 +1,335 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef __PSP__
#include <pspiofilemgr.h>
#include "backends/platform/psp/powerman.h"
#include "backends/fs/psp/psp-stream.h"
#define MIN2(a,b) ((a < b) ? a : b)
#define MIN3(a,b,c) ( (a < b) ? (a < c ? a : c) : (b < c ? b : c) )
//#define __PSP_PRINT_TO_FILE__ /* For debugging suspend stuff, we have no screen output */
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
//#define DEBUG_BUFFERS /* to see the contents of the buffers being read */
#ifdef DEBUG_BUFFERS
void printBuffer(byte *ptr, uint32 len) {
uint32 printLen = len <= 10 ? len : 10;
for (int i = 0; i < printLen; i++) {
PSP_INFO_PRINT("%x ", ptr[i]);
}
if (len > 10) {
PSP_INFO_PRINT("... ");
for (int i = len - 10; i < len; i++)
PSP_INFO_PRINT("%x ", ptr[i]);
}
PSP_INFO_PRINT("\n");
}
#endif
// Class PspIoStream ------------------------------------------------
PspIoStream::PspIoStream(const Common::String &path, bool writeMode)
: _handle(0), _path(path), _fileSize(0), _writeMode(writeMode),
_physicalPos(0), _pos(0), _eos(false), _error(false),
_errorSuspend(0), _errorSource(0), _errorPos(0), _errorHandle(0), _suspendCount(0) {
DEBUG_ENTER_FUNC();
//assert(!path.empty()); // do we need this?
}
PspIoStream::~PspIoStream() {
DEBUG_ENTER_FUNC();
if (PowerMan.beginCriticalSection())
PSP_DEBUG_PRINT_FUNC("suspended\n");
PowerMan.unregisterForSuspend(this); // Unregister with powermanager to be suspended
// Must do this before fclose() or resume() will reopen.
sceIoClose(_handle);
PowerMan.endCriticalSection();
}
/* Function to open the file pointed to by the path.
*
*/
SceUID PspIoStream::open() {
DEBUG_ENTER_FUNC();
if (PowerMan.beginCriticalSection()) {
// No need to open? Just return the _handle resume() already opened
PSP_DEBUG_PRINT_FUNC("suspended\n");
}
_handle = sceIoOpen(_path.c_str(), _writeMode ? PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC : PSP_O_RDONLY, 0777);
if (_handle <= 0) {
_error = true;
_handle = 0;
}
// Get the file size. This way is much faster than going to the end of the file and back
SceIoStat stat;
sceIoGetstat(_path.c_str(), &stat);
_fileSize = stat.st_size; // 4GB file (32 bits) is big enough for us
PSP_DEBUG_PRINT("%s filesize[%d]\n", _path.c_str(), _fileSize);
PowerMan.registerForSuspend(this); // Register with the powermanager to be suspended
PowerMan.endCriticalSection();
return _handle;
}
bool PspIoStream::err() const {
DEBUG_ENTER_FUNC();
if (_error) // We dump since no printing to screen with suspend callback
PSP_ERROR("mem_error[%d], source[%d], suspend error[%d], pos[%d],"
"_errorPos[%d], _errorHandle[%p], suspendCount[%d]\n",
_error, _errorSource, _errorSuspend, _pos,
_errorPos, _errorHandle, _suspendCount);
return _error;
}
void PspIoStream::clearErr() {
_error = false;
}
bool PspIoStream::eos() const {
return _eos;
}
int64 PspIoStream::pos() const {
return _pos;
}
int64 PspIoStream::size() const {
return _fileSize;
}
bool PspIoStream::physicalSeekFromCur(int32 offset) {
int ret = sceIoLseek32(_handle, offset, PSP_SEEK_CUR);
if (ret < 0) {
_error = true;
PSP_ERROR("failed to seek in file[%s] to [%x]. Error[%x]\n", _path.c_str(), offset, ret);
return false;
}
_physicalPos += offset;
return true;
}
bool PspIoStream::seek(int64 offs, int whence) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT_FUNC("offset[0x%x], whence[%d], _pos[0x%x], _physPos[0x%x]\n", offs, whence, _pos, _physicalPos);
_eos = false;
int32 posToSearchFor = 0;
switch (whence) {
case SEEK_CUR:
posToSearchFor = _pos;
break;
case SEEK_END:
posToSearchFor = _fileSize;
break;
}
posToSearchFor += offs;
// Check for bad values
if (posToSearchFor < 0) {
_error = true;
return false;
} else if (posToSearchFor > _fileSize) {
_error = true;
_eos = true;
return false;
}
_pos = posToSearchFor;
return true;
}
uint32 PspIoStream::read(void *ptr, uint32 len) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x], ptr[%p], _pos[%x], _physPos[%x]\n", _path.c_str(), len, ptr, _pos, _physicalPos);
if (_error || _eos || len <= 0)
return 0;
uint32 lenRemainingInFile = _fileSize - _pos;
// check for getting EOS
if (len > lenRemainingInFile) {
len = lenRemainingInFile;
_eos = true;
}
if (PowerMan.beginCriticalSection())
PSP_DEBUG_PRINT_FUNC("suspended\n");
// check if we need to seek
if (_pos != _physicalPos) {
PSP_DEBUG_PRINT("seeking from %x to %x\n", _physicalPos, _pos);
if (!physicalSeekFromCur(_pos - _physicalPos)) {
_error = true;
return 0;
}
}
int ret = sceIoRead(_handle, ptr, len);
PowerMan.endCriticalSection();
_physicalPos += ret; // Update position
_pos = _physicalPos;
if (ret != (int)len) { // error
PSP_ERROR("sceIoRead returned [0x%x] instead of len[0x%x]\n", ret, len);
_error = true;
_errorSource = 4;
}
return ret;
}
uint32 PspIoStream::write(const void *ptr, uint32 len) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x], ptr[%p], _pos[%x], _physPos[%x]\n", _path.c_str(), len, ptr, _pos, _physicalPos);
if (!len || _error) // we actually get some calls with len == 0!
return 0;
_eos = false; // we can't have eos with write
if (PowerMan.beginCriticalSection())
PSP_DEBUG_PRINT_FUNC("suspended\n");
// check if we need to seek
if (_pos != _physicalPos)
if (!physicalSeekFromCur(_pos - _physicalPos)) {
_error = true;
return 0;
}
int ret = sceIoWrite(_handle, ptr, len);
PowerMan.endCriticalSection();
if (ret != (int)len) {
_error = true;
_errorSource = 5;
PSP_ERROR("sceIoWrite returned[0x%x] instead of len[0x%x]\n", ret, len);
}
_physicalPos += ret;
_pos = _physicalPos;
if (_pos > _fileSize)
_fileSize = _pos;
return ret;
}
bool PspIoStream::flush() {
return true;
}
// For the PSP, since we're building in suspend support, we moved opening
// the actual file to an open function since we need an actual PspIoStream object to suspend.
//
PspIoStream *PspIoStream::makeFromPath(const Common::String &path, bool writeMode) {
DEBUG_ENTER_FUNC();
PspIoStream *stream = new PspIoStream(path, writeMode);
if (stream->open() <= 0) {
delete stream;
stream = 0;
}
return stream;
}
/*
* Function to suspend the IO stream (called by PowerManager)
* we can have no output here
*/
int PspIoStream::suspend() {
DEBUG_ENTER_FUNC();
_suspendCount++;
if (_handle > 0 && _pos < 0) { /* check for error */
_errorSuspend = SuspendError;
_errorPos = _pos;
_errorHandle = _handle;
}
if (_handle > 0) {
sceIoClose(_handle); // close our file descriptor
_handle = 0xFFFFFFFF; // Set handle to non-null invalid value so makeFromPath doesn't return error
}
return 0;
}
/*
* Function to resume the IO stream (called by Power Manager)
*/
int PspIoStream::resume() {
DEBUG_ENTER_FUNC();
int ret = 0;
_suspendCount--;
// We reopen our file descriptor
_handle = sceIoOpen(_path.c_str(), _writeMode ? PSP_O_RDWR | PSP_O_CREAT : PSP_O_RDONLY, 0777); // open
if (_handle <= 0) {
_errorSuspend = ResumeError;
_errorPos = _pos;
}
// Resume our previous position if needed
if (_handle > 0 && _pos > 0) {
ret = sceIoLseek32(_handle, _pos, PSP_SEEK_SET);
_physicalPos = _pos;
if (ret < 0) { // Check for problem
_errorSuspend = ResumeError;
_errorPos = _pos;
_errorHandle = _handle;
}
}
return ret;
}
#endif /* __PSP__ */

View File

@@ -0,0 +1,90 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSPSTREAM_H_
#define PSPSTREAM_H_
#include <pspkerneltypes.h>
#include "backends/platform/psp/powerman.h"
//#include "common/list.h"
#include "common/noncopyable.h"
#include "common/stream.h"
#include "common/str.h"
/**
* Class to handle special suspend/resume needs of PSP IO Streams
*/
class PspIoStream final : public Common::SeekableReadStream, public Common::SeekableWriteStream, public Common::NonCopyable, public Suspendable {
protected:
SceUID _handle; // file handle
Common::String _path;
int _fileSize;
bool _writeMode; // for resuming in the right mode
int _physicalPos; // physical position in file
int _pos; // position. Sometimes virtual
bool _eos; // EOS flag
enum {
SuspendError = 2,
ResumeError = 3
};
// debug stuff
mutable int _error; // file error state
int _errorSuspend; // for debugging
mutable int _errorSource;
int _errorPos;
SceUID _errorHandle;
int _suspendCount;
bool physicalSeekFromCur(int32 offset);
public:
/**
* Given a path, invoke fopen on that path and wrap the result in a
* PspIoStream instance.
*/
static PspIoStream *makeFromPath(const Common::String &path, bool writeMode);
PspIoStream(const Common::String &path, bool writeMode);
~PspIoStream() override;
SceUID open(); // open the file pointed to by the file path
bool err() const override;
void clearErr() override;
bool eos() const override;
uint32 write(const void *dataPtr, uint32 dataSize) override;
bool flush() override;
int64 pos() const override;
int64 size() const override;
bool seek(int64 offs, int whence = SEEK_SET) override;
uint32 read(void *dataPtr, uint32 dataSize) override;
// for suspending
int suspend() override; /* Suspendable interface (power manager) */
int resume() override; /* " " */
};
#endif /* PSPSTREAM_H_ */

View File

@@ -0,0 +1,47 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#include "common/scummsys.h"
#if defined(RISCOS)
#include "backends/fs/riscos/riscos-fs-factory.h"
#include "backends/fs/riscos/riscos-fs.h"
#include <unistd.h>
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

View File

@@ -0,0 +1,39 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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 final : public FilesystemFactory {
protected:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*RISCOS_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,288 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#include "common/scummsys.h"
#if defined(RISCOS)
#include "backends/platform/sdl/riscos/riscos-utils.h"
#include "backends/fs/riscos/riscos-fs.h"
#include "backends/fs/stdiostream.h"
#include "common/algorithm.h"
// TODO: Replace use of access()
#include <unistd.h>
#include <kernel.h>
#include <swis.h>
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;
}
void RISCOSFilesystemNode::setFlags() {
_kernel_swi_regs regs;
regs.r[0] = 23;
regs.r[1] = (int)_nativePath.c_str();
_kernel_swi(OS_File, &regs, &regs);
if (regs.r[0] == 0) {
_isDirectory = false;
_isValid = false;
} else if (regs.r[0] == 2) {
_isDirectory = true;
_isValid = true;
} else {
_isDirectory = false;
_isValid = true;
}
}
RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) {
_path = p;
if (p == "/") {
_nativePath = "";
_isDirectory = true;
_isValid = true;
} else {
_nativePath = RISCOS_Utils::toRISCOS(p);
setFlags();
}
}
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[MAXPATHLEN] = "";
for (int fsNum = 0; fsNum < 256; fsNum += 1) {
if (fsNum == 33 || fsNum == 46 || fsNum == 53 || fsNum == 99)
continue;
_kernel_swi_regs regs;
regs.r[0] = 33;
regs.r[1] = fsNum;
regs.r[2] = (int)fsname;
regs.r[3] = sizeof(fsname);
_kernel_swi(OS_FSControl, &regs, &regs);
if (fsname[0] == 0)
continue;
int drives = (fsNum == 193) ? 23 : 9;
for (int discnum = 0; discnum <= drives; discnum += 1) {
const Common::String path = Common::String::format("%s::%d.$", fsname, discnum);
char outpath[MAXPATHLEN] = "";
regs.r[0] = 37;
regs.r[1] = (int)path.c_str();
regs.r[2] = (int)outpath;
regs.r[3] = 0;
regs.r[4] = 0;
regs.r[5] = sizeof(outpath);
if (_kernel_swi(OS_FSControl, &regs, &regs) != NULL)
continue;
RISCOSFilesystemNode *entry = new RISCOSFilesystemNode();
entry->_nativePath = outpath;
entry->_path = Common::String('/') + outpath;
entry->_displayName = outpath;
entry->setFlags();
if (entry->_isDirectory)
myList.push_back(entry);
}
}
return true;
}
char file[MAXPATHLEN];
_kernel_swi_regs regs;
regs.r[0] = 9;
regs.r[1] = (int)_nativePath.c_str();
regs.r[2] = (int)file;
regs.r[3] = 1;
regs.r[4] = 0;
regs.r[5] = sizeof(file);
regs.r[6] = 0;
while (regs.r[4] != -1) {
_kernel_swi(OS_GBPB, &regs, &regs);
if (regs.r[4] == -1)
break;
// Start with a clone of this node, with the correct path set
RISCOSFilesystemNode entry(*this);
entry._displayName = file;
entry._displayName = RISCOS_Utils::toUnix(entry._displayName);
if (_path.lastChar() != '/')
entry._path += '/';
entry._path += entry._displayName;
entry._nativePath = RISCOS_Utils::toRISCOS(entry._path);
entry.setFlags();
if (!entry._isValid)
continue;
// 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(), StdioStream::WriteMode_Read);
}
Common::SeekableWriteStream *RISCOSFilesystemNode::createWriteStream(bool atomic) {
return StdioStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
}
bool RISCOSFilesystemNode::createDirectory() {
_kernel_swi_regs regs;
regs.r[0] = 8;
regs.r[1] = (int)_nativePath.c_str();
if (_kernel_swi(OS_File, &regs, &regs) == NULL)
setFlags();
return _isValid && _isDirectory;
}
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->createDirectory()) {
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)

View File

@@ -0,0 +1,92 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#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 final : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _nativePath;
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);
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
bool isDirectory() const override { return _isDirectory; }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
private:
/**
* Tests and sets the _isValid and _isDirectory flags, using OS_File 20.
*/
virtual void setFlags();
};
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

216
backends/fs/stdiostream.cpp Normal file
View File

@@ -0,0 +1,216 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if !defined(DISABLE_STDIO_FILESTREAM)
// Disable symbol overrides so that we can use FILE, fopen etc.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
// for Windows unicode fopen(): _wfopen() and for Win32::moveFile()
#if defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "backends/platform/sdl/win32/win32_wrapper.h"
#endif
// Include this after windows.h so we don't get a warning for redefining ARRAYSIZE
#include "backends/fs/stdiostream.h"
#include "common/textconsole.h"
#if defined(__DC__)
// libronin doesn't support rename
#define STDIOSTREAM_NO_ATOMIC_SUPPORT
#endif
#if defined(ATARI)
// Atari file names must have a 8.3 format, atomic breaks this
#define STDIOSTREAM_NO_ATOMIC_SUPPORT
#endif
#if defined(RISCOS)
// RISC OS file names are expected to be 10 characters or less, atomic makes this hard to guarantee
#define STDIOSTREAM_NO_ATOMIC_SUPPORT
#endif
StdioStream::StdioStream(void *handle) : _handle(handle), _path(nullptr) {
assert(handle);
}
StdioStream::~StdioStream() {
fclose((FILE *)_handle);
if (!_path) {
return;
}
// _path is set: recreate the temporary file name and rename the file to
// its real name
Common::String tmpPath(*_path);
tmpPath += ".tmp";
if (!moveFile(tmpPath, *_path)) {
warning("Couldn't save file %s", _path->c_str());
}
delete _path;
}
bool StdioStream::moveFile(const Common::String &src, const Common::String &dst) {
#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
// This piece of code can't be in a subclass override, as moveFile is called from the destructor.
// In this case, the vtable is reset to the StdioStream one before calling moveFile.
#if defined(WIN32)
return Win32::moveFile(src, dst);
#else
if (!rename(src.c_str(), dst.c_str())) {
return true;
}
// Error: try to delete the file first
(void)remove(dst.c_str());
return !rename(src.c_str(), dst.c_str());
#endif
#else // STDIOSTREAM_NO_ATOMIC_SUPPORT
return false;
#endif
}
bool StdioStream::err() const {
return ferror((FILE *)_handle) != 0;
}
void StdioStream::clearErr() {
clearerr((FILE *)_handle);
}
bool StdioStream::eos() const {
return feof((FILE *)_handle) != 0;
}
int64 StdioStream::pos() const {
#if defined(WIN32)
return _ftelli64((FILE *)_handle);
#elif defined(HAS_FSEEKO_OFFT_64)
return ftello((FILE *)_handle);
#elif defined(HAS_FSEEKO64)
return ftello64((FILE *)_handle);
#else
return ftell((FILE *)_handle);
#endif
}
int64 StdioStream::size() const {
#if defined(WIN32)
int64 oldPos = _ftelli64((FILE *)_handle);
_fseeki64((FILE *)_handle, 0, SEEK_END);
int64 length = _ftelli64((FILE *)_handle);
_fseeki64((FILE *)_handle, oldPos, SEEK_SET);
#elif defined(HAS_FSEEKO_OFFT_64)
int64 oldPos = ftello((FILE *)_handle);
fseeko((FILE *)_handle, 0, SEEK_END);
int64 length = ftello((FILE *)_handle);
fseeko((FILE *)_handle, oldPos, SEEK_SET);
#elif defined(HAS_FSEEKO64)
int64 oldPos = ftello64((FILE *)_handle);
fseeko64((FILE *)_handle, 0, SEEK_END);
int64 length = ftello64((FILE *)_handle);
fseeko64((FILE *)_handle, oldPos, SEEK_SET);
#else
int64 oldPos = ftell((FILE *)_handle);
fseek((FILE *)_handle, 0, SEEK_END);
int64 length = ftell((FILE *)_handle);
fseek((FILE *)_handle, oldPos, SEEK_SET);
#endif
return length;
}
bool StdioStream::seek(int64 offs, int whence) {
#if defined(WIN32)
return _fseeki64((FILE *)_handle, offs, whence) == 0;
#elif defined(HAS_FSEEKO_OFFT_64)
return fseeko((FILE *)_handle, offs, whence) == 0;
#elif defined(HAS_FSEEKO64)
return fseeko64((FILE *)_handle, offs, whence) == 0;
#else
return fseek((FILE *)_handle, offs, whence) == 0;
#endif
}
uint32 StdioStream::read(void *ptr, uint32 len) {
return fread((byte *)ptr, 1, len, (FILE *)_handle);
}
bool StdioStream::setBufferSize(uint32 bufferSize) {
if (bufferSize != 0) {
return setvbuf((FILE *)_handle, nullptr, _IOFBF, bufferSize) == 0;
} else {
return setvbuf((FILE *)_handle, nullptr, _IONBF, 0) == 0;
}
}
uint32 StdioStream::write(const void *ptr, uint32 len) {
return fwrite(ptr, 1, len, (FILE *)_handle);
}
bool StdioStream::flush() {
return fflush((FILE *)_handle) == 0;
}
StdioStream *StdioStream::makeFromPathHelper(const Common::String &path, WriteMode writeMode,
StdioStream *(*factory)(void *handle)) {
Common::String tmpPath(path);
// If no atmoic support is compiled in, WriteMode_WriteAtomic must behave like WriteMode_Write
#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
// In atomic mode we create a temporary file and rename it when closing the file descriptor
if (writeMode == WriteMode_WriteAtomic) {
tmpPath += ".tmp";
}
#endif
#if defined(WIN32) && defined(UNICODE)
wchar_t *wPath = Win32::stringToTchar(tmpPath);
FILE *handle = _wfopen(wPath, writeMode == WriteMode_Read ? L"rb" : L"wb");
free(wPath);
#elif defined(HAS_FOPEN64)
FILE *handle = fopen64(tmpPath.c_str(), writeMode == WriteMode_Read ? "rb" : "wb");
#else
FILE *handle = fopen(tmpPath.c_str(), writeMode == WriteMode_Read ? "rb" : "wb");
#endif
if (!handle) {
return nullptr;
}
StdioStream *stream = factory(handle);
#ifndef STDIOSTREAM_NO_ATOMIC_SUPPORT
// Store the final path alongside the stream
// If _path is not nullptr, it will be used to rename the file
// when closing it
if (writeMode == WriteMode_WriteAtomic) {
stream->_path = new Common::String(path);
}
#endif
return stream;
}
#endif

98
backends/fs/stdiostream.h Normal file
View File

@@ -0,0 +1,98 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_FS_STDIOSTREAM_H
#define BACKENDS_FS_STDIOSTREAM_H
#include "common/scummsys.h"
#include "common/noncopyable.h"
#include "common/stream.h"
#include "common/str.h"
class StdioStream : public Common::SeekableReadStream, public Common::SeekableWriteStream, public Common::NonCopyable {
public:
enum WriteMode {
WriteMode_Read = 0,
WriteMode_Write = 1,
WriteMode_WriteAtomic = 2,
};
protected:
/** File handle to the actual file. */
void *_handle;
Common::String *_path;
static StdioStream *makeFromPathHelper(const Common::String &path, WriteMode writeMode,
StdioStream *(*factory)(void *handle));
public:
/**
* Given a path, invokes fopen on that path and wrap the result in a
* StdioStream instance.
*/
static StdioStream *makeFromPath(const Common::String &path, WriteMode writeMode) {
return makeFromPathHelper(path, writeMode, [](void *handle) {
return new StdioStream(handle);
});
}
StdioStream(void *handle);
~StdioStream() override;
bool err() const override;
void clearErr() override;
bool eos() const override;
uint32 write(const void *dataPtr, uint32 dataSize) override;
bool flush() override;
int64 pos() const override;
int64 size() const override;
bool seek(int64 offs, int whence = SEEK_SET) override;
uint32 read(void *dataPtr, uint32 dataSize) override;
/**
* Configure buffered IO
*
* Must be called immediately after opening the file.
* A buffer size of 0 disables buffering.
*
* @param bufferSize the size of the Stdio read / write buffer
* @return success or failure
*/
bool setBufferSize(uint32 bufferSize);
private:
/**
* Move the file from src to dst.
* This must succeed even if the destination file already exists.
*
* This function cannot be overridden as it's called from the destructor.
*
* @param src The file to move
* @param dst The path where the file is to be moved.
*
* @returns Whether the renaming succeeded or not.
*/
bool moveFile(const Common::String &src, const Common::String &dst);
};
#endif

View File

@@ -0,0 +1,231 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__WII__)
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#define FORBIDDEN_SYMBOL_EXCEPTION_getcwd
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#include <unistd.h>
#include "backends/fs/wii/wii-fs-factory.h"
#include "backends/fs/wii/wii-fs.h"
#ifdef USE_WII_DI
#include <di/di.h>
#include <iso9660.h>
#ifdef GAMECUBE
#include <ogc/dvd.h>
#endif
#endif
#ifdef USE_WII_SMB
#include <network.h>
#include <smb.h>
#endif
namespace Common {
DECLARE_SINGLETON(WiiFilesystemFactory);
}
WiiFilesystemFactory::WiiFilesystemFactory() :
_dvdMounted(false),
_smbMounted(false),
_dvdError(false),
_smbError(false) {
}
AbstractFSNode *WiiFilesystemFactory::makeRootFileNode() const {
return new WiiFilesystemNode();
}
AbstractFSNode *WiiFilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
if (getcwd(buf, MAXPATHLEN))
return new WiiFilesystemNode(buf);
else
return new WiiFilesystemNode();
}
AbstractFSNode *WiiFilesystemFactory::makeFileNodePath(const Common::String &path) const {
return new WiiFilesystemNode(path);
}
void WiiFilesystemFactory::asyncInit() {
#ifdef USE_WII_SMB
asyncInitNetwork();
#endif
}
void WiiFilesystemFactory::asyncDeinit() {
#ifdef USE_WII_DI
umount(kDVD);
#ifndef GAMECUBE
DI_Close();
#endif
#endif
#ifdef USE_WII_SMB
umount(kSMB);
net_deinit();
#endif
}
#ifdef USE_WII_SMB
void WiiFilesystemFactory::asyncInitNetwork() {
net_init_async(NULL, NULL);
}
void WiiFilesystemFactory::setSMBLoginData(const String &server,
const String &share,
const String &username,
const String &password) {
_smbServer = server;
_smbShare = share;
_smbUsername = username;
_smbPassword = password;
}
#endif
bool WiiFilesystemFactory::isMounted(FileSystemType type) {
switch (type) {
case kDVD:
return _dvdMounted;
case kSMB:
return _smbMounted;
}
return false;
}
bool WiiFilesystemFactory::failedToMount(FileSystemType type) {
switch (type) {
case kDVD:
return _dvdError;
case kSMB:
return _smbError;
}
return false;
}
#ifdef USE_WII_DI
#ifndef GAMECUBE
const DISC_INTERFACE* dvd = &__io_wiidvd;
#else
const DISC_INTERFACE* dvd = &__io_gcdvd;
#endif
#endif
void WiiFilesystemFactory::mount(FileSystemType type) {
switch (type) {
case kDVD:
#ifdef USE_WII_DI
if (_dvdMounted)
break;
printf("mount dvd\n");
if (ISO9660_Mount("dvd", dvd)) {
_dvdMounted = true;
_dvdError = false;
printf("ISO9660 mounted\n");
} else {
_dvdError = true;
printf("ISO9660 mount failed\n");
}
#endif
break;
case kSMB:
#ifdef USE_WII_SMB
if (_smbMounted)
break;
printf("mount smb\n");
if (net_get_status()) {
printf("network not inited\n");
break;
}
if (smbInit(_smbUsername.c_str(), _smbPassword.c_str(),
_smbShare.c_str(), _smbServer.c_str())) {
_smbMounted = true;
_smbError = false;
printf("smb mounted\n");
} else {
_smbError = true;
printf("error mounting smb\n");
}
#endif
break;
}
}
void WiiFilesystemFactory::umount(FileSystemType type) {
switch (type) {
case kDVD:
#ifdef USE_WII_DI
if (!_dvdMounted)
break;
printf("umount dvd\n");
ISO9660_Unmount("dvd:");
_dvdMounted = false;
_dvdError = false;
#endif
break;
case kSMB:
#ifdef USE_WII_SMB
if (!_smbMounted)
break;
printf("umount smb\n");
smbClose("smb:");
_smbMounted = false;
_smbError = false;
#endif
break;
}
}
void WiiFilesystemFactory::mountByPath(const String &path) {
if (path.hasPrefix("dvd:/"))
mount(kDVD);
else if (path.hasPrefix("smb:/"))
mount(kSMB);
}
void WiiFilesystemFactory::umountUnused(const String &path) {
if (!path.hasPrefix("dvd:/"))
umount(kDVD);
if (!path.hasPrefix("smb:/"))
umount(kSMB);
}
#endif

View File

@@ -0,0 +1,86 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _WII_FILESYSTEM_FACTORY_H_
#define _WII_FILESYSTEM_FACTORY_H_
#include "common/str.h"
#include "common/singleton.h"
#include "backends/fs/fs-factory.h"
#include <gctypes.h>
/**
* Creates WiiFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class WiiFilesystemFactory final : public FilesystemFactory, public Common::Singleton<WiiFilesystemFactory> {
public:
typedef Common::String String;
enum FileSystemType {
kDVD,
kSMB
};
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
void asyncInit();
void asyncDeinit();
#ifdef USE_WII_SMB
void asyncInitNetwork();
void setSMBLoginData(const String &server, const String &share,
const String &username, const String &password);
#endif
bool isMounted(FileSystemType type);
bool failedToMount(FileSystemType type);
void mount(FileSystemType type);
void umount(FileSystemType type);
void mountByPath(const String &path);
void umountUnused(const String &path);
protected:
WiiFilesystemFactory();
private:
friend class Common::Singleton<SingletonBaseType>;
bool _dvdMounted;
bool _smbMounted;
bool _dvdError;
bool _smbError;
#ifdef USE_WII_SMB
String _smbServer;
String _smbShare;
String _smbUsername;
String _smbPassword;
#endif
};
#endif /*Wii_FILESYSTEM_FACTORY_H*/

236
backends/fs/wii/wii-fs.cpp Normal file
View File

@@ -0,0 +1,236 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(__WII__)
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
#include "backends/fs/wii/wii-fs.h"
#include "backends/fs/wii/wii-fs-factory.h"
#include "backends/fs/stdiostream.h"
#include <sys/iosupport.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <gctypes.h>
// gets all registered devoptab devices
bool WiiFilesystemNode::getDevopChildren(AbstractFSList &list, ListMode mode, bool hidden) const {
u8 i;
const devoptab_t* dt;
if (mode == Common::FSNode::kListFilesOnly)
return true;
// skip in, out and err
for (i = 3; i < STD_MAX; ++i) {
dt = devoptab_list[i];
if (!dt || !dt->name || !dt->open_r || !dt->diropen_r)
continue;
list.push_back(new WiiFilesystemNode(Common::String(dt->name) + ":/"));
}
return true;
}
void WiiFilesystemNode::initRootNode() {
_path.clear();
_displayName = "<devices>";
_exists = true;
_isDirectory = true;
_isReadable = false;
_isWritable = false;
}
void WiiFilesystemNode::clearFlags() {
_exists = false;
_isDirectory = false;
_isReadable = false;
_isWritable = false;
}
void WiiFilesystemNode::setFlags(const struct stat *st) {
_exists = true;
_isDirectory = ( (st->st_mode & S_IFDIR) != 0 );
_isReadable = ( (st->st_mode & S_IRUSR) != 0 );
_isWritable = ( (st->st_mode & S_IWUSR) != 0 );
}
WiiFilesystemNode::WiiFilesystemNode() {
initRootNode();
}
WiiFilesystemNode::WiiFilesystemNode(const Common::String &p) {
if (p.empty()) {
initRootNode();
return;
}
_path = Common::normalizePath(p, '/');
WiiFilesystemFactory::instance().mountByPath(_path);
// "sd:" is not a valid directory, but "sd:/" is
if (_path.lastChar() == ':')
_path += '/';
_displayName = lastPathComponent(_path, '/');
struct stat st;
if(stat(_path.c_str(), &st) != -1)
setFlags(&st);
else
clearFlags();
}
WiiFilesystemNode::WiiFilesystemNode(const Common::String &p, const struct stat *st) {
if (p.empty()) {
initRootNode();
return;
}
_path = Common::normalizePath(p, '/');
// "sd:" is not a valid directory, but "sd:/" is
if (_path.lastChar() == ':')
_path += '/';
_displayName = lastPathComponent(_path, '/');
setFlags(st);
}
bool WiiFilesystemNode::exists() const {
return _exists;
}
AbstractFSNode *WiiFilesystemNode::getChild(const Common::String &n) const {
assert(_isDirectory);
assert(!n.contains('/'));
Common::String newPath(_path);
if (newPath.lastChar() != '/')
newPath += '/';
newPath += n;
return new WiiFilesystemNode(newPath);
}
bool WiiFilesystemNode::getChildren(AbstractFSList &list, ListMode mode, bool hidden) const {
assert(_isDirectory);
if (_path.empty())
return getDevopChildren(list, mode, hidden);
DIR *dp = opendir (_path.c_str());
if (dp == NULL)
return false;
struct dirent *pent;
while ((pent = readdir(dp)) != NULL) {
if (strcmp(pent->d_name, ".") == 0 || strcmp(pent->d_name, "..") == 0)
continue;
Common::String newPath(_path);
if (newPath.lastChar() != '/')
newPath += '/';
newPath += pent->d_name;
bool isDir = ( pent->d_type == DT_DIR );
if ((mode == Common::FSNode::kListFilesOnly && isDir) ||
(mode == Common::FSNode::kListDirectoriesOnly && !isDir))
continue;
struct stat st;
st.st_mode = 0;
st.st_mode |= ( isDir ? S_IFDIR : 0 );
st.st_mode |= S_IRUSR;
st.st_mode |= S_IWUSR;
list.push_back(new WiiFilesystemNode(newPath, &st));
}
closedir(dp);
return true;
}
AbstractFSNode *WiiFilesystemNode::getParent() const {
if (_path.empty())
return NULL;
const char *start = _path.c_str();
const char *end = lastPathComponent(_path, '/');
return new WiiFilesystemNode(Common::String(start, end - start));
}
Common::SeekableReadStream *WiiFilesystemNode::createReadStream() {
StdioStream *readStream = StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
// disable newlib's buffering, the device libraries handle caching
if (readStream) {
readStream->setBufferSize(0);
}
return readStream;
}
Common::SeekableWriteStream *WiiFilesystemNode::createWriteStream(bool atomic) {
StdioStream *writeStream = StdioStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
// disable newlib's buffering, the device libraries handle caching
if (writeStream) {
writeStream->setBufferSize(0);
}
return writeStream;
}
bool WiiFilesystemNode::createDirectory() {
if(!_exists) {
if (mkdir(_path.c_str(), 0755) == 0) {
_exists = true;
_isDirectory = true;
_isReadable = true;
_isWritable = true;
}
}
return _exists && _isDirectory;
}
#endif //#if defined(__WII__)

74
backends/fs/wii/wii-fs.h Normal file
View File

@@ -0,0 +1,74 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef WII_FILESYSTEM_H
#define WII_FILESYSTEM_H
#include "backends/fs/abstract-fs.h"
/**
* Implementation of the ScummVM file system API based on Wii.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class WiiFilesystemNode final : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _path;
bool _exists, _isDirectory, _isReadable, _isWritable;
virtual void initRootNode();
virtual bool getDevopChildren(AbstractFSList &list, ListMode mode, bool hidden) const;
virtual void setFlags(const struct stat *st);
virtual void clearFlags();
public:
/**
* Creates a WiiFilesystemNode with the root node as path.
*/
WiiFilesystemNode();
/**
* Creates a WiiFilesystemNode for a given path.
*
* @param path Common::String with the path the new node should point to.
*/
WiiFilesystemNode(const Common::String &p);
WiiFilesystemNode(const Common::String &p, const struct stat *st);
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
bool isDirectory() const override { return _isDirectory; }
bool isReadable() const override { return _isReadable; }
bool isWritable() const override { return _isWritable; }
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
};
#endif

View File

@@ -0,0 +1,41 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(WIN32)
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/windows/windows-fs.h"
#include "backends/fs/windows/windows-fs-factory.h"
AbstractFSNode *WindowsFilesystemFactory::makeRootFileNode() const {
return new WindowsFilesystemNode();
}
AbstractFSNode *WindowsFilesystemFactory::makeCurrentDirectoryFileNode() const {
return new WindowsFilesystemNode("", true);
}
AbstractFSNode *WindowsFilesystemFactory::makeFileNodePath(const Common::String &path) const {
return new WindowsFilesystemNode(path, false);
}
#endif

View File

@@ -0,0 +1,39 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef WINDOWS_FILESYSTEM_FACTORY_H
#define WINDOWS_FILESYSTEM_FACTORY_H
#include "backends/fs/fs-factory.h"
/**
* Creates WindowsFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
*/
class WindowsFilesystemFactory final : public FilesystemFactory {
public:
AbstractFSNode *makeRootFileNode() const override;
AbstractFSNode *makeCurrentDirectoryFileNode() const override;
AbstractFSNode *makeFileNodePath(const Common::String &path) const override;
};
#endif /*WINDOWS_FILESYSTEM_FACTORY_H*/

View File

@@ -0,0 +1,241 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(WIN32)
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/fs/windows/windows-fs.h"
#include "backends/fs/stdiostream.h"
bool WindowsFilesystemNode::exists() const {
// Check whether the file actually exists
return (GetFileAttributes(charToTchar(_path.c_str())) != INVALID_FILE_ATTRIBUTES);
}
bool WindowsFilesystemNode::isReadable() const {
// Since all files are always readable and it is not possible to give
// write-only permission, this is equivalent to ::exists().
return (GetFileAttributes(charToTchar(_path.c_str())) != INVALID_FILE_ATTRIBUTES);
}
bool WindowsFilesystemNode::isWritable() const {
// Check whether the file exists and it can be written.
DWORD fileAttribs = GetFileAttributes(charToTchar(_path.c_str()));
return ((fileAttribs != INVALID_FILE_ATTRIBUTES) && (!(fileAttribs & FILE_ATTRIBUTE_READONLY)));
}
void WindowsFilesystemNode::addFile(AbstractFSList &list, ListMode mode, const char *base, bool hidden, WIN32_FIND_DATA* find_data) {
// Skip local directory (.) and parent (..)
if (!_tcscmp(find_data->cFileName, TEXT(".")) ||
!_tcscmp(find_data->cFileName, TEXT("..")))
return;
// Skip hidden files if asked
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && !hidden)
return;
bool isDirectory = ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false);
if ((!isDirectory && mode == Common::FSNode::kListDirectoriesOnly) ||
(isDirectory && mode == Common::FSNode::kListFilesOnly))
return;
const char *fileName = tcharToChar(find_data->cFileName);
WindowsFilesystemNode entry;
entry._isDirectory = isDirectory;
entry._displayName = fileName;
entry._path = base;
entry._path += fileName;
if (entry._isDirectory)
entry._path += "\\";
entry._isValid = true;
entry._isPseudoRoot = false;
list.push_back(new WindowsFilesystemNode(entry));
}
const char* WindowsFilesystemNode::tcharToChar(const TCHAR *str) {
#ifndef UNICODE
return str;
#else
static char multiByteString[MAX_PATH];
WideCharToMultiByte(CP_UTF8, 0, str, _tcslen(str) + 1, multiByteString, MAX_PATH, nullptr, nullptr);
return multiByteString;
#endif
}
const TCHAR* WindowsFilesystemNode::charToTchar(const char *str) {
#ifndef UNICODE
return str;
#else
static wchar_t wideCharString[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, str, strlen(str) + 1, wideCharString, MAX_PATH);
return wideCharString;
#endif
}
WindowsFilesystemNode::WindowsFilesystemNode() {
// Create a virtual root directory for standard Windows system
_isDirectory = true;
_isValid = false;
_path = "";
_isPseudoRoot = true;
}
WindowsFilesystemNode::WindowsFilesystemNode(const Common::String &p, const bool currentDir) {
if (currentDir) {
TCHAR path[MAX_PATH];
GetCurrentDirectory(MAX_PATH, path);
_path = tcharToChar(path);
} else {
assert(p.size() > 0);
_path = p;
}
_displayName = lastPathComponent(_path, '\\');
setFlags();
_isPseudoRoot = false;
}
void WindowsFilesystemNode::setFlags() {
// Check whether it is a directory, and whether the file actually exists
DWORD fileAttribs = GetFileAttributes(charToTchar(_path.c_str()));
if (fileAttribs == INVALID_FILE_ATTRIBUTES) {
_isDirectory = false;
_isValid = false;
} else {
_isDirectory = ((fileAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0);
_isValid = true;
// Add a trailing slash, if necessary.
if (_isDirectory && _path.lastChar() != '\\') {
_path += '\\';
}
}
}
AbstractFSNode *WindowsFilesystemNode::getChild(const Common::String &n) const {
assert(_isDirectory);
// Make sure the string contains no slashes
assert(!n.contains('/'));
Common::String newPath(_path);
if (_path.lastChar() != '\\')
newPath += '\\';
newPath += n;
return new WindowsFilesystemNode(newPath, false);
}
bool WindowsFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
assert(_isDirectory);
if (_isPseudoRoot) {
// Drives enumeration
TCHAR drive_buffer[100];
GetLogicalDriveStrings(sizeof(drive_buffer) / sizeof(TCHAR), drive_buffer);
for (TCHAR *current_drive = drive_buffer; *current_drive;
current_drive += _tcslen(current_drive) + 1) {
WindowsFilesystemNode entry;
char drive_name[2];
drive_name[0] = tcharToChar(current_drive)[0];
drive_name[1] = '\0';
entry._displayName = drive_name;
entry._isDirectory = true;
entry._isValid = true;
entry._isPseudoRoot = false;
entry._path = tcharToChar(current_drive);
myList.push_back(new WindowsFilesystemNode(entry));
}
} else {
// Files enumeration
WIN32_FIND_DATA desc;
HANDLE handle;
char searchPath[MAX_PATH + 10];
Common::sprintf_s(searchPath, "%s*", _path.c_str());
handle = FindFirstFile(charToTchar(searchPath), &desc);
if (handle == INVALID_HANDLE_VALUE)
return false;
addFile(myList, mode, _path.c_str(), hidden, &desc);
while (FindNextFile(handle, &desc))
addFile(myList, mode, _path.c_str(), hidden, &desc);
FindClose(handle);
}
return true;
}
AbstractFSNode *WindowsFilesystemNode::getParent() const {
assert(_isValid || _isPseudoRoot);
if (_isPseudoRoot)
return nullptr;
WindowsFilesystemNode *p;
if (_path.size() > 3) {
const char *start = _path.c_str();
const char *end = lastPathComponent(_path, '\\');
p = new WindowsFilesystemNode();
p->_path = Common::String(start, end - start);
p->_isValid = true;
p->_isDirectory = true;
p->_displayName = lastPathComponent(p->_path, '\\');
p->_isPseudoRoot = false;
} else {
// pseudo root
p = new WindowsFilesystemNode();
}
return p;
}
Common::SeekableReadStream *WindowsFilesystemNode::createReadStream() {
return StdioStream::makeFromPath(getPath(), StdioStream::WriteMode_Read);
}
Common::SeekableWriteStream *WindowsFilesystemNode::createWriteStream(bool atomic) {
return StdioStream::makeFromPath(getPath(), atomic ?
StdioStream::WriteMode_WriteAtomic : StdioStream::WriteMode_Write);
}
bool WindowsFilesystemNode::createDirectory() {
if (CreateDirectory(charToTchar(_path.c_str()), nullptr) != 0)
setFlags();
return _isValid && _isDirectory;
}
#endif //#ifdef WIN32

View File

@@ -0,0 +1,125 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef WINDOWS_FILESYSTEM_H
#define WINDOWS_FILESYSTEM_H
#include <windows.h>
#include "backends/fs/abstract-fs.h"
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
/**
* Implementation of the ScummVM file system API based on Windows API.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class WindowsFilesystemNode final : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _path;
bool _isDirectory;
bool _isPseudoRoot;
bool _isValid;
public:
/**
* Creates a WindowsFilesystemNode with the root node as path.
*
* In regular windows systems, a virtual root path is used "".
*/
WindowsFilesystemNode();
/**
* Creates a WindowsFilesystemNode for a given path.
*
* Examples:
* path=c:\foo\bar.txt, currentDir=false -> c:\foo\bar.txt
* path=c:\foo\bar.txt, currentDir=true -> current directory
* path=NULL, currentDir=true -> current directory
*
* @param path Common::String with the path the new node should point to.
* @param currentDir if true, the path parameter will be ignored and the resulting node will point to the current directory.
*/
WindowsFilesystemNode(const Common::String &path, const bool currentDir);
bool exists() const override;
Common::U32String getDisplayName() const override { return _displayName; }
Common::String getName() const override { return _displayName; }
Common::String getPath() const override { return _path; }
bool isDirectory() const override { return _isDirectory; }
bool isReadable() const override;
bool isWritable() const override;
AbstractFSNode *getChild(const Common::String &n) const override;
bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
AbstractFSNode *getParent() const override;
Common::SeekableReadStream *createReadStream() override;
Common::SeekableWriteStream *createWriteStream(bool atomic) override;
bool createDirectory() override;
private:
/**
* Adds a single WindowsFilesystemNode to a given list.
* This method is used by getChildren() to populate the directory entries list.
*
* @param list List to put the file entry node in.
* @param mode Mode to use while adding the file entry to the list.
* @param base Common::String with the directory being listed.
* @param hidden true if hidden files should be added, false otherwise
* @param find_data Describes a file that the FindFirstFile, FindFirstFileEx, or FindNextFile functions find.
*/
static void addFile(AbstractFSList &list, ListMode mode, const char *base, bool hidden, WIN32_FIND_DATA* find_data);
/**
* Converts a string of TCHARs returned from a Windows API function to
* a character string. If UNICODE is defined then the incoming string
* is wide characters and is converted to UTF8, otherwise the incoming
* string is returned with no conversion.
*
* @param str String to convert if UNICODE is defined
* @return str in UTF8 format if UNICODE is defined, otherwise just str
*/
static const char *tcharToChar(const TCHAR *str);
/**
* Converts a character string to a string of TCHARs for passing
* to a Windows API function. If UNICODE is defined then the incoming
* string is converted from UTF8 to wide characters, otherwise the incoming
* string is returned with no conversion.
*
* @param str String to convert if UNICODE is defined
* @return str in wide character format if UNICODE is defined, otherwise just str
*/
static const TCHAR* charToTchar(const char *str);
/**
* Tests and sets the _isValid and _isDirectory flags, using the GetFileAttributes() function.
*/
virtual void setFlags();
};
#endif