/* This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast 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 Flycast. If not, see . */ #include "storage.h" #include struct retro_vfs_interface* hostfs::LibretroStorage::vfs_interface = nullptr; bool hostfs::LibretroStorage::isFileWriteable(const std::string& path) { // Not ideal; would be nicer if the VFS API gave us a direct way of checking this. RFILE* file = filestream_open(path.c_str(), RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING, RETRO_VFS_FILE_ACCESS_HINT_NONE); if (file == nullptr) return false; filestream_close(file); return true; } hostfs::FileInfo hostfs::LibretroStorage::makeFileInfo(std::string name, std::string path) { const bool is_directory = path_is_directory(path.c_str()); const size_t size = path_get_size(path.c_str()); const bool is_writeable = isFileWriteable(path); return {std::move(name), std::move(path), is_directory, size, is_writeable}; } void hostfs::LibretroStorage::initialise(retro_environment_t environ_cb) { retro_vfs_interface_info vfs_interface_info; vfs_interface_info.required_interface_version = 3; // We really want those directory functions. if (!environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_interface_info)) return; vfs_interface = vfs_interface_info.iface; filestream_vfs_init(&vfs_interface_info); path_vfs_init(&vfs_interface_info); } bool hostfs::LibretroStorage::isKnownPath(const std::string& path) { return filestream_exists(path.c_str()); } std::vector hostfs::LibretroStorage::listContent(const std::string& path) { std::vector list; struct retro_vfs_dir_handle* dir = vfs_interface->opendir(path.c_str(), true); if (dir == nullptr) return list; while (vfs_interface->readdir(dir)) { const auto entry_name = vfs_interface->dirent_get_name(dir); const auto entry_path = path + PATH_DEFAULT_SLASH_C() + entry_name; list.emplace_back(makeFileInfo(entry_name, entry_path)); } vfs_interface->closedir(dir); return list; } hostfs::File *hostfs::LibretroStorage::openFile(const std::string& path, const std::string& mode) { bool extended = false; bool append = false; unsigned int mode_bitfield = 0; for (char character : mode) { switch (character) { case 'R': case 'r': if (mode_bitfield != 0) return nullptr; mode_bitfield = RETRO_VFS_FILE_ACCESS_READ; break; case 'W': case 'w': if (mode_bitfield != 0) return nullptr; mode_bitfield = RETRO_VFS_FILE_ACCESS_WRITE; break; case 'A': case 'a': if (mode_bitfield != 0) return nullptr; mode_bitfield = RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING; append = true; break; case '+': extended = true; break; case 'B': case 'b': case 'T': case 't': // Binary mode unfortunately just gets assumed by default... // TODO: Do any of Flycast's text parsers require text mode? break; default: // Unrecognised character. return nullptr; } } if (mode_bitfield == 0) return nullptr; if (extended) mode_bitfield = RETRO_VFS_FILE_ACCESS_READ_WRITE; RFILE *rfile = filestream_open(path.c_str(), mode_bitfield, RETRO_VFS_FILE_ACCESS_HINT_NONE); if (rfile == nullptr) return nullptr; auto file = new LibretroFile(rfile); if (append) file->seek(0, SEEK_END); return file; } std::string hostfs::LibretroStorage::getParentPath(const std::string& path) { std::string parent_path = path; parent_path.resize(path_parent_dir(parent_path.data(), parent_path.size())); return parent_path; } std::string hostfs::LibretroStorage::getSubPath(const std::string& reference, const std::string& relative) { return reference + PATH_DEFAULT_SLASH_C() + relative; } hostfs::FileInfo hostfs::LibretroStorage::getFileInfo(const std::string& path) { return makeFileInfo(path_basename(path.c_str()), path); } bool hostfs::LibretroStorage::exists(const std::string& path) { return filestream_exists(path.c_str()); } bool hostfs::LibretroStorage::addStorage(bool isDirectory, bool writeAccess, const std::string& description, void (*callback)(bool cancelled, std::string selectedPath), const std::string& mimeType) { // TODO: Should this do anything? return false; } hostfs::CustomStorage& hostfs::customStorage() { static LibretroStorage storage; return storage; }