mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-27 14:12:16 +00:00
2c0f530ba5
Changed the way editor is closed during the update process (updater now asks to close the editor after downloading the update package). Added write access check before performing the update. The editor can now update the updater. Updater: merged relevant parts of SharpCompress into the updater source, reducing updater file size from 900 kb to 150 kb. Updated ZDoom_DECORATE.cfg.
335 lines
9.2 KiB
C#
335 lines
9.2 KiB
C#
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Xml;
|
|
using SharpCompress.Archive.SevenZip;
|
|
using SharpCompress.Common;
|
|
using SharpCompress.Reader;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder
|
|
{
|
|
internal static class UpdateChecker
|
|
{
|
|
private delegate DialogResult ShowWarningMessageDelegate(string message, MessageBoxButtons buttons);
|
|
|
|
private const string NO_UPDATE_REQUIRED = "Your version is up to date.";
|
|
|
|
private static BackgroundWorker worker;
|
|
private static bool verbose;
|
|
|
|
internal static void PerformCheck(bool verbosemode)
|
|
{
|
|
// Update check already runing?
|
|
if(worker != null && worker.IsBusy)
|
|
{
|
|
if(verbosemode) General.ShowWarningMessage("Update check is already running!", MessageBoxButtons.OK);
|
|
return;
|
|
}
|
|
|
|
// Check if we have write access...
|
|
if(!General.CheckWritePremissions(General.AppPath))
|
|
{
|
|
string msg = "Cannot perform update: your user account does not have write access to the destination folder \"" + General.AppPath + "\""
|
|
+ "Move the editor to a folder with write access, or run it as Administrator.";
|
|
|
|
if(verbosemode) General.ShowWarningMessage(msg, MessageBoxButtons.OK);
|
|
else General.ErrorLogger.Add(ErrorType.Error, msg);
|
|
return;
|
|
}
|
|
|
|
// Start checking
|
|
verbose = verbosemode;
|
|
worker = new BackgroundWorker();
|
|
worker.DoWork += DoWork;
|
|
worker.RunWorkerCompleted += RunWorkerCompleted;
|
|
worker.WorkerSupportsCancellation = true;
|
|
worker.RunWorkerAsync();
|
|
}
|
|
|
|
private static void DoWork(object sender, DoWorkEventArgs e)
|
|
{
|
|
string updaterpath = Path.Combine(General.AppPath, "Updater.exe");
|
|
if(!File.Exists(updaterpath))
|
|
{
|
|
ShowResult("Update check failed: \"" + updaterpath + "\" does not exist!");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
string inipath = Path.Combine(General.AppPath, "Updater.ini");
|
|
if(!File.Exists(inipath))
|
|
{
|
|
ShowResult("Update check failed: \"" + inipath + "\" does not exist!");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
// Get some ini values...
|
|
string url = string.Empty;
|
|
string updaterpackname = string.Empty;
|
|
string[] inilines = File.ReadAllLines(inipath);
|
|
foreach(string line in inilines)
|
|
{
|
|
if(line.StartsWith("URL")) url = line.Substring(3).Trim();
|
|
else if(line.StartsWith("UpdaterName")) updaterpackname = line.Substring(11).Trim();
|
|
}
|
|
|
|
if(string.IsNullOrEmpty(url))
|
|
{
|
|
ShowResult("Update check failed: failed to get update url from Updater.ini!");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
if(string.IsNullOrEmpty(updaterpackname))
|
|
{
|
|
ShowResult("Update check failed: failed to get updater pack name from Updater.ini!");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
// 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 numbers
|
|
int remoterev, remoteupdaterrev;
|
|
MemoryStream stream = DownloadWebFile(Path.Combine(url, "Versions.txt"));
|
|
if(stream == null)
|
|
{
|
|
ShowResult("Update check failed: failed to retrieve remote revision info.\nCheck your Internet connection and try again.");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
List<string> lines = new List<string>(2);
|
|
using(StreamReader reader = new StreamReader(stream))
|
|
{
|
|
while(!reader.EndOfStream) lines.Add(reader.ReadLine());
|
|
}
|
|
|
|
if(lines.Count != 2)
|
|
{
|
|
ShowResult("Update check failed: invalid remote revision number.");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
if(!int.TryParse(lines[0], out remoterev))
|
|
{
|
|
ShowResult("Update check failed: failed to retrieve remote revision number.");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
if(!int.TryParse(lines[1], out remoteupdaterrev))
|
|
{
|
|
ShowResult("Update check failed: failed to retrieve remote updater revision number.");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
// Update the updater!
|
|
string result = UpdateUpdater(url, updaterpackname, remoteupdaterrev);
|
|
if(!string.IsNullOrEmpty(result))
|
|
{
|
|
ShowResult("Update check failed: " + result);
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
if(remoterev > localrev)
|
|
{
|
|
// Get changelog info
|
|
string changelog = GetChangelog(url, actuallocalrev);
|
|
|
|
if(string.IsNullOrEmpty(changelog))
|
|
{
|
|
ShowResult("Update check failed: failed to retrieve changelog.\nCheck your Internet connection and try again.");
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
// Pass data to MainForm
|
|
General.MainWindow.UpdateAvailable(remoterev, changelog);
|
|
}
|
|
else if(verbose)
|
|
{
|
|
ShowResult(NO_UPDATE_REQUIRED);
|
|
}
|
|
}
|
|
|
|
private static string UpdateUpdater(string url, string updaterpackname, int remoterev)
|
|
{
|
|
// Check if updater is running...
|
|
try
|
|
{
|
|
Process[] processes = Process.GetProcesses();
|
|
|
|
// Abort if it's running...
|
|
foreach(Process process in processes)
|
|
{
|
|
if(process.ProcessName == "Updater" &&
|
|
Path.GetDirectoryName(process.MainModule.FileName) == Application.StartupPath)
|
|
{
|
|
return "Updater.exe is already running.";
|
|
}
|
|
}
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
return "failed to check Updater process: " + e.Message;
|
|
}
|
|
|
|
// Check local revision
|
|
int localrev = FileVersionInfo.GetVersionInfo("Updater.exe").ProductPrivatePart;
|
|
if(localrev < remoterev)
|
|
{
|
|
// Download update
|
|
MemoryStream stream = DownloadWebFile(Path.Combine(url, updaterpackname));
|
|
if(stream == null)
|
|
{
|
|
return "failed to download Updater package.";
|
|
}
|
|
|
|
// Unpack update
|
|
try
|
|
{
|
|
using(SevenZipArchive arc = SevenZipArchive.Open(stream))
|
|
{
|
|
if(!arc.IsComplete) return "downloaded Updater package is not complete.";
|
|
IReader reader = arc.ExtractAllEntries();
|
|
|
|
// Unpack all
|
|
while(reader.MoveToNextEntry())
|
|
{
|
|
if(reader.Entry.IsDirectory) continue; // Shouldn't be there, but who knows...
|
|
reader.WriteEntryToDirectory(General.AppPath, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
|
|
}
|
|
}
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
return "failed to unpack the Updater: " + e.Message;
|
|
}
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
private static void ShowResult(string message)
|
|
{
|
|
if(!string.IsNullOrEmpty(message))
|
|
{
|
|
if(verbose)
|
|
{
|
|
if(General.MainWindow.InvokeRequired)
|
|
General.MainWindow.Invoke(new ShowWarningMessageDelegate(General.ShowWarningMessage), new object[] { message, MessageBoxButtons.OK });
|
|
else
|
|
General.ShowWarningMessage(message, MessageBoxButtons.OK);
|
|
}
|
|
else if(message != NO_UPDATE_REQUIRED)
|
|
{
|
|
General.ErrorLogger.Add(ErrorType.Error, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
|
{
|
|
worker = null;
|
|
}
|
|
|
|
private static string GetChangelog(string url, int localrev)
|
|
{
|
|
StringBuilder sb = new StringBuilder(@"{\rtf1");
|
|
|
|
using(MemoryStream stream = DownloadWebFile(Path.Combine(url, "Changelog.xml")))
|
|
{
|
|
if(stream == null) return string.Empty;
|
|
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.Load(stream);
|
|
|
|
// Revision infos go in descending order
|
|
if(doc.ChildNodes.Count == 0) return string.Empty;
|
|
foreach(XmlNode log in doc.ChildNodes)
|
|
{
|
|
if(log.ChildNodes.Count == 0) continue;
|
|
foreach(XmlNode logentry in log.ChildNodes)
|
|
{
|
|
int noderev;
|
|
if(logentry.Attributes == null || !int.TryParse(logentry.Attributes.GetNamedItem("revision").Value, out noderev)) continue;
|
|
if(noderev <= localrev) break;
|
|
|
|
// Add info
|
|
sb.Append(@"{\b R" + noderev + @":}\par ");
|
|
|
|
foreach(XmlNode prop in logentry.ChildNodes)
|
|
{
|
|
if(prop.Name == "msg")
|
|
{
|
|
sb.Append(prop.InnerText.Trim().Replace(Environment.NewLine, @"\par ")).Append(@"\par\par ");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sb.Append("}");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static MemoryStream DownloadWebFile(string url)
|
|
{
|
|
// Open a data stream from the supplied URL
|
|
WebRequest request = WebRequest.Create(url);
|
|
WebResponse response;
|
|
|
|
try
|
|
{
|
|
response = request.GetResponse();
|
|
}
|
|
catch(WebException)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Stream source = response.GetResponseStream();
|
|
if(source == null) return null;
|
|
|
|
// Download the data in chuncks
|
|
byte[] buffer = new byte[1024];
|
|
|
|
// Download the data
|
|
MemoryStream result = new MemoryStream();
|
|
while(!General.MainWindow.IsDisposed)
|
|
{
|
|
// Let's try and read the data
|
|
int numbytes = source.Read(buffer, 0, buffer.Length);
|
|
if(numbytes == 0) break; // Download complete
|
|
|
|
// Write the downloaded data
|
|
result.Write(buffer, 0, numbytes);
|
|
}
|
|
|
|
// Release resources
|
|
source.Close();
|
|
|
|
// Rewind and return the stream
|
|
result.Position = 0;
|
|
return result;
|
|
}
|
|
}
|
|
}
|