Index: trunk/src/VBox/Main/AudioAdapterImpl.cpp
===================================================================
--- trunk/src/VBox/Main/AudioAdapterImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/AudioAdapterImpl.cpp	(revision 26156)
@@ -420,10 +420,4 @@
 }
 
-bool AudioAdapter::isReallyModified()
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    return mData.hasActualChanges();
-}
-
 /**
  *  @note Locks this object for writing.
Index: trunk/src/VBox/Main/BIOSSettingsImpl.cpp
===================================================================
--- trunk/src/VBox/Main/BIOSSettingsImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/BIOSSettingsImpl.cpp	(revision 26156)
@@ -525,10 +525,4 @@
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     return m->bd.isBackedUp();
-}
-
-bool BIOSSettings::isReallyModified()
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    return m->bd.hasActualChanges();
 }
 
Index: trunk/src/VBox/Main/MachineImpl.cpp
===================================================================
--- trunk/src/VBox/Main/MachineImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/MachineImpl.cpp	(revision 26156)
@@ -7035,37 +7035,31 @@
     ensureNoStateDependencies();
 
-    AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSessionMachine, E_FAIL);
-
-    BOOL currentStateModified = mData->mCurrentStateModified;
-    bool settingsModified;
-
-    if (!(aFlags & SaveS_ResetCurStateModified) && !currentStateModified)
-    {
-        /* We ignore changes to user data when setting mCurrentStateModified
-         * because the current state will not differ from the current snapshot
-         * if only user data has been changed (user data is shared by all
-         * snapshots). */
-        currentStateModified = isReallyModified (true /* aIgnoreUserData */);
-        settingsModified = mUserData.hasActualChanges() || currentStateModified;
-    }
-    else
-    {
-        if (aFlags & SaveS_ResetCurStateModified)
-            currentStateModified = FALSE;
-        settingsModified = isReallyModified();
-    }
+    AssertReturn(    getClassID() == clsidMachine
+                  || getClassID() == clsidSessionMachine,
+                 E_FAIL);
 
     HRESULT rc = S_OK;
+    bool fNeedsWrite = false;
 
     /* First, prepare to save settings. It will care about renaming the
      * settings directory and file if the machine name was changed and about
      * creating a new settings file if this is a new machine. */
-    bool isRenamed = false;
-    bool isNew = false;
-    rc = prepareSaveSettings(isRenamed, isNew);
+    bool fIsRenamed = false;
+    bool fIsNew = false;
+    rc = prepareSaveSettings(fIsRenamed, fIsNew);
     if (FAILED(rc)) return rc;
 
+    // keep a pointer to the current settings structures
+    settings::MachineConfigFile *pOldConfig = mData->m_pMachineConfigFile;
+
     try
     {
+        // make a fresh one to have everyone write stuff into
+        mData->m_pMachineConfigFile = new settings::MachineConfigFile(NULL);
+        mData->m_pMachineConfigFile->copyBaseFrom(*pOldConfig);
+
+        // deep copy extradata
+        mData->m_pMachineConfigFile->mapExtraDataItems = pOldConfig->mapExtraDataItems;
+
         mData->m_pMachineConfigFile->uuid = mData->mUuid;
         mData->m_pMachineConfigFile->strName = mUserData->mName;
@@ -7099,5 +7093,5 @@
 
         mData->m_pMachineConfigFile->strSnapshotFolder = mUserData->mSnapshotFolder;
-        mData->m_pMachineConfigFile->fCurrentStateModified = !!currentStateModified;
+        // mData->m_pMachineConfigFile->fCurrentStateModified is special, see below
         mData->m_pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
         mData->m_pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
@@ -7121,11 +7115,43 @@
         if (FAILED(rc)) throw rc;
 
-        // now spit it all out
-        mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
+        if (aFlags & SaveS_ResetCurStateModified)
+        {
+            // this gets set by restoreSnapshot()
+            mData->mCurrentStateModified = FALSE;
+            fNeedsWrite = true;     // always, no need to compare
+        }
+        else
+        {
+            if (!mData->mCurrentStateModified)
+            {
+                // do a deep compare of the settings that we just saved with the settings
+                // previously stored in the config file; this invokes MachineConfigFile::operator==
+                // which does a deep compare of all the settings, which is expensive but less expensive
+                // than writing out XML in vain
+                bool fAnySettingsChanged = (*mData->m_pMachineConfigFile == *pOldConfig);
+
+                // could still be modified if any settings changed
+                mData->mCurrentStateModified = fAnySettingsChanged;
+
+                fNeedsWrite = fAnySettingsChanged;
+            }
+            else
+                fNeedsWrite = true;
+        }
+
+        mData->m_pMachineConfigFile->fCurrentStateModified = !!mData->mCurrentStateModified;
+
+        if (fNeedsWrite)
+            // now spit it all out!
+            mData->m_pMachineConfigFile->write(mData->m_strConfigFileFull);
     }
     catch (HRESULT err)
     {
-        /* we assume that error info is set by the thrower */
+        // we assume that error info is set by the thrower
         rc = err;
+
+        // restore old config
+        delete mData->m_pMachineConfigFile;
+        mData->m_pMachineConfigFile = pOldConfig;
     }
     catch (...)
