BizHawk/BizHawk.Client.EmuHawk/UpdateChecker.cs

143 lines
4.0 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Threading;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public static class UpdateChecker
{
private static readonly string _latestVersionInfoURL = "http://tasvideos.org/SystemBizHawkReleaseManager.html";
private static readonly TimeSpan _minimumCheckDuration = TimeSpan.FromHours(8);
private static bool AutoCheckEnabled
{
get { return Global.Config.Update_AutoCheckEnabled; }
set { Global.Config.Update_AutoCheckEnabled = value; }
}
private static DateTime? LastCheckTimeUTC
{
get { return Global.Config.Update_LastCheckTimeUTC; }
set { Global.Config.Update_LastCheckTimeUTC = value; }
}
private static string LatestVersion
{
get { return Global.Config.Update_LatestVersion; }
set { Global.Config.Update_LatestVersion = value; }
}
private static string IgnoreVersion
{
get { return Global.Config.Update_IgnoreVersion; }
set { Global.Config.Update_IgnoreVersion = value; }
}
public static void BeginCheck(bool skipCheck = false)
{
if (skipCheck || String.IsNullOrEmpty(_latestVersionInfoURL) || !AutoCheckEnabled || LastCheckTimeUTC > DateTime.UtcNow - _minimumCheckDuration)
{
OnCheckComplete();
return;
}
ThreadPool.QueueUserWorkItem((s) => CheckInternal());
}
public static bool IsNewVersionAvailable
{
get
{
return AutoCheckEnabled &&
LatestVersion != IgnoreVersion &&
ParseVersion(VersionInfo.Mainversion) != 0 && // Avoid notifying if current version string is invalid
ParseVersion(LatestVersion) > ParseVersion(VersionInfo.Mainversion);
}
}
public static void IgnoreNewVersion()
{
IgnoreVersion = LatestVersion;
}
public static void ResetHistory()
{
LastCheckTimeUTC = null;
LatestVersion = "";
IgnoreVersion = "";
}
private static void CheckInternal()
{
try
{
string latestVersionInfo = WebUtility.HtmlDecode(DownloadURLAsString(_latestVersionInfoURL));
LatestVersion = GetVersionNumberFromVersionInfo(latestVersionInfo);
}
catch
{
// Ignore errors, and fall through to set the last check time to avoid requesting too frequently from the web server
}
LastCheckTimeUTC = DateTime.UtcNow;
OnCheckComplete();
}
private static string DownloadURLAsString(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.KeepAlive = false;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
{
return responseStream.ReadToEnd();
}
}
private static string GetVersionNumberFromVersionInfo(string info)
{
string versionNumber = GetTextFromTag(info, "VersionNumber");
return (versionNumber != null && ParseVersion(versionNumber) != 0) ? versionNumber : "";
}
private static string GetTextFromTag(string info, string tagName)
{
string openTag = "[" + tagName + "]";
string closeTag = "[/" + tagName + "]";
int start = info.IndexOf(openTag, StringComparison.OrdinalIgnoreCase);
if (start == -1) return null;
start += openTag.Length;
int end = info.IndexOf(closeTag, start, StringComparison.OrdinalIgnoreCase);
if (end == -1) return null;
return info.Substring(start, end - start).Trim();
}
// Major version goes in the first 16 bits, and so on, up to 4 parts
private static ulong ParseVersion(string str)
{
string[] split = str.Split('.');
if (split.Length > 4) return 0;
ulong version = 0;
for (int i = 0; i < split.Length; i++)
{
ushort versionPart;
if (!UInt16.TryParse(split[i], out versionPart)) return 0;
version |= (ulong)versionPart << (48 - (i * 16));
}
return version;
}
private static void OnCheckComplete()
{
CheckComplete?.Invoke(null, EventArgs.Empty);
}
public static event EventHandler CheckComplete;
}
}