diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/common/mptFileIO.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/common/mptFileIO.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/common/mptFileIO.cpp | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/common/mptFileIO.cpp b/Src/external_dependencies/openmpt-trunk/common/mptFileIO.cpp new file mode 100644 index 00000000..a38dd779 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/common/mptFileIO.cpp @@ -0,0 +1,608 @@ +/* + * mptFileIO.cpp + * ------------- + * Purpose: File I/O wrappers + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptFileIO.h" + +#if defined(MPT_ENABLE_FILEIO) +#include "mpt/io/io.hpp" +#include "mpt/io/io_stdstream.hpp" +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS +#include "mpt/system_error/system_error.hpp" +#include "FileReader.h" +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS +#endif // MPT_ENABLE_FILEIO + +#if defined(MPT_ENABLE_FILEIO) +#include <stdexcept> +#endif // MPT_ENABLE_FILEIO + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +#include <windows.h> +#include <WinIoCtl.h> +#include <io.h> +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + +#if defined(MPT_ENABLE_FILEIO) +#if MPT_COMPILER_MSVC +#include <stdio.h> +#include <tchar.h> +#endif // MPT_COMPILER_MSVC +#endif // MPT_ENABLE_FILEIO + + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_FILEIO) + + + +#if !defined(MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS) + +#if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR) +#if MPT_GCC_BEFORE(9,1,0) +MPT_WARNING("Warning: MinGW with GCC earlier than 9.1 detected. Standard library does neither provide std::fstream wchar_t overloads nor std::filesystem with wchar_t support. Unicode filename support is thus unavailable.") +#endif // MPT_GCC_AT_LEAST(9,1,0) +#endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR + +#endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS + + + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +bool SetFilesystemCompression(HANDLE hFile) +{ + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + USHORT format = COMPRESSION_FORMAT_DEFAULT; + DWORD dummy = 0; + BOOL result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, (LPVOID)&format, sizeof(format), NULL, 0, &dummy /*required*/ , NULL); + return result != FALSE; +} +bool SetFilesystemCompression(int fd) +{ + if(fd < 0) + { + return false; + } + uintptr_t fhandle = _get_osfhandle(fd); + HANDLE hFile = (HANDLE)fhandle; + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + return SetFilesystemCompression(hFile); +} +bool SetFilesystemCompression(const mpt::PathString &filename) +{ + DWORD attributes = GetFileAttributes(filename.AsNativePrefixed().c_str()); + if(attributes == INVALID_FILE_ATTRIBUTES) + { + return false; + } + if(attributes & FILE_ATTRIBUTE_COMPRESSED) + { + return true; + } + HANDLE hFile = CreateFile(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + bool result = SetFilesystemCompression(hFile); + CloseHandle(hFile); + return result; +} +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +#ifdef MODPLUG_TRACKER + +namespace mpt { + +#if MPT_COMPILER_MSVC + +mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMode flushMode) +{ + mpt::tstring fopen_mode; + switch(mode & ~(std::ios_base::ate | std::ios_base::binary)) + { + case std::ios_base::in: + fopen_mode = _T("r"); + break; + case std::ios_base::out: + [[fallthrough]]; + case std::ios_base::out | std::ios_base::trunc: + fopen_mode = _T("w"); + break; + case std::ios_base::app: + [[fallthrough]]; + case std::ios_base::out | std::ios_base::app: + fopen_mode = _T("a"); + break; + case std::ios_base::out | std::ios_base::in: + fopen_mode = _T("r+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::trunc: + fopen_mode = _T("w+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::app: + [[fallthrough]]; + case std::ios_base::in | std::ios_base::app: + fopen_mode = _T("a+"); + break; + } + if(fopen_mode.empty()) + { + return fopen_mode; + } + if(mode & std::ios_base::binary) + { + fopen_mode += _T("b"); + } + if(flushMode == FlushMode::Full) + { + fopen_mode += _T("c"); // force commit on fflush (MSVC specific) + } + return fopen_mode; +} + +std::FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode) +{ + m_f = nullptr; + mpt::tstring fopen_mode = convert_mode(mode, flushMode); + if(fopen_mode.empty()) + { + return nullptr; + } + std::FILE *f = + #ifdef UNICODE + _wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) + #else + std::fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) + #endif + ; + if(!f) + { + return nullptr; + } + if(mode & std::ios_base::ate) + { + if(std::fseek(f, 0, SEEK_END) != 0) + { + std::fclose(f); + f = nullptr; + return nullptr; + } + } + m_f = f; + return f; +} + +#endif // MPT_COMPILER_MSVC + +// cppcheck-suppress exceptThrowInDestructor +SafeOutputFile::~SafeOutputFile() noexcept(false) +{ + const bool mayThrow = (std::uncaught_exceptions() == 0); + if(!stream()) + { + #if MPT_COMPILER_MSVC + if(m_f) + { + std::fclose(m_f); + } + #endif // MPT_COMPILER_MSVC + if(mayThrow && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + { + // cppcheck-suppress exceptThrowInDestructor + throw std::ios_base::failure(std::string("Error before flushing file buffers.")); + } + return; + } + if(!stream().rdbuf()) + { + #if MPT_COMPILER_MSVC + if(m_f) + { + std::fclose(m_f); + } + #endif // MPT_COMPILER_MSVC + if(mayThrow && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + { + // cppcheck-suppress exceptThrowInDestructor + throw std::ios_base::failure(std::string("Error before flushing file buffers.")); + } + return; + } +#if MPT_COMPILER_MSVC + if(!m_f) + { + return; + } +#endif // MPT_COMPILER_MSVC + bool errorOnFlush = false; + if(m_FlushMode != FlushMode::None) + { + try + { + if(stream().rdbuf()->pubsync() != 0) + { + errorOnFlush = true; + } + } catch(const std::exception &) + { + errorOnFlush = true; +#if MPT_COMPILER_MSVC + if(m_FlushMode != FlushMode::None) + { + if(std::fflush(m_f) != 0) + { + errorOnFlush = true; + } + } + if(std::fclose(m_f) != 0) + { + errorOnFlush = true; + } +#endif // MPT_COMPILER_MSVC + if(mayThrow) + { + // ignore errorOnFlush here, and re-throw the earlier exception + // cppcheck-suppress exceptThrowInDestructor + throw; + } + } + } +#if MPT_COMPILER_MSVC + if(m_FlushMode != FlushMode::None) + { + if(std::fflush(m_f) != 0) + { + errorOnFlush = true; + } + } + if(std::fclose(m_f) != 0) + { + errorOnFlush = true; + } +#endif // MPT_COMPILER_MSVC + if(mayThrow && errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + { + // cppcheck-suppress exceptThrowInDestructor + throw std::ios_base::failure(std::string("Error flushing file buffers.")); + } +} + +} // namespace mpt + +#endif // MODPLUG_TRACKER + + + +#ifdef MODPLUG_TRACKER + +namespace mpt { + +LazyFileRef & LazyFileRef::operator = (const std::vector<std::byte> &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, mpt::as_span(data)); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef & LazyFileRef::operator = (const std::vector<char> &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, mpt::as_span(data)); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef & LazyFileRef::operator = (const std::string &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, mpt::as_span(data)); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef::operator std::vector<std::byte> () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::vector<std::byte>(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector<std::byte> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); + return buf; +} + +LazyFileRef::operator std::vector<char> () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::vector<char>(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector<char> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); + return buf; +} + +LazyFileRef::operator std::string () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::string(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector<char> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); + return mpt::buffer_cast<std::string>(buf); +} + +} // namespace mpt + +#endif // MODPLUG_TRACKER + + +InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching) + : m_IsValid(false) + , m_IsCached(false) +{ + MPT_ASSERT(!filename.empty()); + Open(filename, allowWholeFileCaching); +} + +InputFile::~InputFile() +{ + return; +} + + +bool InputFile::Open(const mpt::PathString &filename, bool allowWholeFileCaching) +{ + m_IsCached = false; + m_Cache.resize(0); + m_Cache.shrink_to_fit(); + m_Filename = filename; + m_File.open(m_Filename, std::ios::binary | std::ios::in); + if(allowWholeFileCaching) + { + if(mpt::IO::IsReadSeekable(m_File)) + { + if(!mpt::IO::SeekEnd(m_File)) + { + m_File.close(); + return false; + } + mpt::IO::Offset filesize = mpt::IO::TellRead(m_File); + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + if(mpt::in_range<std::size_t>(filesize)) + { + std::size_t buffersize = mpt::saturate_cast<std::size_t>(filesize); + m_Cache.resize(buffersize); + if(mpt::IO::ReadRaw(m_File, mpt::as_span(m_Cache)).size() != mpt::saturate_cast<std::size_t>(filesize)) + { + m_File.close(); + return false; + } + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + m_IsCached = true; + m_IsValid = true; + return true; + } + } + } + m_IsValid = true; + return m_File.good(); +} + + +bool InputFile::IsValid() const +{ + return m_IsValid && m_File.good(); +} + + +bool InputFile::IsCached() const +{ + return m_IsCached; +} + + +mpt::PathString InputFile::GetFilename() const +{ + return m_Filename; +} + + +std::istream& InputFile::GetStream() +{ + MPT_ASSERT(!m_IsCached); + return m_File; +} + + +mpt::const_byte_span InputFile::GetCache() +{ + MPT_ASSERT(m_IsCached); + return mpt::as_span(m_Cache); +} + + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + +OnDiskFileWrapper::OnDiskFileWrapper(FileCursor &file, const mpt::PathString &fileNameExtension) + : m_IsTempFile(false) +{ + try + { + file.Rewind(); + if(!file.GetOptionalFileName()) + { + const mpt::PathString tempName = mpt::CreateTempFileName(P_("OpenMPT"), fileNameExtension); + +#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT +#if (_WIN32_WINNT < 0x0602) +#define MPT_ONDISKFILEWRAPPER_NO_CREATEFILE +#endif +#endif + +#ifdef MPT_ONDISKFILEWRAPPER_NO_CREATEFILE + + mpt::ofstream f(tempName, std::ios::binary); + if(!f) + { + throw std::runtime_error("Error creating temporary file."); + } + while(!file.EndOfFile()) + { + FileCursor::PinnedView view = file.ReadPinnedView(mpt::IO::BUFFERSIZE_NORMAL); + std::size_t towrite = view.size(); + std::size_t written = 0; + do + { + std::size_t chunkSize = mpt::saturate_cast<std::size_t>(towrite); + bool chunkOk = false; + chunkOk = mpt::IO::WriteRaw(f, mpt::const_byte_span(view.data() + written, chunkSize)); + if(!chunkOk) + { + throw std::runtime_error("Incomplete Write."); + } + towrite -= chunkSize; + written += chunkSize; + } while(towrite > 0); + } + f.close(); + +#else // !MPT_ONDISKFILEWRAPPER_NO_CREATEFILE + + HANDLE hFile = NULL; + #if MPT_OS_WINDOWS_WINRT + hFile = mpt::windows::CheckFileHANDLE(CreateFile2(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, NULL)); + #else + hFile = mpt::windows::CheckFileHANDLE(CreateFile(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL)); + #endif + while(!file.EndOfFile()) + { + FileCursor::PinnedView view = file.ReadPinnedView(mpt::IO::BUFFERSIZE_NORMAL); + std::size_t towrite = view.size(); + std::size_t written = 0; + do + { + DWORD chunkSize = mpt::saturate_cast<DWORD>(towrite); + DWORD chunkDone = 0; + try + { + mpt::windows::CheckBOOL(WriteFile(hFile, view.data() + written, chunkSize, &chunkDone, NULL)); + } catch(...) + { + CloseHandle(hFile); + hFile = NULL; + throw; + } + if(chunkDone != chunkSize) + { + CloseHandle(hFile); + hFile = NULL; + throw std::runtime_error("Incomplete WriteFile()."); + } + towrite -= chunkDone; + written += chunkDone; + } while(towrite > 0); + } + CloseHandle(hFile); + hFile = NULL; + +#endif // MPT_ONDISKFILEWRAPPER_NO_CREATEFILE + + m_Filename = tempName; + m_IsTempFile = true; + } else + { + m_Filename = file.GetOptionalFileName().value(); + } + } catch (const std::runtime_error &) + { + m_IsTempFile = false; + m_Filename = mpt::PathString(); + } +} + + +OnDiskFileWrapper::~OnDiskFileWrapper() +{ + if(m_IsTempFile) + { + DeleteFile(m_Filename.AsNative().c_str()); + m_IsTempFile = false; + } + m_Filename = mpt::PathString(); +} + + +bool OnDiskFileWrapper::IsValid() const +{ + return !m_Filename.empty(); +} + + +mpt::PathString OnDiskFileWrapper::GetFilename() const +{ + return m_Filename; +} + + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + +#else // !MPT_ENABLE_FILEIO + +MPT_MSVC_WORKAROUND_LNK4221(mptFileIO) + +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_END |