Index: common/mptOS.cpp
===================================================================
--- common/mptOS.cpp	(revision 10963)
+++ common/mptOS.cpp	(working copy)
@@ -589,6 +589,20 @@
 }
 
 
+std::vector<Architecture> GetSupportedProcessArchitectures(Architecture host)
+{
+	std::vector<Architecture> result;
+	for(const auto & entry : hostArchitectureCanRun)
+	{
+		if(entry.Host == host)
+		{
+			result.push_back(entry.Process);
+		}
+	}
+	return result;
+}
+
+
 #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
 
 
@@ -629,6 +643,18 @@
 
 #endif // MPT_OS_WINDOWS
 
+uint64 GetSystemMemorySize()
+{
+	MEMORYSTATUSEX memoryStatus;
+	MemsetZero(memoryStatus);
+	memoryStatus.dwLength = sizeof(MEMORYSTATUSEX);
+	if(GlobalMemoryStatusEx(&memoryStatus) == 0)
+	{
+		return 0;
+	}
+	return memoryStatus.ullTotalPhys;
+}
+
 static bool SystemIsWine(bool allowDetection = true)
 {
 	#if MPT_OS_WINDOWS
Index: common/mptOS.h
===================================================================
--- common/mptOS.h	(revision 10963)
+++ common/mptOS.h	(working copy)
@@ -183,11 +183,15 @@
 
 EmulationLevel HostCanRun(Architecture host, Architecture process) noexcept;
 
+std::vector<Architecture> GetSupportedProcessArchitectures(Architecture host);
+
 #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
 
 
 #if defined(MODPLUG_TRACKER)
 
+uint64 GetSystemMemorySize();
+
 void PreventWineDetection();
 
 bool IsOriginal();
Index: common/versionNumber.h
===================================================================
--- common/versionNumber.h	(revision 10963)
+++ common/versionNumber.h	(working copy)
@@ -21,7 +21,7 @@
 #define VER_MAJORMAJOR  1
 #define VER_MAJOR      28
 #define VER_MINOR      00
-#define VER_MINORMINOR 37
+#define VER_MINORMINOR 38
 
 //Numerical value of the version.
 #define MPT_VERSION_CURRENT MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR)
Index: mptrack/Mptrack.cpp
===================================================================
--- mptrack/Mptrack.cpp	(revision 10963)
+++ mptrack/Mptrack.cpp	(working copy)
@@ -1071,8 +1071,25 @@
 		font.size = Clamp(Util::GetDPIy(m_pMainWnd->m_hWnd) / 96 - 1, 0, 9);
 		TrackerSettings::Instance().patternFont = font;
 		new WelcomeDlg(m_pMainWnd);
+
+		TrackerSettings::Instance().UpdateStatisticsConsentAsked = true;
+
 	} else
 	{
+
+		// ask if user wants to contribute system statistics
+		if(!TrackerSettings::Instance().UpdateStatisticsConsentAsked)
+		{
+			TrackerSettings::Instance().UpdateStatistics = (ConfirmAnswer::cnfYes == Reporting::Confirm(
+				MPT_USTRING("Do you want to contribute to OpenMPT by providing system statistics?\r\n") +
+				MPT_USTRING("\r\n") +
+				mpt::String::Replace(CUpdateCheck::GetStatisticsUserInformation(false), MPT_USTRING("\n"), MPT_USTRING("\r\n")) + MPT_USTRING("\r\n") +
+				MPT_USTRING("\r\n") +
+				mpt::format(MPT_USTRING("This option was previously %1 on your system.\r\n"))(TrackerSettings::Instance().UpdateStatistics ? MPT_USTRING("enabled") : MPT_USTRING("disabled")),
+				false, !TrackerSettings::Instance().UpdateStatistics.Get()));
+			TrackerSettings::Instance().UpdateStatisticsConsentAsked = true;
+		}
+
 		// Update check
 		CUpdateCheck::DoAutoUpdateCheck();
 
Index: mptrack/mptrack.rc
===================================================================
--- mptrack/mptrack.rc	(revision 10963)
+++ mptrack/mptrack.rc	(working copy)
@@ -244,22 +244,25 @@
 CAPTION "Update"
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
 BEGIN
-    GROUPBOX        "Check for Updates",IDC_STATIC,6,6,276,66
-    CONTROL         "&Never",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON,12,18,240,8
-    CONTROL         "&Daily",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,12,30,240,8
-    CONTROL         "&Weekly (recommended)",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,12,42,240,8
-    CONTROL         "&Monthly",IDC_RADIO4,"Button",BS_AUTORADIOBUTTON,12,54,240,8
-    GROUPBOX        "Privacy Settings",IDC_STATIC,6,78,276,54
-    CONTROL         "&Allow us to collect basic update statistics",IDC_CHECK1,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,90,246,12
-    LTEXT           "If enabled, a randomized user ID is created and transmitted with every update check. This ID can not be linked to you or your computer in any way.",IDC_STATIC,12,102,264,24
-    GROUPBOX        "Advanced Settings",IDC_STATIC,6,138,276,60
-    LTEXT           "&Update server URL:",IDC_STATIC,12,150,186,8
-    EDITTEXT        IDC_EDIT1,12,162,264,12,ES_AUTOHSCROLL
-    PUSHBUTTON      "&Reset",IDC_BUTTON2,222,146,54,12
-    LTEXT           "Do not change this unless you are absolutely sure of what you are doing.",IDC_STATIC,12,180,264,12
-    PUSHBUTTON      "&Check for Updates",IDC_BUTTON1,6,204,84,18
-    LTEXT           "",IDC_LASTUPDATE,6,228,276,48
+    CONTROL         "Enable online Update &Check",IDC_CHECK_UPDATEENABLED,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,6,105,10
+    GROUPBOX        "Update Channel",IDC_STATIC_UDATECHANNEL,6,18,276,54
+    CONTROL         "release: official stable released versions only (recommended)",IDC_RADIO1,
+                    "Button",BS_AUTORADIOBUTTON,12,30,211,10
+    CONTROL         "next: previews of the next official stable release",IDC_RADIO2,
+                    "Button",BS_AUTORADIOBUTTON,12,42,171,10
+    CONTROL         "development: bleeding-edge development versions",IDC_RADIO3,
+                    "Button",BS_AUTORADIOBUTTON,12,54,179,10
+    GROUPBOX        "Check for Updates",IDC_STATIC_UPDATECHECK,6,72,276,48
+    LTEXT           "&Automatically check on program start:",IDC_STATIC_UPDATEFREQUENCY,12,84,126,12,SS_CENTERIMAGE
+    COMBOBOX        IDC_COMBO_UPDATEFREQUENCY,138,84,42,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    PUSHBUTTON      "&Check now...",IDC_BUTTON1,216,84,60,12
+    LTEXT           "",IDC_LASTUPDATE,12,102,264,12
+    GROUPBOX        "Privacy Settings",IDC_STATIC_UPDATEPRIVACY,6,120,276,156
+    CONTROL         "&Allow OpenMPT to collect basic statistics about your system configuration",IDC_CHECK1,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,132,264,12
+    LTEXT           "",IDC_STATIC_UPDATEPRIVACYTEXT,12,144,264,36
+    EDITTEXT        IDC_EDIT_STATISTICS,12,180,264,90,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL
 END
 
 IDD_CLOSEDOCUMENTS DIALOGEX 0, 0, 370, 197
@@ -374,23 +377,26 @@
     PUSHBUTTON      "&Cancel",IDCANCEL,138,24,50,14
 END
 
-IDD_WECLOME DIALOGEX 0, 0, 256, 137
+IDD_WECLOME DIALOGEX 0, 0, 257, 261
 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Welcome to OpenMPT!"
 FONT 8, "MS Shell Dlg", 400, 0, 0x1
 BEGIN
-    DEFPUSHBUTTON   "&OK",IDOK,198,116,50,14
+    DEFPUSHBUTTON   "&OK",IDOK,198,236,50,14
     LTEXT           "Please review the following settings before using this software:",IDC_STATIC,6,6,246,8
     CONTROL         "&Automatically check for new versions of OpenMPT",IDC_CHECK1,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,24,246,10
+    CONTROL         "Help OpenMPT development by providing basic s&tatistics",IDC_CHECK3,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,42,234,10
+    LTEXT           "Static",IDC_STATIC_WELCOME_STATISTICS,30,54,216,102
     CONTROL         "&Use a big font in the pattern editor",IDC_CHECK2,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,42,246,10
-    LTEXT           "&Default keyboard scheme:",IDC_STATIC,6,63,108,8
-    COMBOBOX        IDC_COMBO1,114,60,132,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    LTEXT           "Scan for existing VST plugins in the following location:",IDC_STATIC,6,82,172,8
-    PUSHBUTTON      "&Scan",IDC_BUTTON2,198,79,50,14
-    EDITTEXT        IDC_EDIT1,6,95,240,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
-    PUSHBUTTON      "&More Settings",IDC_BUTTON1,6,116,60,14
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,162,246,10
+    LTEXT           "&Default keyboard scheme:",IDC_STATIC,6,183,108,8
+    COMBOBOX        IDC_COMBO1,114,180,132,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Scan for existing VST plugins in the following location:",IDC_STATIC,6,202,172,8
+    PUSHBUTTON      "&Scan",IDC_BUTTON2,198,199,50,14
+    EDITTEXT        IDC_EDIT1,6,215,240,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+    PUSHBUTTON      "&More Settings",IDC_BUTTON1,6,236,60,14
 END
 
 IDD_UPDATE DIALOGEX 0, 0, 196, 138
@@ -751,9 +757,9 @@
     IDD_WECLOME, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 249
+        RIGHTMARGIN, 250
         TOPMARGIN, 7
-        BOTTOMMARGIN, 130
+        BOTTOMMARGIN, 254
     END
 
     IDD_UPDATE, DIALOG
@@ -931,7 +937,17 @@
     0, 100, 100, 0
 END
 
+IDD_OPTIONS_UPDATE AFX_DIALOG_LAYOUT
+BEGIN
+    0
+END
 
+IDD_WECLOME AFX_DIALOG_LAYOUT
+BEGIN
+    0
+END
+
+
 /////////////////////////////////////////////////////////////////////////////
 //
 // Dialog Info
Index: mptrack/resource.h
===================================================================
--- mptrack/resource.h	(revision 10963)
+++ mptrack/resource.h	(working copy)
@@ -975,6 +975,14 @@
 #define IDC_BUTTON_TUNING_REMOVE        2501
 #define IDC_STATIC_WINE_RTAUDIO         2502
 #define IDC_COMBO_WINE_RTAUDIO          2503
+#define IDC_STATIC_UPDATECHECK          2504
+#define IDC_STATIC_UPDATEPRIVACY        2505
+#define IDC_STATIC_UDATECHANNEL         2506
+#define IDC_COMBO_UPDATEFREQUENCY       2507
+#define IDC_STATIC_UPDATEFREQUENCY      2508
+#define IDC_CHECK_UPDATEENABLED         2509
+#define IDC_STATIC_UPDATEPRIVACYTEXT    2510
+#define IDC_STATIC_WELCOME_STATISTICS   2511
 #define ID_FILE_NEWMOD                  32771
 #define ID_FILE_NEWXM                   32772
 #define ID_FILE_NEWS3M                  32773
@@ -1269,9 +1277,9 @@
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_3D_CONTROLS                     1
-#define _APS_NEXT_RESOURCE_VALUE        542
+#define _APS_NEXT_RESOURCE_VALUE        544
 #define _APS_NEXT_COMMAND_VALUE         44646
-#define _APS_NEXT_CONTROL_VALUE         2504
+#define _APS_NEXT_CONTROL_VALUE         2512
 #define _APS_NEXT_SYMED_VALUE           901
 #endif
 #endif
Index: mptrack/TrackerSettings.cpp
===================================================================
--- mptrack/TrackerSettings.cpp	(revision 10963)
+++ mptrack/TrackerSettings.cpp	(working copy)
@@ -331,10 +331,19 @@
 	, vstHostVendorString(conf, MPT_USTRING("VST Plugins"), MPT_USTRING("HostVendorString"), "OpenMPT project")
 	, vstHostVendorVersion(conf, MPT_USTRING("VST Plugins"), MPT_USTRING("HostVendorVersion"), Version::Current().GetRawVersion())
 	// Update
+	, UpdateEnabled(conf, MPT_USTRING("Update"), MPT_USTRING("Enabled"), true)
 	, UpdateLastUpdateCheck(conf, MPT_USTRING("Update"), MPT_USTRING("LastUpdateCheck"), mpt::Date::Unix(time_t()))
-	, UpdateUpdateCheckPeriod(conf, MPT_USTRING("Update"), MPT_USTRING("UpdateCheckPeriod"), 7)
-	, UpdateUpdateURL(conf, MPT_USTRING("Update"), MPT_USTRING("UpdateURL"), CUpdateCheck::GetDefaultUpdateURL())
-	, UpdateSendGUID(conf, MPT_USTRING("Update"), MPT_USTRING("SendGUID"), true)
+	, UpdateUpdateCheckPeriod_DEPRECATED(conf, MPT_USTRING("Update"), MPT_USTRING("UpdateCheckPeriod"), 7)
+	, UpdateIntervalDays(conf, MPT_USTRING("Update"), MPT_USTRING("UpdateCheckPeriodDays"), 7)
+	, UpdateChannel(conf, MPT_USTRING("Update"), MPT_USTRING("Channel"), UpdateChannelRelease)
+	, UpdateUpdateURL_DEPRECATED(conf, MPT_USTRING("Update"), MPT_USTRING("UpdateURL"), CUpdateCheck::GetDefaultChannelReleaseURL())
+	, UpdateChannelReleaseURL(conf, MPT_USTRING("Update"), MPT_USTRING("ChannelReleaseURL"), CUpdateCheck::GetDefaultChannelReleaseURL())
+	, UpdateChannelNextURL(conf, MPT_USTRING("Update"), MPT_USTRING("ChannelStableURL"), CUpdateCheck::GetDefaultChannelNextURL())
+	, UpdateChannelDevelopmentURL(conf, MPT_USTRING("Update"), MPT_USTRING("ChannelDevelopmentURL"), CUpdateCheck::GetDefaultChannelDevelopmentURL())
+	, UpdateAPIURL(conf, MPT_USTRING("Update"), MPT_USTRING("APIURL"), CUpdateCheck::GetDefaultAPIURL())
+	, UpdateStatisticsConsentAsked(conf, MPT_USTRING("Update"), MPT_USTRING("StatistisConsentAsked"), false)
+	, UpdateStatistics(conf, MPT_USTRING("Update"), MPT_USTRING("Statistis"), false)
+	, UpdateSendGUID_DEPRECATED(conf, MPT_USTRING("Update"), MPT_USTRING("SendGUID"), false)
 	, UpdateShowUpdateHint(conf, MPT_USTRING("Update"), MPT_USTRING("ShowUpdateHint"), true)
 	, UpdateSuggestDifferentBuildVariant(conf, MPT_USTRING("Update"), MPT_USTRING("SuggestDifferentBuildVariant"), true)
 	, UpdateIgnoreVersion(conf, MPT_USTRING("Update"), MPT_USTRING("IgnoreVersion"), _T(""))
@@ -644,6 +653,47 @@
 		m_dwPatternSetup &= ~0x200;
 	}
 
