/* * mptFileIO.h * ----------- * Purpose: A wrapper around std::fstream, enforcing usage of mpt::PathString. * Notes : You should only ever use these wrappers instead of plain std::fstream classes. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "BuildSettings.h" #if defined(MPT_ENABLE_FILEIO) #include "../common/mptString.h" #include "../common/mptPathString.h" #include "../common/mptIO.h" #include #include #include #include #include #if MPT_COMPILER_MSVC #include #endif // !MPT_COMPILER_MSVC #if MPT_COMPILER_MSVC #include #endif // !MPT_COMPILER_MSVC #endif // MPT_ENABLE_FILEIO OPENMPT_NAMESPACE_BEGIN #if defined(MPT_ENABLE_FILEIO) // Sets the NTFS compression attribute on the file or directory. // Requires read and write permissions for already opened files. // Returns true if the attribute has been set. // In almost all cases, the return value should be ignored because most filesystems other than NTFS do not support compression. #ifdef MODPLUG_TRACKER #if MPT_OS_WINDOWS bool SetFilesystemCompression(HANDLE hFile); bool SetFilesystemCompression(int fd); bool SetFilesystemCompression(const mpt::PathString &filename); #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER namespace mpt { #if MPT_COMPILER_GCC && MPT_OS_WINDOWS // GCC C++ library has no wchar_t overloads #define MPT_FSTREAM_DO_CONVERSIONS_ANSI #endif namespace detail { template inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode) { #if defined(MPT_FSTREAM_DO_CONVERSIONS_ANSI) base.open(mpt::ToCharset(mpt::Charset::Locale, filename.AsNative()).c_str(), mode); #else base.open(filename.AsNativePrefixed().c_str(), mode); #endif } } // namespace detail class SafeOutputFile; class fstream : public std::fstream { private: typedef std::fstream Tbase; public: friend SafeOutputFile; public: fstream() {} fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { detail::fstream_open(*this, filename, mode); } void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { detail::fstream_open(*this, filename, mode); } void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; #if MPT_OS_WINDOWS void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; #endif }; class ifstream : public std::ifstream { private: typedef std::ifstream Tbase; public: friend SafeOutputFile; public: ifstream() {} ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) { detail::fstream_open(*this, filename, mode); } void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) { detail::fstream_open(*this, filename, mode); } void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; #if MPT_OS_WINDOWS void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; #endif }; class ofstream : public std::ofstream { private: typedef std::ofstream Tbase; public: friend SafeOutputFile; public: ofstream() {} ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) { detail::fstream_open(*this, filename, mode); } #if MPT_COMPILER_MSVC protected: ofstream(FILE * file) : std::ofstream(file) { } #endif // MPT_COMPILER_MSVC public: void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) { detail::fstream_open(*this, filename, mode); } void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; #if MPT_OS_WINDOWS void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; #endif }; enum class FlushMode { None = 0, // no explicit flushes at all Single = 1, // explicitly flush higher-leverl API layers Full = 2, // explicitly flush *all* layers, up to and including disk write caches }; inline FlushMode FlushModeFromBool(bool flush) { return flush ? FlushMode::Full : FlushMode::None; } #ifdef MODPLUG_TRACKER class SafeOutputFile { private: FlushMode m_FlushMode; #if MPT_COMPILER_MSVC FILE *m_f = nullptr; #endif // MPT_COMPILER_MSVC mpt::ofstream m_s; #if MPT_COMPILER_MSVC static mpt::tstring convert_mode(std::ios_base::openmode mode, FlushMode flushMode); FILE * internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode); #endif // MPT_COMPILER_MSVC public: SafeOutputFile() = delete; explicit SafeOutputFile(const mpt::PathString &filename, std::ios_base::openmode mode = std::ios_base::out, FlushMode flushMode = FlushMode::Full) : m_FlushMode(flushMode) #if MPT_COMPILER_MSVC , m_s(internal_fopen(filename, mode | std::ios_base::out, flushMode)) #else // !MPT_COMPILER_MSVC , m_s(filename, mode) #endif // MPT_COMPILER_MSVC { if(!stream().is_open()) stream().setstate(mpt::ofstream::failbit); } mpt::ofstream& stream() { return m_s; } operator mpt::ofstream& () { return stream(); } const mpt::ofstream& stream() const { return m_s; } operator const mpt::ofstream& () const { return stream(); } operator bool() const { return stream() ? true : false; } bool operator!() const { return stream().operator!(); } ~SafeOutputFile() noexcept(false); }; #endif // MODPLUG_TRACKER #ifdef MODPLUG_TRACKER // LazyFileRef is a simple reference to an on-disk file by the means of a // filename which allows easy assignment of the whole file contents to and from // byte buffers. class LazyFileRef { private: const mpt::PathString m_Filename; public: LazyFileRef(const mpt::PathString &filename) : m_Filename(filename) { return; } public: LazyFileRef & operator = (const std::vector &data); LazyFileRef & operator = (const std::vector &data); LazyFileRef & operator = (const std::string &data); operator std::vector () const; operator std::vector () const; operator std::string () const; }; #endif // MODPLUG_TRACKER } // namespace mpt class InputFile { private: mpt::PathString m_Filename; mpt::ifstream m_File; bool m_IsCached; std::vector m_Cache; public: static bool DefaultToLargeAddressSpaceUsage(); public: InputFile(); InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); ~InputFile(); bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); bool IsValid() const; bool IsCached() const; const mpt::PathString& GetFilenameRef() const; std::istream* GetStream(); mpt::const_byte_span GetCache(); }; #endif // MPT_ENABLE_FILEIO OPENMPT_NAMESPACE_END