/* 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 . * */ #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 CloudFilesystemNode::_cloudFolders = Common::HashMap(); 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 storageFiles = response.value; AbstractFSList *dirList = new AbstractFSList(); for (Common::Array::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 *)this, &CloudFilesystemNode::directoryListedCallback), new Common::Callback((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(this, &CloudFilesystemNode::fileDownloadedCallback), new Common::Callback(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)