+	// Update
+	if(storedVersion < MAKE_VERSION_NUMERIC(1,28,00,38))
+	{
+		if(UpdateUpdateCheckPeriod_DEPRECATED <= 0)
+		{
+			UpdateEnabled = true;
+			UpdateIntervalDays = -1;
+		} else
+		{
+			UpdateEnabled = true;
+			UpdateIntervalDays = UpdateUpdateCheckPeriod_DEPRECATED.Get();
+		}
+		if(UpdateUpdateURL_DEPRECATED.Get() == MPT_USTRING(""))
+		{
+			UpdateChannel = UpdateChannelRelease;
+		} else if(UpdateUpdateURL_DEPRECATED.Get() == MPT_USTRING("http://www.soal.org/openmpt/OpenMPTversionCheck.php5"))
+		{
+			UpdateChannel = UpdateChannelRelease;
+		} else if(UpdateUpdateURL_DEPRECATED.Get() == MPT_USTRING("http://update.openmpt.org/check/$VERSION/$GUID"))
+		{
+			UpdateChannel = UpdateChannelRelease;
+		} else if(UpdateUpdateURL_DEPRECATED.Get() == MPT_USTRING("https://update.openmpt.org/check/$VERSION/$GUID"))
+		{
+			UpdateChannel = UpdateChannelRelease;
+		} else if(UpdateUpdateURL_DEPRECATED.Get() == MPT_USTRING("http://update.openmpt.org/check/testing/$VERSION/$GUID"))
+		{
+			UpdateChannel = UpdateChannelDevelopment;
+		} else if(UpdateUpdateURL_DEPRECATED.Get() == MPT_USTRING("https://update.openmpt.org/check/testing/$VERSION/$GUID"))
+		{
+			UpdateChannel = UpdateChannelDevelopment;
+		} else
+		{
+			UpdateChannel = UpdateChannelDevelopment;
+			UpdateChannelDevelopmentURL = UpdateUpdateURL_DEPRECATED.Get();
+		}
+		UpdateStatistics = UpdateSendGUID_DEPRECATED.Get();
+		conf.Forget(UpdateUpdateCheckPeriod_DEPRECATED.GetPath());
+		conf.Forget(UpdateUpdateURL_DEPRECATED.GetPath());
+		conf.Forget(UpdateSendGUID_DEPRECATED.GetPath());
+	}
+
 	// Effects
 #ifndef NO_EQ
 	FixupEQ(m_EqSettings);