@@ -7137,10 +7163,8 @@
     {
         commit();
-
-        /* memorize the new modified state */
-        mData->mCurrentStateModified = currentStateModified;
-    }
-
-    if (settingsModified || (aFlags & SaveS_InformCallbacksAnyway))
+        delete pOldConfig;
+    }
+
+    if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
     {
         /* Fire the data change event, even on failure (since we've already
@@ -8236,62 +8260,4 @@
         (mUSBController && mUSBController->isModified()) ||
         (mBIOSSettings && mBIOSSettings->isModified());
-}
-
-/**
- * Returns the logical OR of data.hasActualChanges() of this and all child
- * objects.
- *
- * @param aIgnoreUserData       @c true to ignore changes to mUserData
- *
- * @note Locks objects for reading!
- */
-bool Machine::isReallyModified (bool aIgnoreUserData /* = false */)
-{
-    AutoCaller autoCaller(this);
-    AssertComRCReturn (autoCaller.rc(), false);
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
-        if (mNetworkAdapters [slot] && mNetworkAdapters [slot]->isReallyModified())
-            return true;
-
-    for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
-        if (mSerialPorts [slot] && mSerialPorts [slot]->isReallyModified())
-            return true;
-
-    for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
-        if (mParallelPorts [slot] && mParallelPorts [slot]->isReallyModified())
-            return true;
-
-    if (!mStorageControllers.isBackedUp())
-    {
-        /* see whether any of the devices has changed its data */
-        for (StorageControllerList::const_iterator
-             it = mStorageControllers->begin();
-             it != mStorageControllers->end();
-             ++it)
-        {
-            if ((*it)->isReallyModified())
-                return true;
-        }
-    }
-    else
-    {
-        if (mStorageControllers->size() != mStorageControllers.backedUpData()->size())
-            return true;
-    }
-
-    return
-        (!aIgnoreUserData && mUserData.hasActualChanges()) ||
-        mHWData.hasActualChanges() ||
-        mMediaData.hasActualChanges() ||
-        mStorageControllers.hasActualChanges() ||
-#ifdef VBOX_WITH_VRDP
-        (mVRDPServer && mVRDPServer->isReallyModified()) ||
-#endif
-        (mAudioAdapter && mAudioAdapter->isReallyModified()) ||
-        (mUSBController && mUSBController->isReallyModified()) ||
-        (mBIOSSettings && mBIOSSettings->isReallyModified());
 }
 
Index: trunk/src/VBox/Main/ParallelPortImpl.cpp
===================================================================
--- trunk/src/VBox/Main/ParallelPortImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/ParallelPortImpl.cpp	(revision 26156)
@@ -458,10 +458,4 @@
 }
 
