#pragma once /* * Copyright (C) 2005-2017 Team Kodi * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, 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 Kodi; see the file COPYING. If not, see * . * */ #include "AddonBase.h" #include #if !defined(_WIN32) #include #if !defined(__stat64) #define __stat64 stat64 #endif #endif #ifdef _WIN32 // windows #ifndef _SSIZE_T_DEFINED typedef intptr_t ssize_t; #define _SSIZE_T_DEFINED #endif // !_SSIZE_T_DEFINED #endif #ifndef S_ISDIR #define S_ISDIR(mode) ((((mode)) & 0170000) == (0040000)) #endif #ifndef S_ISLNK #define S_ISLNK(mode) ((((mode)) & 0170000) == (0120000)) #endif /* * For interface between add-on and kodi. * * This structure defines the addresses of functions stored inside Kodi which * are then available for the add-on to call * * All function pointers there are used by the C++ interface functions below. * You find the set of them on xbmc/addons/interfaces/General.cpp * * Note: For add-on development itself this is not needed */ extern "C" { struct VFSProperty { char* name; char* val; }; struct VFSDirEntry { char* label; //!< item label char* title; //!< item title char* path; //!< item path unsigned int num_props; //!< Number of properties attached to item VFSProperty* properties; //!< Properties time_t date_time; //!< file creation date & time bool folder; //!< Item is a folder uint64_t size; //!< Size of file represented by item }; typedef struct AddonToKodiFuncTable_kodi_filesystem { bool (*can_open_directory)(void* kodiBase, const char* url); bool (*create_directory)(void* kodiBase, const char* path); bool (*remove_directory)(void* kodiBase, const char* path); bool (*directory_exists)(void* kodiBase, const char* path); bool (*get_directory)(void* kodiBase, const char* path, const char* mask, VFSDirEntry** items, unsigned int* num_items); void (*free_directory)(void* kodiBase, VFSDirEntry* items, unsigned int num_items); bool (*file_exists)(void* kodiBase, const char *filename, bool useCache); int (*stat_file)(void* kodiBase, const char *filename, struct __stat64* buffer); bool (*delete_file)(void* kodiBase, const char *filename); bool (*rename_file)(void* kodiBase, const char *filename, const char *newFileName); bool (*copy_file)(void* kodiBase, const char *filename, const char *dest); char* (*get_file_md5)(void* kodiBase, const char* filename); char* (*get_cache_thumb_name)(void* kodiBase, const char* filename); char* (*make_legal_filename)(void* kodiBase, const char* filename); char* (*make_legal_path)(void* kodiBase, const char* path); char* (*translate_special_protocol)(void* kodiBase, const char *strSource); void* (*open_file)(void* kodiBase, const char* filename, unsigned int flags); void* (*open_file_for_write)(void* kodiBase, const char* filename, bool overwrite); ssize_t (*read_file)(void* kodiBase, void* file, void* ptr, size_t size); bool (*read_file_string)(void* kodiBase, void* file, char *szLine, int iLineLength); ssize_t (*write_file)(void* kodiBase, void* file, const void* ptr, size_t size); void (*flush_file)(void* kodiBase, void* file); int64_t (*seek_file)(void* kodiBase, void* file, int64_t position, int whence); int (*truncate_file)(void* kodiBase, void* file, int64_t size); int64_t (*get_file_position)(void* kodiBase, void* file); int64_t (*get_file_length)(void* kodiBase, void* file); double (*get_file_download_speed)(void* kodiBase, void* file); void (*close_file)(void* kodiBase, void* file); int (*get_file_chunk_size)(void* kodiBase, void* file); void* (*curl_create)(void* kodiBase, const char* url); bool (*curl_add_option)(void* kodiBase, void* file, int type, const char* name, const char* value); bool (*curl_open)(void* kodiBase, void* file, unsigned int flags); } AddonToKodiFuncTable_kodi_filesystem; } /* extern "C" */ //============================================================================== /// \defgroup cpp_kodi_vfs_CFile_Defs Definitions, structures and enumerators /// \ingroup cpp_kodi_vfs_CFile /// @brief **Virtual file Server definition values** //------------------------------------------------------------------------------ //============================================================================== /// /// @ingroup cpp_kodi_vfs_CFile_Defs /// Flags to define way how file becomes opened with kodi::vfs::CFile::OpenFile() /// /// The values can be used together, e.g. `file.Open("myfile", READ_TRUNCATED | READ_CHUNKED);` /// typedef enum OpenFileFlags { /// indicate that caller can handle truncated reads, where function returns /// before entire buffer has been filled READ_TRUNCATED = 0x01, /// indicate that that caller support read in the minimum defined chunk size, /// this disables internal cache then READ_CHUNKED = 0x02, /// use cache to access this file READ_CACHED = 0x04, /// open without caching. regardless to file type READ_NO_CACHE = 0x08, /// calcuate bitrate for file while reading READ_BITRATE = 0x10, /// indicate to the caller we will seek between multiple streams in the file /// frequently READ_MULTI_STREAM = 0x20, /// indicate to the caller file is audio and/or video (and e.g. may grow) READ_AUDIO_VIDEO = 0x40, /// indicate that caller will do write operations before reading READ_AFTER_WRITE = 0x80, /// indicate that caller want to reopen a file if its already open READ_REOPEN = 0x100 } OpenFileFlags; //------------------------------------------------------------------------------ //============================================================================== /// \ingroup cpp_kodi_vfs_CFile_Defs /// @brief Used CURL message types /// typedef enum CURLOptiontype { /// Set a general option ADDON_CURL_OPTION_OPTION, /// Set a protocol option ADDON_CURL_OPTION_PROTOCOL, /// Set User and password ADDON_CURL_OPTION_CREDENTIALS, /// Add a Header ADDON_CURL_OPTION_HEADER } CURLOptiontype; //------------------------------------------------------------------------------ //============================================================================ /// /// \ingroup cpp_kodi_vfs_CFile_Defs /// @brief Information about a file /// struct STAT_STRUCTURE { /// ID of device containing file uint32_t deviceId; /// Total size, in bytes uint64_t size; #ifdef TARGET_WINDOWS /// Time of last access __time64_t accessTime; /// Time of last modification __time64_t modificationTime; /// Time of last status change __time64_t statusTime; #else /// Time of last access timespec accessTime; /// Time of last modification timespec modificationTime; /// Time of last status change timespec statusTime; #endif /// The stat url is a directory bool isDirectory; /// The stat url is a symbolic link bool isSymLink; }; //------------------------------------------------------------------------------ namespace kodi { namespace vfs { //============================================================================ /// /// \defgroup cpp_kodi_vfs_CDirEntry class CDirEntry /// \ingroup cpp_kodi_vfs /// /// @brief **Virtual file server directory entry** /// /// This class is used as an entry for files and folders in /// kodi::vfs::GetDirectory(). /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// /// ... /// /// std::vector items; /// kodi::vfs::GetDirectory("special://temp", "", items); /// /// fprintf(stderr, "Directory have %lu entries\n", items.size()); /// for (unsigned long i = 0; i < items.size(); i++) /// { /// char buff[20]; /// time_t now = items[i].DateTime(); /// strftime(buff, 20, "%Y-%m-%d %H:%M:%S", gmtime(&now)); /// fprintf(stderr, " - %04lu -- Folder: %s -- Name: %s -- Path: %s -- Time: %s\n", /// i+1, /// items[i].IsFolder() ? "yes" : "no ", /// items[i].Label().c_str(), /// items[i].Path().c_str(), /// buff); /// } /// ~~~~~~~~~~~~~ /// /// It has the header \ref Filesystem.h "#include " be included /// to enjoy it. /// //@{ class CDirEntry { public: //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Constructor for VFS directory entry /// /// @param[in] label [opt] Name to use for entry /// @param[in] path [opt] Used path of the entry /// @param[in] folder [opt] If set entry used as folder /// @param[in] size [opt] If used as file, his size defined there /// CDirEntry(const std::string& label = "", const std::string& path = "", bool folder = false, int64_t size = -1) : m_label(label), m_path(path), m_folder(folder), m_size(size) { } //---------------------------------------------------------------------------- //============================================================================ // @note Not for addon development itself needed, thats why below is // disabled for doxygen! // // @ingroup cpp_kodi_vfs_CDirEntry // @brief Constructor to create own copy // // @param[in] dirEntry pointer to own class type // CDirEntry(const VFSDirEntry& dirEntry) : m_label(dirEntry.label ? dirEntry.label : ""), m_path(dirEntry.path ? dirEntry.path : ""), m_folder(dirEntry.folder), m_size(dirEntry.size), m_dateTime(dirEntry.date_time) { } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Get the directory entry name /// /// @return Name of the entry /// const std::string& Label(void) const { return m_label; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Get the optional title of entry /// /// @return Title of the entry, if exists /// const std::string& Title(void) const { return m_title; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Get the path of the entry /// /// @return File system path of the entry /// const std::string& Path(void) const { return m_path; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Used to check entry is folder /// /// @return true if entry is a folder /// bool IsFolder(void) const { return m_folder; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief If file, the size of the file /// /// @return Defined file size /// int64_t Size(void) const { return m_size; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Get file time and date for a new entry /// /// @return The with time_t defined date and time of file /// time_t DateTime() { return m_dateTime; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Set the label name /// /// @param[in] label name of entry /// void SetLabel(const std::string& label) { m_label = label; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Set the title name /// /// @param[in] title title name of entry /// void SetTitle(const std::string& title) { m_title = title; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Set the path of the entry /// /// @param[in] path path of entry /// void SetPath(const std::string& path) { m_path = path; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Set the entry defined as folder /// /// @param[in] folder If true becomes entry defined as folder /// void SetFolder(bool folder) { m_folder = folder; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Set a file size for a new entry /// /// @param[in] size Size to set for dir entry /// void SetSize(int64_t size) { m_size = size; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Set file time and date for a new entry /// /// @param[in] dateTime The with time_t defined date and time of file /// void SetDateTime(time_t dateTime) { m_dateTime = dateTime; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Add a by string defined property entry to directory entry /// /// @note A property can be used to add some special information about a file /// or directory entry, this can be used on other places to do the right work /// of them. /// /// @param[in] id Identification name of property /// @param[in] value The property value to add by given id /// void AddProperty(const std::string& id, const std::string& value) { m_properties[id] = value; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Clear all present properties /// void ClearProperties() { m_properties.clear(); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs_CDirEntry /// @brief Get the present properties list on directory entry /// /// @return map with all present properties /// const std::map& GetProperties() const { return m_properties; } //---------------------------------------------------------------------------- private: std::string m_label; std::string m_title; std::string m_path; std::map m_properties; bool m_folder; int64_t m_size; time_t m_dateTime; }; //@} //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Make a directory /// /// The kodi::vfs::CreateDirectory() function shall create a /// new directory with name path. /// /// The newly created directory shall be an empty directory. /// /// @param[in] path Path to the directory. /// @return Upon successful completion, CreateDirectory() shall return true. /// Otherwise false shall be returned, no directory shall be created. /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string directory = "C:\\my_dir"; /// bool ret = kodi::vfs::CreateDirectory(directory); /// fprintf(stderr, "Directory '%s' successfull created: %s\n", directory.c_str(), ret ? "yes" : "no"); /// ... /// ~~~~~~~~~~~~~ /// inline bool CreateDirectory(const std::string& path) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->create_directory(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, path.c_str()); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Verifying the Existence of a Directory /// /// The kodi::vfs::DirectoryExists() method determines whether /// a specified folder exists. /// /// @param[in] path Path to the directory. /// @return True when it exists, false otherwise. /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string directory = "C:\\my_dir"; /// bool ret = kodi::vfs::DirectoryExists(directory); /// fprintf(stderr, "Directory '%s' present: %s\n", directory.c_str(), ret ? "yes" : "no"); /// ... /// ~~~~~~~~~~~~~ /// inline bool DirectoryExists(const std::string& path) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->directory_exists(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, path.c_str()); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Removes a directory. /// /// The kodi::vfs::RemoveDirectory() function shall remove a /// directory whose name is given by path. /// /// @param[in] path Path to the directory. /// @return Upon successful completion, the function RemoveDirectory() shall /// return true. Otherwise, false shall be returned, and errno set /// to indicate the error. If false is returned, the named directory /// shall not be changed. /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// bool ret = kodi::vfs::RemoveDirectory("C:\\my_dir"); /// ... /// ~~~~~~~~~~~~~ /// inline bool RemoveDirectory(const std::string& path) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->remove_directory(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, path.c_str()); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Lists a directory. /// /// Return the list of files and directories which have been found in the /// specified directory and which respect the given constraint. /// /// It can handle the normal OS dependent paths and also the special virtual /// filesystem from Kodi what starts with \b special://. /// /// @param[in] path The path in which the files and directories are located. /// @param[in] mask Mask to filter out requested files, e.g. "*.avi|*.mpg" to /// files with this ending. /// @param[out] items The returned list directory entries. /// @return True if listing was successful, false otherwise. /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// /// std::vector items; /// kodi::vfs::GetDirectory("special://temp", "", items); /// /// fprintf(stderr, "Directory have %lu entries\n", items.size()); /// for (unsigned long i = 0; i < items.size(); i++) /// { /// fprintf(stderr, " - %04lu -- Folder: %s -- Name: %s -- Path: %s\n", /// i+1, /// items[i].IsFolder() ? "yes" : "no ", /// items[i].Label().c_str(), /// items[i].Path().c_str()); /// } /// ~~~~~~~~~~~~~ inline bool GetDirectory(const std::string& path, const std::string& mask, std::vector& items) { VFSDirEntry* dir_list = nullptr; unsigned int num_items = 0; if (::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_directory(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, path.c_str(), mask.c_str(), &dir_list, &num_items)) { if (dir_list) { for (unsigned int i = 0; i < num_items; ++i) items.push_back(CDirEntry(dir_list[i])); ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->free_directory(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, dir_list, num_items); } return true; } return false; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Retrieve MD5sum of a file /// /// @param[in] path path to the file to MD5sum /// @return md5 sum of the file /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// #include /// ... /// std::string md5; /// std::string filename; /// if (kodi::gui::DialogFileBrowser::ShowAndGetFile("local", "*.avi|*.mpg|*.mp4", /// "Test File selection to get MD5", /// filename)) /// { /// md5 = kodi::vfs::GetFileMD5(filename); /// fprintf(stderr, "MD5 of file '%s' is %s\n", md5.c_str(), filename.c_str()); /// } /// ~~~~~~~~~~~~~ /// inline std::string GetFileMD5(const std::string& path) { std::string strReturn; char* strMd5 = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_md5(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, path.c_str()); if (strMd5 != nullptr) { if (std::strlen(strMd5)) strReturn = strMd5; ::kodi::addon::CAddonBase::m_interface->toKodi->free_string(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, strMd5); } return strReturn; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Returns a thumb cache filename /// /// @param[in] filename path to file /// @return cache filename /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// #include /// ... /// std::string thumb; /// std::string filename; /// if (kodi::gui::DialogFileBrowser::ShowAndGetFile("local", "*.avi|*.mpg|*.mp4", /// "Test File selection to get Thumnail", /// filename)) /// { /// thumb = kodi::vfs::GetCacheThumbName(filename); /// fprintf(stderr, "Thumb name of file '%s' is %s\n", thumb.c_str(), filename.c_str()); /// } /// ~~~~~~~~~~~~~ /// inline std::string GetCacheThumbName(const std::string& filename) { std::string strReturn; char* strThumbName = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_cache_thumb_name(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str()); if (strThumbName != nullptr) { if (std::strlen(strThumbName)) strReturn = strThumbName; ::kodi::addon::CAddonBase::m_interface->toKodi->free_string(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, strThumbName); } return strReturn; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Make filename valid /// /// Function to replace not valid characters with '_'. It can be also /// compared with original before in a own loop until it is equal /// (no invalid characters). /// /// @param[in] filename Filename to check and fix /// @return The legal filename /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string fileName = "///\\jk???lj????.mpg"; /// std::string legalName = kodi::vfs::MakeLegalFileName(fileName); /// fprintf(stderr, "Legal name of '%s' is '%s'\n", fileName.c_str(), legalName.c_str()); /// /// /* Returns as legal: 'jk___lj____.mpg' */ /// ~~~~~~~~~~~~~ /// inline std::string MakeLegalFileName(const std::string& filename) { std::string strReturn; char* strLegalFileName = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->make_legal_filename(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str()); if (strLegalFileName != nullptr) { if (std::strlen(strLegalFileName)) strReturn = strLegalFileName; ::kodi::addon::CAddonBase::m_interface->toKodi->free_string(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, strLegalFileName); } return strReturn; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Make directory name valid /// /// Function to replace not valid characters with '_'. It can be also /// compared with original before in a own loop until it is equal /// (no invalid characters). /// /// @param[in] path Directory name to check and fix /// @return The legal directory name /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string path = "///\\jk???lj????\\hgjkg"; /// std::string legalPath = kodi::vfs::MakeLegalPath(path); /// fprintf(stderr, "Legal name of '%s' is '%s'\n", path.c_str(), legalPath.c_str()); /// /// /* Returns as legal: '/jk___lj____/hgjkg' */ /// ~~~~~~~~~~~~~ /// inline std::string MakeLegalPath(const std::string& path) { std::string strReturn; char* strLegalPath = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->make_legal_path(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, path.c_str()); if (strLegalPath != nullptr) { if (std::strlen(strLegalPath)) strReturn = strLegalPath; ::kodi::addon::CAddonBase::m_interface->toKodi->free_string(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, strLegalPath); } return strReturn; } //---------------------------------------------------------------------------- //============================================================================ /// /// \ingroup cpp_kodi /// @brief Returns the translated path /// /// @param[in] source string or unicode - Path to format /// @return A human-readable string suitable for logging /// /// @note Only useful if you are coding for both Linux and Windows. /// e.g. Converts 'special://masterprofile/script_data' -> '/home/user/.kodi/UserData/script_data' /// on Linux. /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string path = kodi::vfs::TranslateSpecialProtocol("special://masterprofile/script_data"); /// fprintf(stderr, "Translated path is: %s\n", path.c_str()); /// ... /// ~~~~~~~~~~~~~ /// or /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// fprintf(stderr, "Directory 'special://temp' is '%s'\n", kodi::vfs::TranslateSpecialProtocol("special://temp").c_str()); /// ... /// ~~~~~~~~~~~~~ /// inline std::string TranslateSpecialProtocol(const std::string& source) { std::string strReturn; char* protocol = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->translate_special_protocol(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, source.c_str()); if (protocol != nullptr) { if (std::strlen(protocol)) strReturn = protocol; ::kodi::addon::CAddonBase::m_interface->toKodi->free_string(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, protocol); } return strReturn; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Return the file name from given complate path string /// /// @param[in] path The complete path include file and directory /// @return Filename from path /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string fileName = kodi::vfs::GetFileName("special://temp/kodi.log"); /// fprintf(stderr, "File name is '%s'\n", fileName.c_str()); /// ~~~~~~~~~~~~~ /// inline std::string GetFileName(const std::string& path) { /* find the last slash */ const size_t slash = path.find_last_of("/\\"); return path.substr(slash+1); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Return the directory name from given complate path string /// /// @param[in] path The complete path include file and directory /// @return Directory name from path /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// std::string dirName = kodi::vfs::GetDirectoryName("special://temp/kodi.log"); /// fprintf(stderr, "Directory name is '%s'\n", dirName.c_str()); /// ~~~~~~~~~~~~~ /// inline std::string GetDirectoryName(const std::string& path) { // Will from a full filename return the directory the file resides in. // Keeps the final slash at end and possible |option=foo options. size_t iPosSlash = path.find_last_of("/\\"); if (iPosSlash == std::string::npos) return ""; // No slash, so no path (ignore any options) size_t iPosBar = path.rfind('|'); if (iPosBar == std::string::npos) return path.substr(0, iPosSlash + 1); // Only path return path.substr(0, iPosSlash + 1) + path.substr(iPosBar); // Path + options } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Return a size aligned to the chunk size at least as large as the chunk size. /// /// @param[in] chunk The chunk size /// @param[in] minimum The minimum size (or maybe the minimum number of chunks?) /// @return The aligned size /// inline unsigned int GetChunkSize(unsigned int chunk, unsigned int minimum) { if (chunk) return chunk * ((minimum + chunk - 1) / chunk); else return minimum; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Check if a file exists. /// /// @param[in] filename The filename to check. /// @param[in] usecache Check in file cache. /// @return true if the file exists false otherwise. /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// bool exists = kodi::vfs::FileExists("special://temp/kodi.log"); /// fprintf(stderr, "Log file should be always present, is it present? %s\n", exists ? "yes" : "no"); /// ~~~~~~~~~~~~~ /// static inline bool FileExists(const std::string& filename, bool usecache = false) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->file_exists(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), usecache); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Get file status. /// /// These function return information about a file. Execute (search) /// permission is required on all of the directories in path that /// lead to the file. /// /// The call return a stat structure, which contains the on \ref STAT_STRUCTURE /// defined values. /// /// @warning Not all of the OS file systems implement all of the time fields. /// /// @param[in] filename The filename to read the status from. /// @param[out] buffer The file status is written into this buffer. /// @return On success, tru is returned. On error, fale is returned /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// ... /// STAT_STRUCTURE statFile; /// int ret = kodi::vfs::StatFile("special://temp/kodi.log", &statFile); /// fprintf(stderr, "deviceId (ID of device containing file) = %u\n" /// "size (total size, in bytes) = %lu\n" /// "accessTime (time of last access) = %lu\n" /// "modificationTime (time of last modification) = %lu\n" /// "statusTime (time of last status change) = %lu\n" /// "isDirectory (The stat url is a directory) = %s\n" /// "isSymLink (The stat url is a symbolic link) = %s\n" /// "Return value = %i\n", /// statFile.deviceId, /// statFile.size, /// statFile.accessTime, /// statFile.modificationTime, /// statFile.statusTime, /// statFile.isDirectory ? "true" : "false", /// statFile.isSymLink ? "true" : "false", /// ret); /// ~~~~~~~~~~~~~ /// static inline bool StatFile(const std::string& filename, STAT_STRUCTURE& buffer) { struct __stat64 frontendBuffer = { }; if (::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->stat_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), &frontendBuffer)) { buffer.deviceId = frontendBuffer.st_dev; buffer.size = frontendBuffer.st_size; #if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) buffer.accessTime = frontendBuffer.st_atimespec; buffer.modificationTime = frontendBuffer.st_mtimespec; buffer.statusTime = frontendBuffer.st_ctimespec; #elif defined(TARGET_WINDOWS) buffer.accessTime = frontendBuffer.st_atime; buffer.modificationTime = frontendBuffer.st_mtime; buffer.statusTime = frontendBuffer.st_ctime; #elif defined(TARGET_ANDROID) buffer.accessTime.tv_sec = frontendBuffer.st_atime; buffer.accessTime.tv_nsec = frontendBuffer.st_atime_nsec; buffer.modificationTime.tv_sec = frontendBuffer.st_mtime; buffer.modificationTime.tv_nsec = frontendBuffer.st_mtime_nsec; buffer.statusTime.tv_sec = frontendBuffer.st_ctime; buffer.statusTime.tv_nsec = frontendBuffer.st_ctime_nsec; #else buffer.accessTime = frontendBuffer.st_atim; buffer.modificationTime = frontendBuffer.st_mtim; buffer.statusTime = frontendBuffer.st_ctim; #endif buffer.isDirectory = S_ISDIR(frontendBuffer.st_mode); buffer.isSymLink = S_ISLNK(frontendBuffer.st_mode); return true; } return false; } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Deletes a file. /// /// @param[in] filename The filename to delete. /// @return The file was successfully deleted. /// /// /// ------------------------------------------------------------------------- /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// #include /// #include /// ... /// std::string filename; /// if (kodi::gui::DialogFileBrowser::ShowAndGetFile("local", "", /// "Test File selection and delete of them!", /// filename)) /// { /// bool successed = kodi::vfs::DeleteFile(filename); /// if (!successed) /// kodi::gui::DialogOK::ShowAndGetInput("Error", "Delete of File", filename, "failed!"); /// else /// kodi::gui::DialogOK::ShowAndGetInput("Information", "Delete of File", filename, "successfull done."); /// } /// ~~~~~~~~~~~~~ /// static inline bool DeleteFile(const std::string& filename) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->delete_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str()); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Rename a file name /// /// @param[in] filename The filename to copy. /// @param[in] newFileName The new filename /// @return true if successfully renamed /// /// static inline bool RenameFile(const std::string& filename, const std::string& newFileName) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->rename_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), newFileName.c_str()); } //---------------------------------------------------------------------------- //============================================================================ /// /// @ingroup cpp_kodi_vfs /// @brief Copy a file from source to destination /// /// @param[in] filename The filename to copy. /// @param[in] destination The destination to copy file to /// @return true if successfully copied /// /// static inline bool CopyFile(const std::string& filename, const std::string& destination) { return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->copy_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), destination.c_str()); } //---------------------------------------------------------------------------- //============================================================================ /// /// \defgroup cpp_kodi_vfs_CFile class CFile /// \ingroup cpp_kodi_vfs /// /// @brief **Virtual file server control** /// /// CFile is the class used for handling Files in Kodi. This class can be used /// for creating, reading, writing and modifying files. It directly provides unbuffered, binary disk input/output services /// /// It has the header \ref Filesystem.h "#include " be included /// to enjoy it. /// /// /// ------------------------------------------------------------------------ /// /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// /// ... /// /// /* Create the needed file handle class */ /// kodi::vfs::CFile myFile(); /// /// /* In this example we use the user path for the add-on */ /// std::string file = kodi::GetUserPath() + "/myFile.txt"; /// /// /* Now create and open the file or overwrite if present */ /// myFile.OpenFileForWrite(file, true); /// /// const char* str = "I love Kodi!"; /// /// /* write string */ /// myFile.Write(str, sizeof(str)); /// /// /* On this way the Close() is not needed to call, becomes done from destructor */ /// /// ~~~~~~~~~~~~~ /// //@{ class CFile { public: //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Construct a new, unopened file /// CFile() : m_file(nullptr) { } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Close() is called from the destructor, so explicitly closing the /// file isn't required /// virtual ~CFile() { Close(); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Open the file with filename via Kodi's \ref cpp_kodi_vfs_CFile /// "CFile". Needs to be closed by calling Close() when done. /// /// @param[in] filename The filename to open. /// @param[in] flags [opt] The flags to pass, see \ref OpenFileFlags /// @return True on success or false on failure /// bool OpenFile(const std::string& filename, unsigned int flags = 0) { Close(); m_file = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->open_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), flags); return m_file != nullptr; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Open the file with filename via Kodi's \ref cpp_kodi_vfs_CFile /// "CFile" in write mode. Needs to be closed by calling Close() when /// done. /// /// @note Related folders becomes created if not present. /// /// @param[in] filename The filename to open. /// @param[in] overwrite True to overwrite, false otherwise. /// @return True on success or false on failure /// bool OpenFileForWrite(const std::string& filename, bool overwrite = false) { Close(); // Try to open the file. If it fails, check if we need to create the directory first // This way we avoid checking if the directory exists every time m_file = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->open_file_for_write(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), overwrite); if (!m_file) { std::string cacheDirectory = kodi::vfs::GetDirectoryName(filename); if (::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->directory_exists(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, cacheDirectory.c_str()) || ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->create_directory(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, cacheDirectory.c_str())) m_file = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->open_file_for_write(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, filename.c_str(), overwrite); } return m_file != nullptr; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Create a Curl representation /// /// @param[in] url the URL of the Type. /// @return True on success or false on failure /// bool CURLCreate(const std::string& url) { m_file = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->curl_create(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, url.c_str()); return m_file != nullptr; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Add options to the curl file created with CURLCreate /// /// @param[in] type option type to set, see \ref CURLOptiontype /// @param[in] name name of the option /// @param[in] value value of the option /// @return True on success or false on failure /// bool CURLAddOption(CURLOptiontype type, const std::string& name, const std::string& value) { if (!m_file) { kodi::Log(ADDON_LOG_ERROR, "kodi::vfs::CURLCreate(...) needed to call before!"); return false; } return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->curl_add_option(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, type, name.c_str(), value.c_str()); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Open the curl file created with CURLCreate /// /// @param[in] flags [opt] The flags to pass, see \ref OpenFileFlags /// @return True on success or false on failure /// bool CURLOpen(unsigned int flags = 0) { if (!m_file) { kodi::Log(ADDON_LOG_ERROR, "kodi::vfs::CURLCreate(...) needed to call before!"); return false; } return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->curl_open(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, flags); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Read from an open file. /// /// @param[in] ptr The buffer to store the data in. /// @param[in] size The size of the buffer. /// @return number of successfully read bytes if any bytes /// were read and stored in buffer, zero if no bytes /// are available to read (end of file was reached) /// or undetectable error occur, -1 in case of any /// explicit error /// ssize_t Read(void* ptr, size_t size) { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->read_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, ptr, size); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Read a string from an open file. /// /// @param[out] line The buffer to store the data in. /// @return True when a line was read, false otherwise. /// bool ReadLine(std::string &line) { line.clear(); if (!m_file) return false; // TODO: Read 1024 chars into buffer. If file position advanced that many // chars, we didn't hit a newline. Otherwise, if file position is 1 or 2 // past the number of bytes read, we read (and skipped) a newline sequence. char buffer[1025]; if (::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->read_file_string(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, buffer, sizeof(buffer))) { line = buffer; return !line.empty(); } return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Write to a file opened in write mode. /// /// @param[in] ptr Pointer to the data to write, converted to a /// const void*. /// @param[in] size Size of the data to write. /// @return number of successfully written bytes if any /// bytes were written, zero if no bytes were /// written and no detectable error occur,-1 in case /// of any explicit error /// ssize_t Write(const void* ptr, size_t size) { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->write_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, ptr, size); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Flush buffered data. /// /// If the given stream was open for writing (or if it was open for updating /// and the last i/o operation was an output operation) any unwritten data /// in its output buffer is written to the file. /// /// The stream remains open after this call. /// /// When a file is closed, either because of a call to close or because the /// class is destructed, all the buffers associated with it are /// automatically flushed. /// void Flush() { if (!m_file) return; ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->flush_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Set the file's current position. /// /// The whence argument is optional and defaults to SEEK_SET (0) /// /// @param[in] position the position that you want to seek to /// @param[in] whence [optional] offset relative to /// You can set the value of whence to one. /// of three things: /// | Value | int | Description | /// |:--------:|:---:|:----------------------------------------------------| /// | SEEK_SET | 0 | position is relative to the beginning of the file. This is probably what you had in mind anyway, and is the most commonly used value for whence. /// | SEEK_CUR | 1 | position is relative to the current file pointer position. So, in effect, you can say, "Move to my current position plus 30 bytes," or, "move to my current position minus 20 bytes." /// | SEEK_END | 2 | position is relative to the end of the file. Just like SEEK_SET except from the other end of the file. Be sure to use negative values for offset if you want to back up from the end of the file, instead of going past the end into oblivion. /// /// @return Returns the resulting offset location as /// measured in bytes from the beginning of /// the file. On error, the value -1 is /// returned. /// int64_t Seek(int64_t position, int whence = SEEK_SET) { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->seek_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, position, whence); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Truncate a file to the requested size. /// /// @param[in] size The new max size. /// @return New size? On error, the value -1 is /// returned. /// int Truncate(int64_t size) { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->truncate_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file, size); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief The current offset in an open file. /// /// @return The requested offset. On error, the value -1 is /// returned. /// int64_t GetPosition() { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_position(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Get the file size of an open file. /// /// @return The requested size. On error, the value -1 is /// returned. /// int64_t GetLength() { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_length(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Checks the file access is on end position. /// /// @return If you've reached the end of the file, AtEnd() returns true. /// bool AtEnd() { if (!m_file) return true; int64_t length = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_length(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); int64_t position = ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_position(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); return position >= length; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Close an open file. /// void Close() { if (!m_file) return; ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->close_file(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); m_file = nullptr; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Get the chunk size for an open file. /// /// @return The requested size. On error, the value -1 is /// returned. /// int GetChunkSize() { if (!m_file) return -1; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_chunk_size(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_vfs_CFile /// @brief Get the current download speed of file if loaded from web. /// /// @return The current download speed. /// double GetFileDownloadSpeed() { if (!m_file) return 0.0; return ::kodi::addon::CAddonBase::m_interface->toKodi->kodi_filesystem->get_file_download_speed(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase, m_file); } //-------------------------------------------------------------------------- private: void* m_file; }; //@} //---------------------------------------------------------------------------- } /* namespace vfs */ } /* namespace kodi */