Index: mptrack/TrackerSettings.h
===================================================================
--- mptrack/TrackerSettings.h	(revision 10963)
+++ mptrack/TrackerSettings.h	(working copy)
@@ -817,10 +817,19 @@
 
 	// Update
 
+	Setting<bool> UpdateEnabled;
 	Setting<mpt::Date::Unix> UpdateLastUpdateCheck;
-	Setting<int32> UpdateUpdateCheckPeriod;
-	Setting<mpt::ustring> UpdateUpdateURL;
-	Setting<bool> UpdateSendGUID;
+	Setting<int32> UpdateUpdateCheckPeriod_DEPRECATED;
+	Setting<int32> UpdateIntervalDays;
+	Setting<uint32> UpdateChannel;
+	Setting<mpt::ustring> UpdateUpdateURL_DEPRECATED;
+	Setting<mpt::ustring> UpdateChannelReleaseURL;
+	Setting<mpt::ustring> UpdateChannelNextURL;
+	Setting<mpt::ustring> UpdateChannelDevelopmentURL;
+	Setting<mpt::ustring> UpdateAPIURL;
+	Setting<bool> UpdateStatisticsConsentAsked;
+	Setting<bool> UpdateStatistics;
+	Setting<bool> UpdateSendGUID_DEPRECATED;
 	Setting<bool> UpdateShowUpdateHint;
 	Setting<bool> UpdateSuggestDifferentBuildVariant;
 	Setting<CString> UpdateIgnoreVersion;
Index: mptrack/UpdateCheck.cpp
===================================================================
--- mptrack/UpdateCheck.cpp	(revision 10963)
+++ mptrack/UpdateCheck.cpp	(working copy)
@@ -13,6 +13,7 @@
 #include "BuildVariants.h"
 #include "../common/version.h"
 #include "../common/misc_util.h"
+#include "../common/mptStringBuffer.h"
 #include "Mptrack.h"
 #include "TrackerSettings.h"
 // Setup dialog stuff
@@ -19,6 +20,7 @@
 #include "Mainfrm.h"
 #include "../common/mptThread.h"
 #include "HTTP.h"
+#include "../misc/JSON.h"
 
 
 OPENMPT_NAMESPACE_BEGIN
@@ -83,13 +85,46 @@
 
 
 
+mpt::ustring CUpdateCheck::GetStatisticsUserInformation(bool shortText)
+{
+	if(shortText)
+	{
+		return MPT_USTRING("A randomized user ID is created and transmitted alongside. This ID can only be linked to you or your computer by this very ID, which is stored solely on your computer. OpenMPT will use this information to gather usage statistics and to plan removal of support for older systems. The following information will be sent:");
+	} else
+	{
+		return MPT_USTRING("")
+			+ MPT_USTRING("When checking for updates, OpenMPT can additionally collect some basic statistical information.") + MPT_USTRING("\n")
+			+ MPT_USTRING("A randomized user ID is created and transmitted alongside the update check. This ID can only be linked to you or your computer by this very ID, which is stored solely on your computer.") + MPT_USTRING("\n")
+			+ MPT_USTRING("OpenMPT will use this information to gather usage statistics and to plan removal of support for older systems.") + MPT_USTRING("\n")
+			+ MPT_USTRING("Without this statistical information, the OpenMPT developers would be blind with respect to what systems are used to run OpenMPT. This makes deciding where to focus development plain guesswork.") + MPT_USTRING("\n")
+			+ MPT_USTRING("OpenMPT collects the following statistical data points: OpenMPT version, Windows version, type of CPU, amount of RAM, configured update check frequency of OpenMPT.")
+			;
+	}
+}
 