-bool ParallelPort::isReallyModified()
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    return m->bd.hasActualChanges();
-}
-
 /**
  *  @note Locks this object for writing.
Index: trunk/src/VBox/Main/SerialPortImpl.cpp
===================================================================
--- trunk/src/VBox/Main/SerialPortImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/SerialPortImpl.cpp	(revision 26156)
@@ -571,10 +571,4 @@
 }
 
-bool SerialPort::isReallyModified()
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    return m->bd.hasActualChanges();
-}
-
 /**
  *  @note Locks this object for writing.
Index: trunk/src/VBox/Main/StorageControllerImpl.cpp
===================================================================
--- trunk/src/VBox/Main/StorageControllerImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/StorageControllerImpl.cpp	(revision 26156)
@@ -721,10 +721,4 @@
 }
 
-bool StorageController::isReallyModified()
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    return m->bd.hasActualChanges();
-}
-
 /** @note Locks objects for writing! */
 bool StorageController::rollback()
Index: trunk/src/VBox/Main/USBControllerImpl.cpp
===================================================================
--- trunk/src/VBox/Main/USBControllerImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/USBControllerImpl.cpp	(revision 26156)
@@ -738,69 +738,4 @@
 }
 
-/** @note Locks objects for reading! */
-bool USBController::isReallyModified()
-{
-    AutoCaller autoCaller(this);
-    AssertComRCReturn (autoCaller.rc(), false);
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (m->bd.hasActualChanges())
-        return true;
-
-#ifdef VBOX_WITH_USB
-    if (!m->llDeviceFilters.isBackedUp())
-    {
-        /* see whether any of filters has changed its data */
-        for (DeviceFilterList::const_iterator
-             it = m->llDeviceFilters->begin();
-             it != m->llDeviceFilters->end();
-             ++ it)
-        {
-            if ((*it)->isReallyModified())
-                return true;
-        }
-
-        return false;
-    }
-
-    if (m->llDeviceFilters->size() != m->llDeviceFilters.backedUpData()->size())
-        return true;
-
-    if (m->llDeviceFilters->size() == 0)
-        return false;
-
-    /* Make copies to speed up comparison */
-    DeviceFilterList devices = *m->llDeviceFilters.data();
-    DeviceFilterList backDevices = *m->llDeviceFilters.backedUpData();
-
-    DeviceFilterList::iterator it = devices.begin();
-    while (it != devices.end())
-    {
-        bool found = false;
-        DeviceFilterList::iterator thatIt = backDevices.begin();
-        while (thatIt != backDevices.end())
-        {
-            if ((*it)->getData() == (*thatIt)->getData())
-            {
-                backDevices.erase (thatIt);
-                found = true;
-                break;
-            }
-            else
-                ++ thatIt;
-        }
-        if (found)
-            it = devices.erase (it);
-        else
-            return false;
-    }
-
-    Assert (devices.size() == 0 && backDevices.size() == 0);
-#endif /* VBOX_WITH_USB */
-
-    return false;
-}
-
 /** @note Locks objects for writing! */
 bool USBController::rollback()
Index: trunk/src/VBox/Main/VirtualBoxImpl.cpp
===================================================================
--- trunk/src/VBox/Main/VirtualBoxImpl.cpp	(revision 26155)
+++ trunk/src/VBox/Main/VirtualBoxImpl.cpp	(revision 26156)
@@ -48,4 +48,5 @@
 #include <algorithm>
 #include <set>
+#include <vector>
 #include <memory> // for auto_ptr
 
Index: trunk/src/VBox/Main/include/AudioAdapterImpl.h
===================================================================
--- trunk/src/VBox/Main/include/AudioAdapterImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/AudioAdapterImpl.h	(revision 26156)
@@ -93,5 +93,4 @@
 
     bool isModified();
-    bool isReallyModified();
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/include/BIOSSettingsImpl.h
===================================================================
--- trunk/src/VBox/Main/include/BIOSSettingsImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/BIOSSettingsImpl.h	(revision 26156)
@@ -85,5 +85,4 @@
 
     bool isModified();
-    bool isReallyModified();
     void rollback();
     void commit();
Index: trunk/src/VBox/Main/include/MachineImpl.h
===================================================================
--- trunk/src/VBox/Main/include/MachineImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/MachineImpl.h	(revision 26156)
@@ -746,5 +746,4 @@
 
     bool isModified();
-    bool isReallyModified(bool aIgnoreUserData = false);
     void rollback(bool aNotify);
     void commit();
