Index: common/mptFileIO.h
===================================================================
--- common/mptFileIO.h	(revision 10955)
+++ common/mptFileIO.h	(working copy)
@@ -23,6 +23,14 @@
 #include <streambuf>
 #include <utility>
 
+#if MPT_COMPILER_MSVC
+#include <cstdio>
+#endif // !MPT_COMPILER_MSVC
+
+#if MPT_COMPILER_MSVC
+#include <stdio.h>
+#endif // !MPT_COMPILER_MSVC
+
 #endif // MPT_ENABLE_FILEIO
 
 
@@ -125,6 +133,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 +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<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 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;
