Index: common/mptFileIO.h
===================================================================
--- common/mptFileIO.h	(revision 10938)
+++ common/mptFileIO.h	(working copy)
@@ -125,6 +125,14 @@
 	{
 		detail::fstream_open<Tbase>(*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<Tbase>(*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<const char*>(UTF16LE_BOM), 2);
 	inifile.write(reinterpret_cast<const char*>(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;