Index: trunk/src/VBox/Main/include/NetworkAdapterImpl.h
===================================================================
--- trunk/src/VBox/Main/include/NetworkAdapterImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/NetworkAdapterImpl.h	(revision 26156)
@@ -140,5 +140,4 @@
 
     bool isModified() { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); return mData.isBackedUp(); }
-    bool isReallyModified() { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); return mData.hasActualChanges(); }
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/include/ParallelPortImpl.h
===================================================================
--- trunk/src/VBox/Main/include/ParallelPortImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/ParallelPortImpl.h	(revision 26156)
@@ -79,5 +79,4 @@
 
     bool isModified();
-    bool isReallyModified();
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/include/SerialPortImpl.h
===================================================================
--- trunk/src/VBox/Main/include/SerialPortImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/SerialPortImpl.h	(revision 26156)
@@ -85,5 +85,4 @@
 
     bool isModified();
-    bool isReallyModified();
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/include/StorageControllerImpl.h
===================================================================
--- trunk/src/VBox/Main/include/StorageControllerImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/StorageControllerImpl.h	(revision 26156)
@@ -90,5 +90,4 @@
 
     bool isModified();
-    bool isReallyModified();
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/include/USBControllerImpl.h
===================================================================
--- trunk/src/VBox/Main/include/USBControllerImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/USBControllerImpl.h	(revision 26156)
@@ -84,5 +84,4 @@
 
     bool isModified();
-    bool isReallyModified();
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/include/VRDPServerImpl.h
===================================================================
--- trunk/src/VBox/Main/include/VRDPServerImpl.h	(revision 26155)
+++ trunk/src/VBox/Main/include/VRDPServerImpl.h	(revision 26156)
@@ -112,5 +112,4 @@
 
     bool isModified() { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); return mData.isBackedUp(); }
-    bool isReallyModified() { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); return mData.hasActualChanges(); }
     bool rollback();
     void commit();
Index: trunk/src/VBox/Main/xml/Settings.cpp
===================================================================
--- trunk/src/VBox/Main/xml/Settings.cpp	(revision 26155)
+++ trunk/src/VBox/Main/xml/Settings.cpp	(revision 26156)
@@ -147,4 +147,13 @@
                                                         // or SettingsVersion_Null if none
 
+    void copyFrom(const Data &d)
+    {
+        strFilename = d.strFilename;
+        fFileExists = d.fFileExists;
+        strSettingsVersionFull = d.strSettingsVersionFull;
+        sv = d.sv;
+        svRead = d.svRead;
+    }
+
     void cleanup()
     {
@@ -704,4 +713,44 @@
 }
 
+/**
+ * Copies the base variables from another instance. Used by Machine::saveSettings
+ * so that the settings version does not get lost when a copy of the Machine settings
+ * file is made to see if settings have actually changed.
+ * @param b
+ */
+void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
+{
+    m->copyFrom(*b.m);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Structures shared between Machine XML and VirtualBox.xml
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
+{
+    return (    (this == &u)
+             || (    (strName           == u.strName)
+                  && (fActive           == u.fActive)
+                  && (strVendorId       == u.strVendorId)
+                  && (strProductId      == u.strProductId)
+                  && (strRevision       == u.strRevision)
+                  && (strManufacturer   == u.strManufacturer)
+                  && (strProduct        == u.strProduct)
+                  && (strSerialNumber   == u.strSerialNumber)
+                  && (strPort           == u.strPort)
+                  && (action            == u.action)
+                  && (strRemote         == u.strRemote)
+                  && (ulMaskedInterfaces == u.ulMaskedInterfaces)
+                )
+           );
+}
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1242,4 +1291,153 @@
 }
 
