Index: common/mptFileIO.h =================================================================== --- common/mptFileIO.h (revision 10955) +++ common/mptFileIO.h (working copy) @@ -23,6 +23,14 @@ #include #include +#if MPT_COMPILER_MSVC +#include +#endif // !MPT_COMPILER_MSVC + +#if MPT_COMPILER_MSVC +#include +#endif // !MPT_COMPILER_MSVC + #endif // MPT_ENABLE_FILEIO @@ -125,6 +133,14 @@ { 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); @@ -137,8 +153,123 @@ #endif }; +class SafeOutputFile + : public mpt::ofstream +{ +private: + typedef std::ofstream Tbase; +#if MPT_COMPILER_MSVC + FILE *m_f; +#endif // MPT_COMPILER_MSVC +#if MPT_COMPILER_MSVC + static mpt::tstring convert_mode(std::ios_base::openmode mode) + { + mpt::tstring fopen_mode; + switch(mode & ~(std::ios_base::ate | std::ios_base::binary)) + { + case std::ios_base::in: + fopen_mode = TEXT("r"); + break; + case std::ios_base::out: + MPT_FALLTHROUGH; + case std::ios_base::out | std::ios_base::trunc: + fopen_mode = TEXT("w"); + break; + case std::ios_base::app: + MPT_FALLTHROUGH; + case std::ios_base::out | std::ios_base::app: + fopen_mode = TEXT("a"); + break; + case std::ios_base::out | std::ios_base::in: + fopen_mode = TEXT("r+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::trunc: + fopen_mode = TEXT("w+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::app: + MPT_FALLTHROUGH; + case std::ios_base::in | std::ios_base::app: + fopen_mode = TEXT("a+"); + break; + } + if(fopen_mode.empty()) + { + return fopen_mode; + } + if(mode & std::ios_base::binary) + { + fopen_mode += TEXT("b"); + } + fopen_mode += TEXT("c"); // force commit on fflush (MSVC specific) + return fopen_mode; + } + FILE * internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode) + { + mpt::tstring fopen_mode = convert_mode(mode); + if(fopen_mode.empty()) + { + return nullptr; + } + FILE *f = +#ifdef UNICODE + _wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) +#else + fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) +#endif + ; + if(!f) + { + return nullptr; + } + if(mode & std::ios_base::ate) + { + if(fseek(f, 0, SEEK_END) != 0) + { + fclose(f); + f = nullptr; + return nullptr; + } + } + m_f = f; + return f; + } +#endif // MPT_COMPILER_MSVC +public: + SafeOutputFile() = delete; + explicit SafeOutputFile(const mpt::PathString &filename, std::ios_base::openmode mode = std::ios_base::out) +#if MPT_COMPILER_MSVC + : mpt::ofstream(internal_fopen(filename, mode | std::ios_base::out)) +#else // !MPT_COMPILER_MSVC + : mpt::ofstream(filename, mode) +#endif // MPT_COMPILER_MSVC + { + } + ~SafeOutputFile() + { + if(!*this) + { + return; + } + if(!rdbuf()) + { + return; + } + #if MPT_COMPILER_MSVC + if(!m_f) + { + return; + } + #endif // MPT_COMPILER_MSVC + rdbuf()->pubsync(); + #if MPT_COMPILER_MSVC + fflush(m_f); + fclose(m_f); + #endif // MPT_COMPILER_MSVC + } +}; + // 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. @@ -223,5 +354,6 @@ #endif // MPT_ENABLE_FILEIO + OPENMPT_NAMESPACE_END Index: mptrack/CommandSet.cpp =================================================================== --- mptrack/CommandSet.cpp (revision 10955) +++ mptrack/CommandSet.cpp (working copy) @@ -1510,7 +1510,7 @@ ... */ - mpt::ofstream f(filename); + mpt::SafeOutputFile f(filename); if(!f) { ErrorBox(IDS_CANT_OPEN_FILE_FOR_WRITING); Index: mptrack/ExceptionHandler.cpp =================================================================== --- mptrack/ExceptionHandler.cpp (revision 10955) +++ mptrack/ExceptionHandler.cpp (working copy) @@ -268,13 +268,13 @@ errorMessage += MPT_ULITERAL("\n\n"); { - mpt::ofstream f(crashDirectory.path + MPT_PATHSTRING("error.txt"), std::ios::binary); + mpt::SafeOutputFile f(crashDirectory.path + MPT_PATHSTRING("error.txt"), std::ios::binary); f.imbue(std::locale::classic()); f << mpt::String::Replace(mpt::ToCharset(mpt::CharsetUTF8, errorMessage), "\n", "\r\n"); } { - mpt::ofstream f(crashDirectory.path + MPT_PATHSTRING("threads.txt"), std::ios::binary); + mpt::SafeOutputFile f(crashDirectory.path + MPT_PATHSTRING("threads.txt"), std::ios::binary); f.imbue(std::locale::classic()); f << mpt::format("current : %1")(mpt::fmt::hex0<8>(GetCurrentThreadId())) << "\r\n"; f << mpt::format("GUI : %1")(mpt::fmt::hex0<8>(mpt::log::Trace::GetThreadId(mpt::log::Trace::ThreadKindGUI))) << "\r\n"; @@ -290,7 +290,7 @@ }; { - mpt::ofstream f(crashDirectory.path + MPT_PATHSTRING("active-settings.txt"), std::ios::binary); + mpt::SafeOutputFile f(crashDirectory.path + MPT_PATHSTRING("active-settings.txt"), std::ios::binary); f.imbue(std::locale::classic()); if(&theApp.GetSettings()) { @@ -357,7 +357,7 @@ */ { - mpt::ofstream f(crashDirectory.path + MPT_PATHSTRING("about-openmpt.txt"), std::ios::binary); + mpt::SafeOutputFile f(crashDirectory.path + MPT_PATHSTRING("about-openmpt.txt"), std::ios::binary); f.imbue(std::locale::classic()); f << mpt::ToCharset(mpt::CharsetUTF8, CAboutDlg::GetTabText(0)); } Index: mptrack/mod2midi.cpp =================================================================== --- mptrack/mod2midi.cpp (revision 10955) +++ mptrack/mod2midi.cpp (working copy) @@ -695,7 +695,7 @@ void CDoMidiConvert::Run() { - mpt::ofstream f(m_fileName, std::ios::binary); + mpt::SafeOutputFile f(m_fileName, std::ios::binary); if(!f.good()) { Reporting::Error("Could not open file for writing. Is it open in another application?"); Index: mptrack/Mod2wave.cpp =================================================================== --- mptrack/Mod2wave.cpp (revision 10955) +++ mptrack/Mod2wave.cpp (working copy) @@ -949,7 +949,7 @@ normalizeFile.open(normalizeFileName, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); } - mpt::ofstream fileStream(m_lpszFileName, std::ios::binary); + mpt::SafeOutputFile fileStream(m_lpszFileName, std::ios::binary); if(!fileStream) { Index: mptrack/Moddoc.cpp =================================================================== --- mptrack/Moddoc.cpp (revision 10955) +++ mptrack/Moddoc.cpp (working copy) @@ -278,7 +278,7 @@ return FALSE; BOOL ok = FALSE; - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(f) { BeginWaitCursor(); @@ -1898,7 +1898,7 @@ if(!dlg.Show()) return; filename = dlg.GetFirstFile(); - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) return; Index: mptrack/Modedit.cpp =================================================================== --- mptrack/Modedit.cpp (revision 10955) +++ mptrack/Modedit.cpp (working copy) @@ -1099,7 +1099,7 @@ CStringA s; EnvelopeToString(s, pIns->GetEnvelope(nEnv)); - mpt::ofstream f(fileName, std::ios::binary); + mpt::SafeOutputFile f(fileName, std::ios::binary); if(!f) { EndWaitCursor(); Index: mptrack/Settings.cpp =================================================================== --- mptrack/Settings.cpp (revision 10955) +++ mptrack/Settings.cpp (working copy) @@ -490,7 +490,7 @@ static void WriteFileUTF16LE(const mpt::PathString &filename, const std::wstring &str) { STATIC_ASSERT(sizeof(wchar_t) == 2); - mpt::ofstream inifile(filename, std::ios::binary); + mpt::SafeOutputFile inifile(filename, std::ios::binary); const uint8 UTF16LE_BOM[] = { 0xff, 0xfe }; inifile.write(reinterpret_cast(UTF16LE_BOM), 2); inifile.write(reinterpret_cast(str.c_str()), str.length() * sizeof(std::wstring::value_type)); Index: mptrack/TrackerSettings.cpp =================================================================== --- mptrack/TrackerSettings.cpp (revision 10955) +++ mptrack/TrackerSettings.cpp (working copy) @@ -786,7 +786,7 @@ if(!fn.FileOrDirectoryExists()) { CTuning * pT = CSoundFile::CreateTuning12TET("12TET"); - mpt::ofstream f(fn, std::ios::binary); + mpt::SafeOutputFile f(fn, std::ios::binary); pT->Serialize(f); f.close(); delete pT; @@ -798,7 +798,7 @@ if(!fn.FileOrDirectoryExists()) { CTuning * pT = CSoundFile::CreateTuning12TET("12TET [[fs15 1.17.02.49]]"); - mpt::ofstream f(fn, std::ios::binary); + mpt::SafeOutputFile f(fn, std::ios::binary); pT->Serialize(f); f.close(); delete pT; Index: mptrack/TuningDialog.cpp =================================================================== --- mptrack/TuningDialog.cpp (revision 10955) +++ mptrack/TuningDialog.cpp (working copy) @@ -617,7 +617,7 @@ BeginWaitCursor(); - mpt::ofstream fout(dlg.GetFirstFile(), std::ios::binary); + mpt::SafeOutputFile fout(dlg.GetFirstFile(), std::ios::binary); if(tuningFilter != -1 && filterIndex == tuningFilter) { @@ -691,7 +691,7 @@ SanitizeFilename(nameW); fileNameW = mpt::String::Replace(fileNameW, MPT_USTRING("%tuning_name%"), nameW); fileName = mpt::PathString::FromUnicode(fileNameW); - mpt::ofstream fout(fileName, std::ios::binary); + mpt::SafeOutputFile fout(fileName, std::ios::binary); if(tuning.Serialize(fout) != Tuning::SerializationResult::Success) { failure = true; Index: soundlib/Load_it.cpp =================================================================== --- soundlib/Load_it.cpp (revision 10955) +++ soundlib/Load_it.cpp (working copy) @@ -1364,7 +1364,6 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool compatibilityExport) { - if(!f) return false; const CModSpecifications &specs = (GetType() == MOD_TYPE_MPT ? ModSpecs::mptm : (compatibilityExport ? ModSpecs::it : ModSpecs::itEx)); Index: soundlib/Load_mod.cpp =================================================================== --- soundlib/Load_mod.cpp (revision 10955) +++ soundlib/Load_mod.cpp (working copy) @@ -2184,7 +2184,10 @@ bool CSoundFile::SaveMod(std::ostream &f) const { - if(!f || m_nChannels == 0) return false; + if(m_nChannels == 0) + { + return false; + } // Write song title { Index: soundlib/Load_s3m.cpp =================================================================== --- soundlib/Load_s3m.cpp (revision 10955) +++ soundlib/Load_s3m.cpp (working copy) @@ -610,7 +610,10 @@ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; - if(!f || m_nChannels == 0) return false; + if(m_nChannels == 0) + { + return false; + } const bool saveMuteStatus = #ifdef MODPLUG_TRACKER Index: soundlib/Load_xm.cpp =================================================================== --- soundlib/Load_xm.cpp (revision 10955) +++ soundlib/Load_xm.cpp (working copy) @@ -749,7 +749,6 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) { - if(!f) return false; bool addChannel = false; // avoid odd channel count for FT2 compatibility Index: soundlib/plugins/PlugInterface.cpp =================================================================== --- soundlib/plugins/PlugInterface.cpp (revision 10955) +++ soundlib/plugins/PlugInterface.cpp (working copy) @@ -652,7 +652,7 @@ bool bank = (dlg.GetExtension() == MPT_PATHSTRING("fxb")); - mpt::ofstream f(dlg.GetFirstFile(), std::ios::binary); + mpt::SafeOutputFile f(dlg.GetFirstFile(), std::ios::binary); if(f.good() && VSTPresets::SaveFile(f, *this, bank)) { return true; Index: soundlib/SampleFormatFLAC.cpp =================================================================== --- soundlib/SampleFormatFLAC.cpp (revision 10955) +++ soundlib/SampleFormatFLAC.cpp (working copy) @@ -533,7 +533,7 @@ { #ifdef MPT_WITH_FLAC - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; Index: soundlib/SampleFormats.cpp =================================================================== --- soundlib/SampleFormats.cpp (revision 10955) +++ soundlib/SampleFormats.cpp (working copy) @@ -539,7 +539,7 @@ #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const { - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; @@ -829,7 +829,7 @@ bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const { - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; @@ -1170,7 +1170,7 @@ bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, const mpt::PathString &filename) const { - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) return false; @@ -1342,7 +1342,7 @@ return false; } - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; @@ -2160,7 +2160,7 @@ { return false; } - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f.good()) { return false; @@ -3255,7 +3255,7 @@ ModInstrument *pIns = Instruments[nInstr]; if((!pIns) || filename.empty()) return false; - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; Index: soundlib/tuningCollection.cpp =================================================================== --- soundlib/tuningCollection.cpp (revision 10955) +++ soundlib/tuningCollection.cpp (working copy) @@ -281,7 +281,7 @@ error = true; } else { - mpt::ofstream fout(fn, std::ios::binary); + mpt::SafeOutputFile fout(fn, std::ios::binary); if(tuning.Serialize(fout) != Tuning::SerializationResult::Success) { error = true;