Index: common/mptFileIO.h =================================================================== --- common/mptFileIO.h (revision 10938) +++ common/mptFileIO.h (working copy) @@ -125,6 +125,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 +145,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 +346,16 @@ #endif // MPT_ENABLE_FILEIO + +#ifdef MODPLUG_TRACKER +inline bool SyncFileData(mpt::ofstream & f) +{ + f.rdbuf()->pubsync(); + return true; +} +#endif // MODPLUG_TRACKER + + + OPENMPT_NAMESPACE_END Index: mptrack/CommandSet.cpp =================================================================== --- mptrack/CommandSet.cpp (revision 10938) +++ 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 10938) +++ 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 10938) +++ mptrack/mod2midi.cpp (working copy) @@ -710,7 +710,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 10938) +++ 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/Modedit.cpp =================================================================== --- mptrack/Modedit.cpp (revision 10938) +++ 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 10938) +++ 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 10938) +++ 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 10938) +++ 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 10938) +++ soundlib/Load_it.cpp (working copy) @@ -1375,7 +1375,7 @@ { return false; } - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; Index: soundlib/Load_mod.cpp =================================================================== --- soundlib/Load_mod.cpp (revision 10938) +++ soundlib/Load_mod.cpp (working copy) @@ -2186,7 +2186,7 @@ { if(m_nChannels == 0 || filename.empty()) return false; - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; Index: soundlib/Load_s3m.cpp =================================================================== --- soundlib/Load_s3m.cpp (revision 10938) +++ soundlib/Load_s3m.cpp (working copy) @@ -611,7 +611,7 @@ }; if(m_nChannels == 0 || filename.empty()) return false; - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; Index: soundlib/Load_xm.cpp =================================================================== --- soundlib/Load_xm.cpp (revision 10938) +++ soundlib/Load_xm.cpp (working copy) @@ -753,7 +753,7 @@ { return false; } - mpt::ofstream f(filename, std::ios::binary); + mpt::SafeOutputFile f(filename, std::ios::binary); if(!f) { return false; Index: soundlib/plugins/PlugInterface.cpp =================================================================== --- soundlib/plugins/PlugInterface.cpp (revision 10938) +++ soundlib/plugins/PlugInterface.cpp (working copy) @@ -669,7 +669,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 10938) +++ soundlib/SampleFormatFLAC.cpp (working copy) @@ -470,11 +470,11 @@ struct FLAC__StreamEncoder_RAII { FLAC__StreamEncoder *encoder; - mpt::ofstream f; + mpt::ofstream & f; operator FLAC__StreamEncoder *() { return encoder; } - FLAC__StreamEncoder_RAII() : encoder(FLAC__stream_encoder_new()) { } + FLAC__StreamEncoder_RAII(mpt::ofstream & f) : encoder(FLAC__stream_encoder_new()), f(f) { } ~FLAC__StreamEncoder_RAII() { FLAC__stream_encoder_delete(encoder); @@ -532,7 +532,12 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const { #ifdef MPT_WITH_FLAC - FLAC__StreamEncoder_RAII encoder; + mpt::SafeOutputFile f(filename, std::ios::binary); + if(!f) + { + return false; + } + FLAC__StreamEncoder_RAII encoder(f); if(encoder == nullptr) { return false; @@ -668,8 +673,7 @@ FLAC__int32 *sampleData = nullptr; SmpLength numSamples = 0; - encoder.f.open(filename, std::ios::binary); - if(!encoder.f || FLAC__stream_encoder_init_stream(encoder, &FLAC__StreamEncoder_RAII::StreamEncoderWriteCallback, &FLAC__StreamEncoder_RAII::StreamEncoderSeekCallback, &FLAC__StreamEncoder_RAII::StreamEncoderTellCallback, nullptr, &encoder.f) != FLAC__STREAM_ENCODER_INIT_STATUS_OK) + if(FLAC__stream_encoder_init_stream(encoder, &FLAC__StreamEncoder_RAII::StreamEncoderWriteCallback, &FLAC__StreamEncoder_RAII::StreamEncoderSeekCallback, &FLAC__StreamEncoder_RAII::StreamEncoderTellCallback, nullptr, &encoder.f) != FLAC__STREAM_ENCODER_INIT_STATUS_OK) { goto fail; } Index: soundlib/SampleFormats.cpp =================================================================== --- soundlib/SampleFormats.cpp (revision 10938) +++ 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 10938) +++ 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;