-mpt::ustring CUpdateCheck::GetDefaultUpdateURL()
+
+mpt::ustring CUpdateCheck::GetDefaultChannelReleaseURL()
 {
 	return MPT_USTRING("https://update.openmpt.org/check/$VERSION/$GUID");
 }
 
+mpt::ustring CUpdateCheck::GetDefaultChannelNextURL()
+{
+	return MPT_USTRING("https://update.openmpt.org/check/next/$VERSION/$GUID");
+}
 
+mpt::ustring CUpdateCheck::GetDefaultChannelDevelopmentURL()
+{
+	return MPT_USTRING("https://update.openmpt.org/check/testing/$VERSION/$GUID");
+}
+
+
+mpt::ustring CUpdateCheck::GetDefaultAPIURL()
+{
+	return MPT_USTRING("https://update.openmpt.org/api/v3/");
+}
+
+
 std::atomic<int32> CUpdateCheck::s_InstanceCount(0);
 
 
@@ -104,11 +139,15 @@
 {
 	if(isAutoUpdate)
 	{
-		int updateCheckPeriod = TrackerSettings::Instance().UpdateUpdateCheckPeriod;
-		if(updateCheckPeriod == 0)
+		if(!TrackerSettings::Instance().UpdateEnabled)
 		{
 			return;
 		}
+		int updateCheckPeriod = TrackerSettings::Instance().UpdateIntervalDays;
+		if(updateCheckPeriod < 0)
+		{
+			return;
+		}
 		// Do we actually need to run the update check right now?
 		const time_t now = time(nullptr);
 		if(difftime(now, TrackerSettings::Instance().UpdateLastUpdateCheck.Get()) < (double)(updateCheckPeriod * 86400))
@@ -121,7 +160,7 @@
 		{
 			TrackerSettings::Instance().UpdateShowUpdateHint = false;
 			CString msg;
-			msg.Format(_T("OpenMPT would like to check for updates now, proceed?\n\nNote: In the future, OpenMPT will check for updates every %u days. If you do not want this, you can disable update checks in the setup."), TrackerSettings::Instance().UpdateUpdateCheckPeriod.Get());
+			msg.Format(_T("OpenMPT would like to check for updates now, proceed?\n\nNote: In the future, OpenMPT will check for updates every %u days. If you do not want this, you can disable update checks in the setup."), TrackerSettings::Instance().UpdateIntervalDays.Get());
 			if(Reporting::Confirm(msg, _T("OpenMPT Internet Update")) == cnfNo)
 			{
 				TrackerSettings::Instance().UpdateLastUpdateCheck = mpt::Date::Unix(now);
@@ -128,6 +167,15 @@
 				return;
 			}
 		}
+	} else
+	{
+		if(!TrackerSettings::Instance().UpdateEnabled)
+		{
+			if(Reporting::Confirm(_T("Update Check is disabled. Do you want to check anyway?"), _T("OpenMPT Internet Update")) != cnfYes)
+			{
+				return;
+			}
+		}
 	}
 	TrackerSettings::Instance().UpdateShowUpdateHint = false;
 
@@ -137,22 +185,33 @@
 		return;
 	}
 
-	CUpdateCheck::Settings settings;
-	settings.window = CMainFrame::GetMainFrame();
-	settings.msgProgress = MPT_WM_APP_UPDATECHECK_PROGRESS;
-	settings.msgSuccess = MPT_WM_APP_UPDATECHECK_SUCCESS;
-	settings.msgFailure = MPT_WM_APP_UPDATECHECK_FAILURE;
-	settings.autoUpdate = isAutoUpdate;
-	settings.updateBaseURL = TrackerSettings::Instance().UpdateUpdateURL;
-	settings.sendStatistics = TrackerSettings::Instance().UpdateSendGUID;
-	settings.statisticsUUID = TrackerSettings::Instance().VersionInstallGUID;
-	settings.suggestDifferentBuilds = TrackerSettings::Instance().UpdateSuggestDifferentBuildVariant;
-	std::thread(CUpdateCheck::ThreadFunc(settings)).detach();
+	CUpdateCheck::Context context;
+	context.window = CMainFrame::GetMainFrame();
+	context.msgProgress = MPT_WM_APP_UPDATECHECK_PROGRESS;
+	context.msgSuccess = MPT_WM_APP_UPDATECHECK_SUCCESS;
+	context.msgFailure = MPT_WM_APP_UPDATECHECK_FAILURE;
+	context.autoUpdate = isAutoUpdate;
+	std::thread(CUpdateCheck::ThreadFunc(CUpdateCheck::Settings(), context)).detach();
 }
 
 
-CUpdateCheck::ThreadFunc::ThreadFunc(const CUpdateCheck::Settings &settings)
+CUpdateCheck::Settings::Settings()
+	: periodDays(TrackerSettings::Instance().UpdateIntervalDays)
+	, channel(TrackerSettings::Instance().UpdateChannel)
+	, channelReleaseURL(TrackerSettings::Instance().UpdateChannelReleaseURL)
+	, channelNextURL(TrackerSettings::Instance().UpdateChannelNextURL)
+	, channelDevelopmentURL(TrackerSettings::Instance().UpdateChannelDevelopmentURL)
+	, apiURL(TrackerSettings::Instance().UpdateAPIURL)
+	, sendStatistics(TrackerSettings::Instance().UpdateStatistics)
+	, statisticsUUID(TrackerSettings::Instance().VersionInstallGUID)
+	, suggestDifferentBuilds(TrackerSettings::Instance().UpdateSuggestDifferentBuildVariant)
+{
+}
+
+
+CUpdateCheck::ThreadFunc::ThreadFunc(const CUpdateCheck::Settings &settings, const CUpdateCheck::Context &context)
 	: settings(settings)
+	, context(context)
 {
 	return;
 }
@@ -160,27 +219,103 @@
 
 void CUpdateCheck::ThreadFunc::operator () ()
 {
-	mpt::SetCurrentThreadPriority(settings.autoUpdate ? mpt::ThreadPriorityLower : mpt::ThreadPriorityNormal);
-	CUpdateCheck::CheckForUpdate(settings);
+	mpt::SetCurrentThreadPriority(context.autoUpdate ? mpt::ThreadPriorityLower : mpt::ThreadPriorityNormal);
+	CUpdateCheck::CheckForUpdate(settings, context);
 }
 
 
