diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 2599d280b724486bbd79849faddbc33bd529da97..1e0772ffa11ff293ca4e6c523e210e11f02fba98 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -772,6 +772,7 @@
     <Compile Include="General\CRC.cs" />
     <Compile Include="General\ErrorItem.cs" />
     <Compile Include="General\ErrorLogger.cs" />
+    <Compile Include="General\FileLockChecker.cs" />
     <Compile Include="General\MurmurHash2.cs" />
     <Compile Include="General\SavePurpose.cs" />
     <Compile Include="General\UpdateChecker.cs" />
diff --git a/Source/Core/General/FileLockChecker.cs b/Source/Core/General/FileLockChecker.cs
new file mode 100644
index 0000000000000000000000000000000000000000..34d976eebf5ec550bb9bb23f751b76d2efee7132
--- /dev/null
+++ b/Source/Core/General/FileLockChecker.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace CodeImp.DoomBuilder
+{
+	internal static class FileLockChecker
+	{
+		internal class FileLockCheckResult //mxd
+		{
+			public string Error;
+			public List<string> ProcessInfos = new List<string>();
+		}
+		
+		[StructLayout(LayoutKind.Sequential)]
+		private struct RM_UNIQUE_PROCESS
+		{
+			public int dwProcessId;
+			public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
+		}
+
+		private const int RmRebootReasonNone = 0;
+		private const int CCH_RM_MAX_APP_NAME = 255;
+		private const int CCH_RM_MAX_SVC_NAME = 63;
+
+		private enum RM_APP_TYPE
+		{
+			RmUnknownApp = 0,
+			RmMainWindow = 1,
+			RmOtherWindow = 2,
+			RmService = 3,
+			RmExplorer = 4,
+			RmConsole = 5,
+			RmCritical = 1000
+		}
+
+		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+		private struct RM_PROCESS_INFO
+		{
+			public RM_UNIQUE_PROCESS Process;
+
+			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
+			public string strAppName;
+
+			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
+			public string strServiceShortName;
+
+			public RM_APP_TYPE ApplicationType;
+			public uint AppStatus;
+			public uint TSSessionId;
+			[MarshalAs(UnmanagedType.Bool)]
+			public bool bRestartable;
+		}
+
+		[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
+		private static extern int RmRegisterResources(uint pSessionHandle,
+											  UInt32 nFiles,
+											  string[] rgsFilenames,
+											  UInt32 nApplications,
+											  [In] RM_UNIQUE_PROCESS[] rgApplications,
+											  UInt32 nServices,
+											  string[] rgsServiceNames);
+
+		[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
+		private static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
+
+		[DllImport("rstrtmgr.dll")]
+		private static extern int RmEndSession(uint pSessionHandle);
+
+		[DllImport("rstrtmgr.dll")]
+		private static extern int RmGetList(uint dwSessionHandle,
+									out uint pnProcInfoNeeded,
+									ref uint pnProcInfo,
+									[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
+									ref uint lpdwRebootReasons);
+
+		/// <summary>
+		/// Find out what process(es) have a lock on the specified file.
+		/// </summary>
+		/// <param name="path">Path of the file.</param>
+		/// <returns>Processes locking the file</returns>
+		/// <remarks>See also:
+		/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
+		/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
+		/// 
+		/// </remarks>
+		static public FileLockCheckResult CheckFile(string path)
+		{
+			uint handle;
+			string key = Guid.NewGuid().ToString();
+			FileLockCheckResult result = new FileLockCheckResult(); //mxd
+
+			int res = RmStartSession(out handle, 0, key);
+			if(res != 0)
+			{
+				result.Error = "Could not begin restart session. Unable to determine file locker."; //mxd
+				return result;
+			}
+
+			try
+			{
+				const int ERROR_MORE_DATA = 234;
+				uint pnProcInfoNeeded,
+					 pnProcInfo = 0,
+					 lpdwRebootReasons = RmRebootReasonNone;
+
+				string[] resources = new[] { path }; // Just checking on one resource.
+				res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
+				if(res != 0)
+				{
+					result.Error = "Could not register resource."; //mxd
+					return result;
+				}
+
+				//Note: there's a race condition here -- the first call to RmGetList() returns
+				//      the total number of process. However, when we call RmGetList() again to get
+				//      the actual processes this number may have increased.
+				res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
+				if(res == ERROR_MORE_DATA)
+				{
+					// Create an array to store the process results
+					RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
+					pnProcInfo = pnProcInfoNeeded;
+
+					// Get the list
+					res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
+					if(res == 0)
+					{
+						List<Process> processes = new List<Process>((int)pnProcInfo);
+
+						// Enumerate all of the results and add them to the 
+						// list to be returned
+						for(int i = 0; i < pnProcInfo; i++)
+						{
+							try
+							{
+								processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
+							}
+							// catch the error -- in case the process is no longer running
+							catch(ArgumentException) {}
+						}
+
+						//mxd
+						foreach(Process process in processes)
+						{
+							if(General.ThisAssembly.Location == process.MainModule.FileName) continue; //don't count ourselves
+							result.ProcessInfos.Add(Path.GetFileName(process.MainModule.FileName) 
+								+ " ('" + process.MainModule.FileName 
+								+ "', started at " + process.StartTime + ")");
+						}
+					}
+					else
+					{
+						result.Error = "ERROR " + res + ". Could not list processes locking resource."; //mxd
+						return result;
+					}
+				}
+				else if(res != 0)
+				{
+					result.Error = "ERROR " + res + ". Could not list processes locking resource. Failed to get size of result."; //mxd
+					return result;
+				}
+			}
+			finally
+			{
+				RmEndSession(handle);
+			}
+
+			return result;
+		}
+	}
+}
diff --git a/Source/Core/General/General.cs b/Source/Core/General/General.cs
index 36d2eadffd5993829b408c2b56a20395e45346e6..403c79cfb64ffe8c84ec9b9b9a81e392808a4584 100644
--- a/Source/Core/General/General.cs
+++ b/Source/Core/General/General.cs
@@ -729,21 +729,7 @@ namespace CodeImp.DoomBuilder
 				}
 
 				//mxd. Check for updates?
-				if(General.Settings.CheckForUpdates)
-				{
-					if(!File.Exists(Path.Combine(apppath, "Updater.exe")))
-					{
-						General.ErrorLogger.Add(ErrorType.Warning, "Update check failed: Updater.exe does not exist!");
-					} 
-					else if(!File.Exists(Path.Combine(apppath, "Updater.ini")))
-					{
-						General.ErrorLogger.Add(ErrorType.Warning, "Update check failed: Updater.ini does not exist!");
-					}
-					else
-					{
-						UpdateChecker.PerformCheck(false);
-					}
-				}
+				if(General.Settings.CheckForUpdates) UpdateChecker.PerformCheck(false);
 				
 				// Run application from the main window
 				Application.Run(mainwindow);
@@ -1076,13 +1062,6 @@ namespace CodeImp.DoomBuilder
 		[BeginAction("newmap")]
 		internal static void NewMap()
 		{
-			//mxd
-			if(map != null && map.Launcher.GameEngineRunning) 
-			{
-				ShowWarningMessage("Cannot create a map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return;
-			}
-			
 			MapOptions newoptions = new MapOptions();
 
 			// Cancel volatile mode, if any
@@ -1160,13 +1139,6 @@ namespace CodeImp.DoomBuilder
 		internal static void ActionCloseMap() { CloseMap(); }
 		internal static bool CloseMap()
 		{
-			//mxd
-			if(map != null && map.Launcher.GameEngineRunning) 
-			{
-				ShowWarningMessage("Cannot close the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return false;
-			}
-			
 			// Cancel volatile mode, if any
 			editing.DisengageVolatileMode();
 
@@ -1220,12 +1192,6 @@ namespace CodeImp.DoomBuilder
 		[BeginAction("openmap")]
 		internal static void OpenMap()
 		{
-			if(map != null && map.Launcher.GameEngineRunning) //mxd
-			{
-				ShowWarningMessage("Cannot open a map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return;
-			}
-			
 			// Cancel volatile mode, if any
 			editing.DisengageVolatileMode();
 
@@ -1264,13 +1230,6 @@ namespace CodeImp.DoomBuilder
 				return;
 			}
 
-			//mxd
-			if(map.Launcher.GameEngineRunning) 
-			{
-				ShowWarningMessage("Cannot change the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return;
-			}
-
 			// Cancel volatile mode, if any
 			Editing.DisengageVolatileMode();
 
@@ -1457,14 +1416,6 @@ namespace CodeImp.DoomBuilder
 		internal static bool SaveMap()
 		{
 			if(map == null) return false;
-
-			//mxd
-			if (map.Launcher.GameEngineRunning) 
-			{
-				ShowWarningMessage("Cannot save the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return false;
-			}
-
 			bool result = false;
 			
 			// Cancel volatile mode, if any
@@ -1535,14 +1486,6 @@ namespace CodeImp.DoomBuilder
 		internal static bool SaveMapAs()
 		{
 			if(map == null) return false;
-
-			//mxd
-			if(map.Launcher.GameEngineRunning)
-			{
-				ShowWarningMessage("Cannot save the map while a game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return false;
-			}
-
 			bool result = false;
 
 			// Cancel volatile mode, if any
@@ -1615,14 +1558,6 @@ namespace CodeImp.DoomBuilder
 		internal static bool SaveMapInto()
 		{
 			if(map == null) return false;
-
-			//mxd
-			if(map.Launcher.GameEngineRunning) 
-			{
-				ShowWarningMessage("Cannot save the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
-				return false;
-			}
-
 			bool result = false;
 
 			// Cancel volatile mode, if any
diff --git a/Source/Core/General/Launcher.cs b/Source/Core/General/Launcher.cs
index 23cf3d7cfb77cff7b5b51cc23cbff24142046a93..4f761e29315fe603161e87b9b3a7be5309226bdd 100644
--- a/Source/Core/General/Launcher.cs
+++ b/Source/Core/General/Launcher.cs
@@ -49,7 +49,6 @@ namespace CodeImp.DoomBuilder
 		#region ================== Properties
 
 		public string TempWAD { get { return tempwad; } }
-		public bool GameEngineRunning { get { return process != null; } } //mxd
 
 		#endregion
 
@@ -251,16 +250,9 @@ namespace CodeImp.DoomBuilder
 			General.Editing.Mode.OnMapTestEnd(true);
 		}
 		
-		// This saves the map to a temporary file and launches a test wit hthe given skill
+		// This saves the map to a temporary file and launches a test with the given skill
 		public void TestAtSkill(int skill)
 		{
-			//mxd
-			if (process != null) 
-			{
-				General.ShowWarningMessage("Game engine is already running." + Environment.NewLine + " Please close '" + General.Map.ConfigSettings.TestProgram + "' before testing again", MessageBoxButtons.OK);
-				return;
-			}
-			
 			Cursor oldcursor = Cursor.Current;
 
 			// Check if configuration is OK
@@ -362,21 +354,6 @@ namespace CodeImp.DoomBuilder
 			if(General.Editing.Mode is ClassicMode) General.MainWindow.RedrawDisplay();
 		}
 
-		//mxd
-		public void StopGameEngine() 
-		{
-			//mxd. Terminate process?
-			if(process != null) 
-			{
-				process.CloseMainWindow();
-				process.Close();
-				process = null;
-
-				// Remove temporary file
-				try { if(File.Exists(tempwad)) File.Delete(tempwad); } catch(Exception) { }
-			}
-		}
-
 		//mxd
 		private void ProcessOnExited(object sender, EventArgs eventArgs) 
 		{
diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs
index b2a447e87cc4f83a5cce0a2f6a82bb5ed14f9559..b4763d01c1554af621b26e9b4b6c8cdac9f44a3c 100644
--- a/Source/Core/General/MapManager.cs
+++ b/Source/Core/General/MapManager.cs
@@ -577,13 +577,29 @@ namespace CodeImp.DoomBuilder
 		// Initializes for an existing map
 		internal bool SaveMap(string newfilepathname, SavePurpose purpose) 
 		{
-			MapSet outputset;
-			string nodebuildername, settingsfile;
-			StatusInfo oldstatus;
+			//mxd. Check if the target file is locked
+			FileLockChecker.FileLockCheckResult checkresult = FileLockChecker.CheckFile(newfilepathname);
+			if(!string.IsNullOrEmpty(checkresult.Error))
+			{
+				General.ShowErrorMessage("Unable to save the map: target file is locked by another process. "
+					+ Environment.NewLine + "Also, unable to get the name of the offending process:" 
+					+ Environment.NewLine + Environment.NewLine + checkresult.Error
+					, MessageBoxButtons.OK);
+				return false;
+			}
+			if(checkresult.ProcessInfos.Count > 0)
+			{
+				General.ShowErrorMessage("Unable to save the map: target file is locked by the following process" + (checkresult.ProcessInfos.Count > 1 ? "es" : "") + ":"
+					+ Environment.NewLine + Environment.NewLine
+					+ string.Join(Environment.NewLine + Environment.NewLine, checkresult.ProcessInfos.ToArray())
+					, MessageBoxButtons.OK);
+				return false;
+			}
+
+			string settingsfile;
 			WAD targetwad;
 			int index;
 			bool includenodes;
-			string origmapname;
 
 			General.WriteLogLine("Saving map to file: " + newfilepathname);
 
@@ -614,7 +630,7 @@ namespace CodeImp.DoomBuilder
 			if (changed) 
 			{
 				// Make a copy of the map data
-				outputset = map.Clone();
+				MapSet outputset = map.Clone();
 
 				// Remove all flags from all 3D Start things
 				foreach (Thing t in outputset.Things) 
@@ -629,6 +645,7 @@ namespace CodeImp.DoomBuilder
 				}
 
 				// Do we need sidedefs compression?
+				StatusInfo oldstatus;
 				if (map.Sidedefs.Count > io.MaxSidedefs) 
 				{
 					// Compress sidedefs
@@ -684,15 +701,12 @@ namespace CodeImp.DoomBuilder
 				outputset.Dispose();
 
 				// Get the corresponding nodebuilder
-				nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
+				string nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
 
 				// Build the nodes
 				oldstatus = General.MainWindow.Status;
 				General.MainWindow.DisplayStatus(StatusType.Busy, "Building map nodes...");
-				if (!string.IsNullOrEmpty(nodebuildername))
-					includenodes = BuildNodes(nodebuildername, true);
-				else
-					includenodes = false;
+				includenodes = (!string.IsNullOrEmpty(nodebuildername) && BuildNodes(nodebuildername, true));
 				General.MainWindow.DisplayStatus(oldstatus);
 			}
 			else 
@@ -705,7 +719,7 @@ namespace CodeImp.DoomBuilder
 			data.Suspend();
 
 			// Determine original map name
-			origmapname = (options.PreviousName != "" && purpose != SavePurpose.IntoFile) ? options.PreviousName : options.CurrentName;
+			string origmapname = (options.PreviousName != "" && purpose != SavePurpose.IntoFile) ? options.PreviousName : options.CurrentName;
 			string origwadfile = string.Empty; //mxd
 
 			try 
diff --git a/Source/Core/General/UpdateChecker.cs b/Source/Core/General/UpdateChecker.cs
index 3ac9e5a06c3bc13632f3e65d18bb9d2841bd26fd..c2876ae923c8c70165eaf01c87fa2c33af093bd0 100644
--- a/Source/Core/General/UpdateChecker.cs
+++ b/Source/Core/General/UpdateChecker.cs
@@ -16,6 +16,7 @@ namespace CodeImp.DoomBuilder
 
 		internal static void PerformCheck(bool verbosemode)
 		{
+			// Update check already runing?
 			if(worker != null && worker.IsBusy)
 			{
 				if(verbosemode)
@@ -30,6 +31,7 @@ namespace CodeImp.DoomBuilder
 				return;
 			}
 
+			// Start checking
 			verbose = verbosemode;
 			worker = new BackgroundWorker();
 			worker.DoWork += DoWork;
@@ -40,7 +42,23 @@ namespace CodeImp.DoomBuilder
 
 		private static void DoWork(object sender, DoWorkEventArgs e)
 		{
-			string url = GetDownloadUrl(Path.Combine(General.AppPath, "Updater.ini"));
+			string updaterpath = Path.Combine(General.AppPath, "Updater.exe");
+			if(!File.Exists(updaterpath))
+			{
+				errordesc = "Update check failed: '" + updaterpath + "' does not exist!";
+				e.Cancel = true;
+				return;
+			} 
+			
+			string inipath = Path.Combine(General.AppPath, "Updater.ini");
+			if(!File.Exists(inipath))
+			{
+				errordesc = "Update check failed: '" + inipath + "' does not exist!";
+				e.Cancel = true;
+				return;
+			}
+
+			string url = GetDownloadUrl(inipath);
 			if(string.IsNullOrEmpty(url))
 			{
 				errordesc = "Update check failed: failed to get update url from Updater.ini!";
@@ -50,6 +68,7 @@ namespace CodeImp.DoomBuilder
 
 			// Get local revision number
 			int localrev = General.ThisAssembly.GetName().Version.Revision;
+			int actuallocalrev = localrev;
 			if(!verbose) localrev = Math.Max(localrev, General.Settings.IgnoredRemoteRevision);
 
 			// Get remote revision number
@@ -80,7 +99,7 @@ namespace CodeImp.DoomBuilder
 			if(remoterev > localrev)
 			{
 				// Get changelog info
-				string changelog = GetChangelog(url, localrev);
+				string changelog = GetChangelog(url, actuallocalrev);
 
 				if(string.IsNullOrEmpty(changelog))
 				{
diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs
index 9e756cf0d53b00fd95e50a56afe28460c245dc36..3facf31424664c2266ec585eac6d4e7fea18fd4e 100644
--- a/Source/Core/Windows/MainForm.cs
+++ b/Source/Core/Windows/MainForm.cs
@@ -650,10 +650,7 @@ namespace CodeImp.DoomBuilder.Windows
 		protected override void OnFormClosing(FormClosingEventArgs e) 
 		{
 			base.OnFormClosing(e);
-
 			if(e.CloseReason == CloseReason.ApplicationExitCall) return;
-			if(General.Map != null && General.Map.Launcher.GameEngineRunning)
-				General.Map.Launcher.StopGameEngine(); //mxd
 
 			// Close the map
 			if(General.CloseMap())