+////////////////////////////////////////////////////////////////////////////////
+//
+// Machine XML structures
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool VRDPSettings::operator==(const VRDPSettings& v) const
+{
+    return (    (this == &v)
+             || (    (fEnabled                  == v.fEnabled)
+                  && (strPort                   == v.strPort)
+                  && (strNetAddress             == v.strNetAddress)
+                  && (authType                  == v.authType)
+                  && (ulAuthTimeout             == v.ulAuthTimeout)
+                  && (fAllowMultiConnection     == v.fAllowMultiConnection)
+                  && (fReuseSingleConnection    == v.fReuseSingleConnection)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool BIOSSettings::operator==(const BIOSSettings &d) const
+{
+    return (    (this == &d)
+             || (    fACPIEnabled        == d.fACPIEnabled
+                  && fIOAPICEnabled      == d.fIOAPICEnabled
+                  && fLogoFadeIn         == d.fLogoFadeIn
+                  && fLogoFadeOut        == d.fLogoFadeOut
+                  && ulLogoDisplayTime   == d.ulLogoDisplayTime
+                  && strLogoImagePath    == d.strLogoImagePath
+                  && biosBootMenuMode    == d.biosBootMenuMode
+                  && fPXEDebugEnabled    == d.fPXEDebugEnabled
+                  && llTimeOffset        == d.llTimeOffset)
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool USBController::operator==(const USBController &u) const
+{
+    return (    (this == &u)
+                 || (    (fEnabled          == u.fEnabled)
+                      && (fEnabledEHCI      == u.fEnabledEHCI)
+                      && (llDeviceFilters   == u.llDeviceFilters)
+                    )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool NetworkAdapter::operator==(const NetworkAdapter &n) const
+{
+    return (    (this == &n)
+             || (    (ulSlot            == n.ulSlot)
+                  && (type              == n.type)
+                  && (fEnabled          == n.fEnabled)
+                  && (strMACAddress     == n.strMACAddress)
+                  && (fCableConnected   == n.fCableConnected)
+                  && (ulLineSpeed       == n.ulLineSpeed)
+                  && (fTraceEnabled     == n.fTraceEnabled)
+                  && (strTraceFile      == n.strTraceFile)
+                  && (mode              == n.mode)
+                  && (strName           == n.strName)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool SerialPort::operator==(const SerialPort &s) const
+{
+    return (    (this == &s)
+             || (    (ulSlot            == s.ulSlot)
+                  && (fEnabled          == s.fEnabled)
+                  && (ulIOBase          == s.ulIOBase)
+                  && (ulIRQ             == s.ulIRQ)
+                  && (portMode          == s.portMode)
+                  && (strPath           == s.strPath)
+                  && (fServer           == s.fServer)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool ParallelPort::operator==(const ParallelPort &s) const
+{
+    return (    (this == &s)
+             || (    (ulSlot            == s.ulSlot)
+                  && (fEnabled          == s.fEnabled)
+                  && (ulIOBase          == s.ulIOBase)
+                  && (ulIRQ             == s.ulIRQ)
+                  && (strPath           == s.strPath)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool SharedFolder::operator==(const SharedFolder &g) const
+{
+    return (    (this == &g)
+             || (    (strName       == g.strName)
+                  && (strHostPath   == g.strHostPath)
+                  && (fWritable     == g.fWritable)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool GuestProperty::operator==(const GuestProperty &g) const
+{
+    return (    (this == &g)
+             || (    (strName           == g.strName)
+                  && (strValue          == g.strValue)
+                  && (timestamp         == g.timestamp)
+                  && (strFlags          == g.strFlags)
+                )
+           );
+}
+
 // use a define for the platform-dependent default value of
 // hwvirt exclusivity, since we'll need to check that value
@@ -1285,4 +1483,210 @@
     fPAE = true;
 #endif
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool Hardware::operator==(const Hardware& h) const
+{
+    return (    (this == &h)
+             || (    (strVersion                == h.strVersion)
+                  && (uuid                      == h.uuid)
+                  && (fHardwareVirt             == h.fHardwareVirt)
+                  && (fHardwareVirtExclusive    == h.fHardwareVirtExclusive)
+                  && (fNestedPaging             == h.fNestedPaging)
+                  && (fVPID                     == h.fVPID)
+                  && (fSyntheticCpu             == h.fSyntheticCpu)
+                  && (fPAE                      == h.fPAE)
+                  && (cCPUs                     == h.cCPUs)
+                  && (fCpuHotPlug               == h.fCpuHotPlug)
+                  && (llCpus                    == h.llCpus)
+                  && (llCpuIdLeafs              == h.llCpuIdLeafs)
+                  && (ulMemorySizeMB            == h.ulMemorySizeMB)
+                  && (mapBootOrder              == h.mapBootOrder)
+                  && (ulVRAMSizeMB              == h.ulVRAMSizeMB)
+                  && (cMonitors                 == h.cMonitors)
+                  && (fAccelerate3D             == h.fAccelerate3D)
+                  && (fAccelerate2DVideo        == h.fAccelerate2DVideo)
+                  && (firmwareType              == h.firmwareType)
+                  && (vrdpSettings              == h.vrdpSettings)
+                  && (biosSettings              == h.biosSettings)
+                  && (usbController             == h.usbController)
+                  && (llNetworkAdapters         == h.llNetworkAdapters)
+                  && (llSerialPorts             == h.llSerialPorts)
+                  && (llParallelPorts           == h.llParallelPorts)
+                  && (audioAdapter              == h.audioAdapter)
+                  && (llSharedFolders           == h.llSharedFolders)
+                  && (clipboardMode             == h.clipboardMode)
+                  && (ulMemoryBalloonSize       == h.ulMemoryBalloonSize)
+                  && (ulStatisticsUpdateInterval == h.ulStatisticsUpdateInterval)
+                  && (llGuestProperties         == h.llGuestProperties)
+                  && (strNotificationPatterns   == h.strNotificationPatterns)
+                )
+            );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool AttachedDevice::operator==(const AttachedDevice &a) const
+{
+    return (    (this == &a)
+             || (    (deviceType                == a.deviceType)
+                  && (fPassThrough              == a.fPassThrough)
+                  && (lPort                     == a.lPort)
+                  && (lDevice                   == a.lDevice)
+                  && (uuid                      == a.uuid)
+                  && (strHostDriveSrc           == a.strHostDriveSrc)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool StorageController::operator==(const StorageController &s) const
+{
+    return (    (this == &s)
+             || (    (strName                   == s.strName)
+                  && (storageBus                == s.storageBus)
+                  && (controllerType            == s.controllerType)
+                  && (ulPortCount               == s.ulPortCount)
+                  && (ulInstance                == s.ulInstance)
+                  && (lIDE0MasterEmulationPort  == s.lIDE0MasterEmulationPort)
+                  && (lIDE0SlaveEmulationPort   == s.lIDE0SlaveEmulationPort)
+                  && (lIDE1MasterEmulationPort  == s.lIDE1MasterEmulationPort)
+                  && (lIDE1SlaveEmulationPort   == s.lIDE1SlaveEmulationPort)
+                  && (llAttachedDevices         == s.llAttachedDevices)
+                )
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool Storage::operator==(const Storage &s) const
+{
+    return (    (this == &s)
+             || (llStorageControllers == s.llStorageControllers)            // deep compare
+           );
+}
+
+/**
+ * Comparison operator. This gets called from MachineConfigFile::operator==,
+ * which in turn gets called from Machine::saveSettings to figure out whether
+ * machine settings have really changed and thus need to be written out to disk.
+ */
+bool Snapshot::operator==(const Snapshot &s) const
+{
+    return (    (this == &s)
+             || (    (uuid                  == s.uuid)
+                  && (strName               == s.strName)
+                  && (strDescription        == s.strDescription)
+                  && (RTTimeSpecIsEqual(&timestamp, &s.timestamp))
+                  && (strStateFile          == s.strStateFile)
+                  && (hardware              == s.hardware)                  // deep compare
+                  && (storage               == s.storage)                   // deep compare
+                  && (llChildSnapshots      == s.llChildSnapshots)          // deep compare
+                )
+           );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MachineConfigFile
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Constructor.
+ *
+ * If pstrFilename is != NULL, this reads the given settings file into the member
+ * variables and various substructures and lists. Otherwise, the member variables
+ * are initialized with default values.
+ *
+ * Throws variants of xml::Error for I/O, XML and logical content errors, which
+ * the caller should catch; if this constructor does not throw, then the member
+ * variables contain meaningful values (either from the file or defaults).
+ *
+ * @param strFilename
+ */
+MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
+    : ConfigFileBase(pstrFilename),
+      fNameSync(true),
+      fTeleporterEnabled(false),
+      uTeleporterPort(0),
+      fRTCUseUTC(false),
+      fCurrentStateModified(true),
+      fAborted(false)
+{
+    RTTimeNow(&timeLastStateChange);
+
+    if (pstrFilename)
+    {
+        // the ConfigFileBase constructor has loaded the XML file, so now
+        // we need only analyze what is in there
+
+        xml::NodesLoop nlRootChildren(*m->pelmRoot);
+        const xml::ElementNode *pelmRootChild;
+        while ((pelmRootChild = nlRootChildren.forAllNodes()))
+        {
+            if (pelmRootChild->nameEquals("Machine"))
+                readMachine(*pelmRootChild);
+        }
+
+        // clean up memory allocated by XML engine
+        clearDocument();
+    }
+}
+
+/**
+ * Comparison operator. This gets called from Machine::saveSettings to figure out
+ * whether machine settings have really changed and thus need to be written out to disk.
+ *
+ * Even though this is called operator==, this does NOT compare all fields; the "equals"
+ * should be understood as "has the same machine config as". The following fields are
+ * NOT compared:
+ *      -- settings versions and file names inherited from ConfigFileBase;
+ *      -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
+ *
+ * The "deep" comparisons marked below will invoke the operator== functions of the
+ * structs defined in this file, which may in turn go into comparing lists of
+ * other structures. As a result, invoking this can be expensive, but it's
+ * less expensive than writing out XML to disk.
+ */
+bool MachineConfigFile::operator==(const MachineConfigFile &c) const
+{
+    return (    (this == &c)
+            || (    (uuid                       == c.uuid)
+                 && (strName                    == c.strName)
+                 && (fNameSync                  == c.fNameSync)
+                 && (strDescription             == c.strDescription)
+                 && (strOsType                  == c.strOsType)
+                 && (strStateFile               == c.strStateFile)
+                 && (uuidCurrentSnapshot        == c.uuidCurrentSnapshot)
+                 && (strSnapshotFolder          == c.strSnapshotFolder)
+                 && (fTeleporterEnabled         == c.fTeleporterEnabled)
+                 && (uTeleporterPort            == c.uTeleporterPort)
+                 && (strTeleporterAddress       == c.strTeleporterAddress)
+                 && (strTeleporterPassword      == c.strTeleporterPassword)
+                 && (fRTCUseUTC                 == c.fRTCUseUTC)
+                 // skip fCurrentStateModified!
+                 && (RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange))
+                 && (fAborted                   == c.fAborted)
+                 && (hardwareMachine            == c.hardwareMachine)       // this one's deep
+                 && (storageMachine             == c.storageMachine)        // this one's deep
+                 && (mapExtraDataItems          == c.mapExtraDataItems)     // this one's deep
+                 && (llFirstSnapshot            == c.llFirstSnapshot)       // this one's deep
+               )
+           );
 }
 
@@ -2417,52 +2821,4 @@
 }
 
-////////////////////////////////////////////////////////////////////////////////
-//
-// MachineConfigFile
-//
-////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Constructor.
- *
- * If pstrFilename is != NULL, this reads the given settings file into the member
- * variables and various substructures and lists. Otherwise, the member variables
- * are initialized with default values.
- *
- * Throws variants of xml::Error for I/O, XML and logical content errors, which
- * the caller should catch; if this constructor does not throw, then the member
- * variables contain meaningful values (either from the file or defaults).
- *
- * @param strFilename
- */
-MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
-    : ConfigFileBase(pstrFilename),
-      fNameSync(true),
-      fTeleporterEnabled(false),
-      uTeleporterPort(0),
-      fRTCUseUTC(false),
-      fCurrentStateModified(true),
-      fAborted(false)
-{
-    RTTimeNow(&timeLastStateChange);
-
-    if (pstrFilename)
-    {
-        // the ConfigFileBase constructor has loaded the XML file, so now
-        // we need only analyze what is in there
-
-        xml::NodesLoop nlRootChildren(*m->pelmRoot);
-        const xml::ElementNode *pelmRootChild;
-        while ((pelmRootChild = nlRootChildren.forAllNodes()))
-        {
-            if (pelmRootChild->nameEquals("Machine"))
-                readMachine(*pelmRootChild);
-        }
-
-        // clean up memory allocated by XML engine
-        clearDocument();
-    }
-}
-
 /**
  * Creates a <Hardware> node under elmParent and then writes out the XML