-// Run update check (independent thread)
-CUpdateCheck::Result CUpdateCheck::SearchUpdate(const CUpdateCheck::Settings &settings)
+std::string CUpdateCheck::GetStatisticsDataV3(const Settings &settings)
 {
-	
-	HTTP::InternetSession internet(Version::Current().GetOpenMPTVersionString());
+	JSON::value j;
+	j["OpenMPT"]["Version"] = mpt::ufmt::val(Version::Current());
+	j["OpenMPT"]["BuildVariant"] = BuildVariants().GuessCurrentBuildName();
+	j["OpenMPT"]["Architecture"] = mpt::Windows::Name(mpt::Windows::GetProcessArchitecture());
+	j["Update"]["PeriodDays"] = settings.periodDays;
+	j["System"]["Windows"]["Version"]["Name"] = mpt::Windows::Version::Current().GetName();
+	j["System"]["Windows"]["Version"]["Major"] = mpt::Windows::Version::Current().GetSystem().Major;
+	j["System"]["Windows"]["Version"]["Minor"] = mpt::Windows::Version::Current().GetSystem().Minor;
+	j["System"]["Windows"]["ServicePack"]["Major"] = mpt::Windows::Version::Current().GetServicePack().Major;
+	j["System"]["Windows"]["ServicePack"]["Minor"] = mpt::Windows::Version::Current().GetServicePack().Minor;
+	j["System"]["Windows"]["Build"] = mpt::Windows::Version::Current().GetBuild();
+	j["System"]["Windows"]["Architecture"] = mpt::Windows::Name(mpt::Windows::GetHostArchitecture());
+	j["System"]["Windows"]["IsWine"] = mpt::Windows::IsWine();
+	std::vector<mpt::Windows::Architecture> architectures = mpt::Windows::GetSupportedProcessArchitectures(mpt::Windows::GetHostArchitecture());
+	for(const auto & arch : architectures)
+	{
+		j["System"]["Windows"]["ProcessArchitectures"][mpt::ToCharset(mpt::CharsetUTF8, mpt::Windows::Name(arch))] = true;
+	}
+	j["System"]["Memory"] = mpt::Windows::GetSystemMemorySize() / 1024 / 1024;  // MB
+	if(mpt::Windows::IsWine())
+	{
+		mpt::Wine::VersionContext v;
+		j["System"]["Windows"]["Wine"]["Version"]["Raw"] = v.RawVersion();
+		if(v.Version().IsValid())
+		{
+			j["System"]["Windows"]["Wine"]["Version"]["Major"] = v.Version().GetMajor();
+			j["System"]["Windows"]["Wine"]["Version"]["Minor"] = v.Version().GetMinor();
+			j["System"]["Windows"]["Wine"]["Version"]["Update"] = v.Version().GetUpdate();
+		}
+		j["System"]["Windows"]["Wine"]["HostSysName"] = v.RawHostSysName();
+	}
+	#ifdef ENABLE_ASM
+		j["System"]["Processor"]["Vendor"] = std::string(mpt::String::ReadAutoBuf(ProcVendorID));
+		j["System"]["Processor"]["Brand"] = std::string(mpt::String::ReadAutoBuf(ProcBrandID));
+		j["System"]["Processor"]["Id"]["Family"] = ProcFamily;
+		j["System"]["Processor"]["Id"]["Model"] = ProcModel;
+		j["System"]["Processor"]["Id"]["Stepping"] = ProcStepping;
+		j["System"]["Processor"]["Features"]["lm"] = ((GetRealProcSupport() & PROCSUPPORT_LM) ? true : false);
+		j["System"]["Processor"]["Features"]["cmov"] = ((GetRealProcSupport() & PROCSUPPORT_CMOV) ? true : false);
+		j["System"]["Processor"]["Features"]["mmx"] = ((GetRealProcSupport() & PROCSUPPORT_MMX) ? true : false);
+		j["System"]["Processor"]["Features"]["mmxext"] = ((GetRealProcSupport() & PROCSUPPORT_AMD_MMXEXT) ? true : false);
+		j["System"]["Processor"]["Features"]["3dnow"] = ((GetRealProcSupport() & PROCSUPPORT_AMD_3DNOW) ? true : false);
+		j["System"]["Processor"]["Features"]["3dnowext"] = ((GetRealProcSupport() & PROCSUPPORT_AMD_3DNOWEXT) ? true : false);
+		j["System"]["Processor"]["Features"]["sse"] = ((GetRealProcSupport() & PROCSUPPORT_SSE) ? true : false);
+		j["System"]["Processor"]["Features"]["sse2"] = ((GetRealProcSupport() & PROCSUPPORT_SSE2) ? true : false);
+		j["System"]["Processor"]["Features"]["sse3"] = ((GetRealProcSupport() & PROCSUPPORT_SSE3) ? true : false);
+		j["System"]["Processor"]["Features"]["ssse3"] = ((GetRealProcSupport() & PROCSUPPORT_SSSE3) ? true : false);
+		j["System"]["Processor"]["Features"]["sse4_1"] = ((GetRealProcSupport() & PROCSUPPORT_SSE4_1) ? true : false);
+		j["System"]["Processor"]["Features"]["sse4_2"] = ((GetRealProcSupport() & PROCSUPPORT_SSE4_2) ? true : false);
+	#endif
+	return j.dump(1, '\t');
+}
 
-	mpt::ustring updateURL = settings.updateBaseURL;
-	if(updateURL.empty())
+
+mpt::ustring CUpdateCheck::GetUpdateURLV2(const CUpdateCheck::Settings &settings)
+{
+	mpt::ustring updateURL;
+	if(settings.channel == UpdateChannelRelease)
 	{
-		updateURL = GetDefaultUpdateURL();
+		updateURL = settings.channelReleaseURL;
+		if(updateURL.empty())
+		{
+			updateURL = GetDefaultChannelReleaseURL();
+		}
+	}	else if(settings.channel == UpdateChannelNext)
+	{
+		updateURL = settings.channelNextURL;
+		if(updateURL.empty())
+		{
+			updateURL = GetDefaultChannelNextURL();
+		}
+	}	else if(settings.channel == UpdateChannelDevelopment)
+	{
+		updateURL = settings.channelDevelopmentURL;
+		if(updateURL.empty())
+		{
+			updateURL = GetDefaultChannelDevelopmentURL();
+		}
+	}	else
+	{
+		updateURL = settings.channelReleaseURL;
+		if(updateURL.empty())
+		{
+			updateURL = GetDefaultChannelReleaseURL();
+		}
 	}
 	if(updateURL.find(MPT_USTRING("://")) == mpt::ustring::npos)
 	{
 		updateURL = MPT_USTRING("https://") + updateURL;
 	}
-
 	// Build update URL
 	updateURL = mpt::String::Replace(updateURL, MPT_USTRING("$VERSION"), mpt::uformat(MPT_USTRING("%1-%2-%3"))
 		( Version::Current()
@@ -188,15 +323,44 @@
 		, settings.sendStatistics ? mpt::Windows::Version::Current().GetNameShort() : MPT_USTRING("unknown")
 		));
 	updateURL = mpt::String::Replace(updateURL, MPT_USTRING("$GUID"), settings.sendStatistics ? mpt::ufmt::val(settings.statisticsUUID) : MPT_USTRING("anonymous"));
+	return updateURL;
+}
 
+
+// Run update check (independent thread)
+CUpdateCheck::Result CUpdateCheck::SearchUpdate(const CUpdateCheck::Settings &settings)
+{
+	
+	HTTP::InternetSession internet(Version::Current().GetOpenMPTVersionString());
+
 	// Establish a connection.
 	HTTP::Request request;
-	request.SetURI(ParseURI(updateURL));
+	request.SetURI(ParseURI(GetUpdateURLV2(settings)));
 	request.method = HTTP::Method::Get;
 	request.flags = HTTP::NoCache;
 
 	HTTP::Result resultHTTP = internet(request.InsecureTLSDowngradeWindowsXP());
 
+	if(settings.sendStatistics)
+	{
+		HTTP::Request requestStatistics;
+		if(settings.statisticsUUID.IsValid())
+		{
+			requestStatistics.SetURI(ParseURI(settings.apiURL + mpt::format(MPT_USTRING("statistics/%1"))(settings.statisticsUUID)));
+			requestStatistics.method = HTTP::Method::Put;
+		} else
+		{
+			requestStatistics.SetURI(ParseURI(settings.apiURL + MPT_USTRING("statistics/")));
+			requestStatistics.method = HTTP::Method::Post;
+		}
+		requestStatistics.dataMimeType = HTTP::MimeType::JSON();
+		requestStatistics.acceptMimeTypes = HTTP::MimeTypes::JSON();
+		std::string jsondata = GetStatisticsDataV3(settings);
+		MPT_LOG(LogInformation, "Update", mpt::ToUnicode(mpt::CharsetUTF8, jsondata));
+		requestStatistics.data = mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(jsondata));
+		internet(requestStatistics.InsecureTLSDowngradeWindowsXP());
+	}
+
 	// Retrieve HTTP status code.
 	if(resultHTTP.Status >= 400)
 	{
@@ -244,12 +408,12 @@
 }
 
 
-void CUpdateCheck::CheckForUpdate(const CUpdateCheck::Settings &settings)
+void CUpdateCheck::CheckForUpdate(const CUpdateCheck::Settings &settings, const CUpdateCheck::Context &context)
 {
 	// íncremented before starting the thread
 	MPT_ASSERT(s_InstanceCount.load() >= 1);
 	CUpdateCheck::Result result;
-	settings.window->SendMessage(settings.msgProgress, settings.autoUpdate ? 1 : 0, s_InstanceCount.load());
+	context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, s_InstanceCount.load());
 	try
 	{
 		try
@@ -264,12 +428,12 @@
 		}
 	} catch(const CUpdateCheck::Error &e)
 	{
-		settings.window->SendMessage(settings.msgFailure, settings.autoUpdate ? 1 : 0, reinterpret_cast<LPARAM>(&e));
+		context.window->SendMessage(context.msgFailure, context.autoUpdate ? 1 : 0, reinterpret_cast<LPARAM>(&e));
 		s_InstanceCount.fetch_sub(1);
 		MPT_ASSERT(s_InstanceCount.load() >= 0);
 		return;
 	}
-	settings.window->SendMessage(settings.msgSuccess, settings.autoUpdate ? 1 : 0, reinterpret_cast<LPARAM>(&result));
+	context.window->SendMessage(context.msgSuccess, context.autoUpdate ? 1 : 0, reinterpret_cast<LPARAM>(&result));
 	s_InstanceCount.fetch_sub(1);
 	MPT_ASSERT(s_InstanceCount.load() >= 0);
 }
@@ -347,14 +511,13 @@
 // CUpdateSetupDlg
 
 BEGIN_MESSAGE_MAP(CUpdateSetupDlg, CPropertyPage)
-	ON_COMMAND(IDC_BUTTON1,			&CUpdateSetupDlg::OnCheckNow)
-	ON_COMMAND(IDC_BUTTON2,			&CUpdateSetupDlg::OnResetURL)
-	ON_COMMAND(IDC_RADIO1,			&CUpdateSetupDlg::OnSettingsChanged)
-	ON_COMMAND(IDC_RADIO2,			&CUpdateSetupDlg::OnSettingsChanged)
-	ON_COMMAND(IDC_RADIO3,			&CUpdateSetupDlg::OnSettingsChanged)
-	ON_COMMAND(IDC_RADIO4,			&CUpdateSetupDlg::OnSettingsChanged)
-	ON_COMMAND(IDC_CHECK1,			&CUpdateSetupDlg::OnSettingsChanged)
-	ON_EN_CHANGE(IDC_EDIT1,			&CUpdateSetupDlg::OnSettingsChanged)
+	ON_COMMAND(IDC_CHECK_UPDATEENABLED,         &CUpdateSetupDlg::OnSettingsChanged)
+	ON_COMMAND(IDC_RADIO1,                      &CUpdateSetupDlg::OnSettingsChanged)
+	ON_COMMAND(IDC_RADIO2,                      &CUpdateSetupDlg::OnSettingsChanged)
+	ON_COMMAND(IDC_RADIO3,                      &CUpdateSetupDlg::OnSettingsChanged)
+	ON_COMMAND(IDC_BUTTON1,                     &CUpdateSetupDlg::OnCheckNow)
+	ON_CBN_SELCHANGE(IDC_COMBO_UPDATEFREQUENCY, &CUpdateSetupDlg::OnSettingsChanged)
+	ON_COMMAND(IDC_CHECK1,                      &CUpdateSetupDlg::OnSettingsChanged)
 END_MESSAGE_MAP()
 
 
@@ -366,29 +529,82 @@
 }
 
 
+void CUpdateSetupDlg::DoDataExchange(CDataExchange *pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_COMBO_UPDATEFREQUENCY, m_CbnUpdateFrequency);
+}
+
+
 BOOL CUpdateSetupDlg::OnInitDialog()
 {
 	CPropertyPage::OnInitDialog();
 
+	CheckDlgButton(IDC_CHECK_UPDATEENABLED, TrackerSettings::Instance().UpdateEnabled ? BST_CHECKED : BST_UNCHECKED);
+
 	int radioID = 0;
-	int periodDays = TrackerSettings::Instance().UpdateUpdateCheckPeriod;
-	if(periodDays >= 30)
+	int updateChannel = TrackerSettings::Instance().UpdateChannel;
+	if(updateChannel == UpdateChannelRelease)
 	{
-		radioID = IDC_RADIO4;
-	} else if(periodDays >= 7)
+		radioID = IDC_RADIO1;
+	} else if(updateChannel == UpdateChannelNext)
 	{
+		radioID = IDC_RADIO2;
+	} else if(updateChannel == UpdateChannelDevelopment)
+	{
 		radioID = IDC_RADIO3;
-	} else if(periodDays >= 1)
-	{
-		radioID = IDC_RADIO2;
 	} else
 	{
 		radioID = IDC_RADIO1;
 	}
-	CheckRadioButton(IDC_RADIO1, IDC_RADIO4, radioID);
-	CheckDlgButton(IDC_CHECK1, TrackerSettings::Instance().UpdateSendGUID ? BST_CHECKED : BST_UNCHECKED);
-	SetDlgItemText(IDC_EDIT1, mpt::ToCString(TrackerSettings::Instance().UpdateUpdateURL.Get()));
+	CheckRadioButton(IDC_RADIO1, IDC_RADIO3, radioID);
 
+	int32 periodDays = TrackerSettings::Instance().UpdateIntervalDays;
+	int ndx;
+
+	ndx = m_CbnUpdateFrequency.AddString(_T("always"));
+	m_CbnUpdateFrequency.SetItemData(ndx, 0);
+	if(periodDays >= 30)
+	{
+		m_CbnUpdateFrequency.SetCurSel(ndx);
+	}
+
+	ndx = m_CbnUpdateFrequency.AddString(_T("daily"));
+	m_CbnUpdateFrequency.SetItemData(ndx, 1);
+	if(periodDays >= 30)
+	{
+		m_CbnUpdateFrequency.SetCurSel(ndx);
+	}
+
+	ndx = m_CbnUpdateFrequency.AddString(_T("weekly"));
+	m_CbnUpdateFrequency.SetItemData(ndx, 7);
+	if(periodDays >= 7)
+	{
+		m_CbnUpdateFrequency.SetCurSel(ndx);
+	}
+
+	ndx = m_CbnUpdateFrequency.AddString(_T("monthly"));
+	m_CbnUpdateFrequency.SetItemData(ndx, 30);
+	if(periodDays >= 0)
+	{
+		m_CbnUpdateFrequency.SetCurSel(ndx);
+	}
+
+	ndx = m_CbnUpdateFrequency.AddString(_T("never"));
+	m_CbnUpdateFrequency.SetItemData(ndx, ~(DWORD_PTR)0);
+	if(periodDays < 0)
+	{		
+		m_CbnUpdateFrequency.SetCurSel(ndx);
+	}
+
+	CheckDlgButton(IDC_CHECK1, TrackerSettings::Instance().UpdateStatistics ? BST_CHECKED : BST_UNCHECKED);
+
+	GetDlgItem(IDC_STATIC_UPDATEPRIVACYTEXT)->SetWindowText(mpt::ToCString(CUpdateCheck::GetStatisticsUserInformation(true)));
+
+	UpdateStatistics();
+
+	EnableDisableDialog();
+
 	m_SettingChangedNotifyGuard.Register(this);
 	SettingChanged(TrackerSettings::Instance().UpdateLastUpdateCheck.GetPath());
 
@@ -396,6 +612,43 @@
 }
 
 
+void CUpdateSetupDlg::UpdateStatistics()
+{
+	CUpdateCheck::Settings settings;
+
+	int updateChannel = TrackerSettings::Instance().UpdateChannel;
+	if(IsDlgButtonChecked(IDC_RADIO1)) updateChannel = UpdateChannelRelease;
+	if(IsDlgButtonChecked(IDC_RADIO2)) updateChannel = UpdateChannelNext;
+	if(IsDlgButtonChecked(IDC_RADIO3)) updateChannel = UpdateChannelDevelopment;
+
+	int updateCheckPeriod = (m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()) == ~(DWORD_PTR)0) ? -1 : static_cast<int>(m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()));
+
+	CString updateURL;
+	GetDlgItemText(IDC_EDIT1, updateURL);
+
+	settings.periodDays = updateCheckPeriod;
+	settings.channel = updateChannel;
+	settings.sendStatistics = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
+
+	mpt::ustring statistics;
+	statistics += MPT_USTRING("GET ") + CUpdateCheck::GetUpdateURLV2(settings) + MPT_ULITERAL("\n");
+	statistics += MPT_ULITERAL("\n");
+	if(settings.sendStatistics)
+	{
+		if(settings.statisticsUUID.IsValid())
+		{
+			statistics += MPT_USTRING("PUT ") + settings.apiURL + mpt::format(MPT_USTRING("statistics/%1"))(settings.statisticsUUID) + MPT_ULITERAL("\n");
+		} else
+		{
+			statistics += MPT_USTRING("POST ") + settings.apiURL + MPT_USTRING("statistics/") + MPT_ULITERAL("\n");
+		}
+		statistics += mpt::String::Replace(mpt::ToUnicode(mpt::CharsetUTF8, CUpdateCheck::GetStatisticsDataV3(settings)), MPT_USTRING("\t"), MPT_USTRING("    "));
+		statistics += MPT_ULITERAL("\n");
+	}
+	SetDlgItemText(IDC_EDIT_STATISTICS, mpt::ToCString(mpt::String::Replace(statistics, MPT_USTRING("\n"), MPT_USTRING("\r\n"))));
+}
+
+
 void CUpdateSetupDlg::SettingChanged(const SettingPath &changedPath)
 {
 	if(changedPath == TrackerSettings::Instance().UpdateLastUpdateCheck.GetPath())
@@ -420,20 +673,61 @@
 }
 
 
+void CUpdateSetupDlg::EnableDisableDialog()
+{
+
+	BOOL status = ((IsDlgButtonChecked(IDC_CHECK_UPDATEENABLED) != BST_UNCHECKED) ? TRUE : FALSE);
+
+	GetDlgItem(IDC_STATIC_UDATECHANNEL)->EnableWindow(status);
+	GetDlgItem(IDC_RADIO1)->EnableWindow(status);
+	GetDlgItem(IDC_RADIO2)->EnableWindow(status);
+	GetDlgItem(IDC_RADIO3)->EnableWindow(status);
+
+	GetDlgItem(IDC_STATIC_UPDATECHECK)->EnableWindow(status);
+	GetDlgItem(IDC_STATIC_UPDATEFREQUENCY)->EnableWindow(status);
+	GetDlgItem(IDC_COMBO_UPDATEFREQUENCY)->EnableWindow(status);
+	GetDlgItem(IDC_BUTTON1)->EnableWindow(status);
+	GetDlgItem(IDC_LASTUPDATE)->EnableWindow(status);
+
+	GetDlgItem(IDC_STATIC_UPDATEPRIVACY)->EnableWindow(status);
+	GetDlgItem(IDC_CHECK1)->EnableWindow(status);
+	GetDlgItem(IDC_STATIC_UPDATEPRIVACYTEXT)->EnableWindow(status);
+	GetDlgItem(IDC_EDIT_STATISTICS)->EnableWindow(status);
+
+	// disabled features
+	GetDlgItem(IDC_CHECK_UPDATEENABLED)->EnableWindow(FALSE);
+	GetDlgItem(IDC_RADIO2)->EnableWindow(FALSE);
+
+}
+
+
+void CUpdateSetupDlg::OnSettingsChanged()
+{
+	EnableDisableDialog();
+	UpdateStatistics();
+	SetModified(TRUE);
+}
+
+
 void CUpdateSetupDlg::OnOK()
 {
-	int updateCheckPeriod = TrackerSettings::Instance().UpdateUpdateCheckPeriod;
-	if(IsDlgButtonChecked(IDC_RADIO1)) updateCheckPeriod = 0;
-	if(IsDlgButtonChecked(IDC_RADIO2)) updateCheckPeriod = 1;
-	if(IsDlgButtonChecked(IDC_RADIO3)) updateCheckPeriod = 7;
-	if(IsDlgButtonChecked(IDC_RADIO4)) updateCheckPeriod = 31;
+	int updateChannel = TrackerSettings::Instance().UpdateChannel;
+	if(IsDlgButtonChecked(IDC_RADIO1)) updateChannel = UpdateChannelRelease;
+	if(IsDlgButtonChecked(IDC_RADIO2)) updateChannel = UpdateChannelNext;
+	if(IsDlgButtonChecked(IDC_RADIO3)) updateChannel = UpdateChannelDevelopment;
 
+	int updateCheckPeriod = (m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()) == ~(DWORD_PTR)0) ? -1 : static_cast<int>(m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()));
+	
 	CString updateURL;
 	GetDlgItemText(IDC_EDIT1, updateURL);
 
-	TrackerSettings::Instance().UpdateUpdateCheckPeriod = updateCheckPeriod;
-	TrackerSettings::Instance().UpdateUpdateURL = mpt::ToUnicode(updateURL);
-	TrackerSettings::Instance().UpdateSendGUID = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
+	if(GetDlgItem(IDC_CHECK_UPDATEENABLED)->IsWindowEnabled() != FALSE)
+	{
+		TrackerSettings::Instance().UpdateEnabled = (IsDlgButtonChecked(IDC_CHECK_UPDATEENABLED) != BST_UNCHECKED);
+	}
+	TrackerSettings::Instance().UpdateIntervalDays = updateCheckPeriod;
+	TrackerSettings::Instance().UpdateChannel = updateChannel;
+	TrackerSettings::Instance().UpdateStatistics = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
 	
 	CPropertyPage::OnOK();
 }
@@ -452,10 +746,4 @@
 }
 
 
-void CUpdateSetupDlg::OnResetURL()
-{
-	SetDlgItemText(IDC_EDIT1, mpt::ToCString(CUpdateCheck::GetDefaultUpdateURL()));
-}
-
-
 OPENMPT_NAMESPACE_END
Index: mptrack/UpdateCheck.h
===================================================================
--- mptrack/UpdateCheck.h	(revision 10963)
+++ mptrack/UpdateCheck.h	(working copy)
@@ -24,6 +24,13 @@
 OPENMPT_NAMESPACE_BEGIN
 
 
+enum UpdateChannel
+{
+	UpdateChannelRelease = 1,
+	UpdateChannelNext = 2,
+	UpdateChannelDevelopment = 3,
+};
+
 class CUpdateCheck
 {
 
@@ -33,7 +40,13 @@
 
 public:
 
-	static mpt::ustring GetDefaultUpdateURL();
+	static mpt::ustring GetStatisticsUserInformation(bool shortText);
+
+	static mpt::ustring GetDefaultChannelReleaseURL();
+	static mpt::ustring GetDefaultChannelNextURL();
+	static mpt::ustring GetDefaultChannelDevelopmentURL();
+
+	static mpt::ustring GetDefaultAPIURL();
 	
 	int32 GetNumCurrentRunningInstances();
 
@@ -42,7 +55,7 @@
 
 public:
 
-	struct Settings
+	struct Context
 	{
 		CWnd *window;
 		UINT msgProgress;
@@ -49,10 +62,20 @@
 		UINT msgSuccess;
 		UINT msgFailure;
 		bool autoUpdate;
-		mpt::ustring updateBaseURL;  // URL where the version check should be made.
+	};
+
+	struct Settings
+	{
+		int32 periodDays;
+		uint32 channel;
+		mpt::ustring channelReleaseURL;
+		mpt::ustring channelNextURL;
+		mpt::ustring channelDevelopmentURL;
+		mpt::ustring apiURL;
 		bool sendStatistics;
 		mpt::UUID statisticsUUID;
 		bool suggestDifferentBuilds;
+		Settings();
 	};
 
 	class Error
@@ -86,6 +109,14 @@
 	static void ShowSuccessGUI(WPARAM wparam, LPARAM lparam);
 	static void ShowFailureGUI(WPARAM wparam, LPARAM lparam);
 
+public:
+
+	// v2
+	static mpt::ustring GetUpdateURLV2(const Settings &settings);
+
+	// v3
+	static std::string GetStatisticsDataV3(const Settings &settings);  // UTF8
+
 protected:
 
 	static void StartUpdateCheckAsync(bool autoUpdate);
@@ -93,11 +124,12 @@
 	struct ThreadFunc
 	{
 		CUpdateCheck::Settings settings;
-		ThreadFunc(const CUpdateCheck::Settings &settings);
+		CUpdateCheck::Context context;
+		ThreadFunc(const CUpdateCheck::Settings &settings, const CUpdateCheck::Context &context);
 		void operator () ();
 	};
 
-	static void CheckForUpdate(const CUpdateCheck::Settings &settings);
+	static void CheckForUpdate(const CUpdateCheck::Settings &settings, const CUpdateCheck::Context &context);
 
 	static CUpdateCheck::Result SearchUpdate(const CUpdateCheck::Settings &settings); // may throw
 	
@@ -111,17 +143,20 @@
 	CUpdateSetupDlg();
 
 protected:
+	virtual void DoDataExchange(CDataExchange *pDX);
 	virtual BOOL OnInitDialog();
 	virtual void OnOK();
 	virtual BOOL OnSetActive();
-	afx_msg void OnSettingsChanged() { SetModified(TRUE); }
+	afx_msg void OnSettingsChanged();
 	afx_msg void OnCheckNow();
-	afx_msg void OnResetURL();
 	virtual void SettingChanged(const SettingPath &changedPath);
+	void EnableDisableDialog();
+	void UpdateStatistics();
 	DECLARE_MESSAGE_MAP()
 
 private:
 	SettingChangedNotifyGuard m_SettingChangedNotifyGuard;
+	CComboBox m_CbnUpdateFrequency;
 };
 
 
Index: mptrack/WelcomeDialog.cpp
===================================================================
--- mptrack/WelcomeDialog.cpp	(revision 10963)
+++ mptrack/WelcomeDialog.cpp	(working copy)
@@ -119,6 +119,8 @@
 	combo->SetItemDataPtr(combo->AddString(_T("FastTracker 2")), (void*)("US_mpt-ft2_classic"));
 
 	CheckDlgButton(IDC_CHECK1, BST_CHECKED);
+	CheckDlgButton(IDC_CHECK3, BST_UNCHECKED);
+	GetDlgItem(IDC_STATIC_WELCOME_STATISTICS)->SetWindowText(mpt::ToCString(mpt::String::Replace(CUpdateCheck::GetStatisticsUserInformation(false), MPT_USTRING("\n"), MPT_USTRING(" "))));
 	CheckDlgButton(IDC_CHECK2, (TrackerSettings::Instance().patternFont.Get().name == PATTERNFONT_LARGE) ? BST_CHECKED : BST_UNCHECKED);
 
 	ShowWindow(SW_SHOW);
@@ -147,7 +149,8 @@
 	CDialog::OnOK();
 
 	bool runUpdates = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED;
-	TrackerSettings::Instance().UpdateUpdateCheckPeriod = (runUpdates ? 7 : 0);
+	TrackerSettings::Instance().UpdateIntervalDays = (runUpdates ? 7 : 0);
+	TrackerSettings::Instance().UpdateStatistics = (IsDlgButtonChecked(IDC_CHECK3) != BST_UNCHECKED);
 	if(IsDlgButtonChecked(IDC_CHECK2) != BST_UNCHECKED)
 	{
 		FontSetting font = TrackerSettings::Instance().patternFont;
