Kart-Public/tools/SRB2Updater/Launcher.cs

659 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
//using System.Data.OleDb;
using System.Xml;
//using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Threading;
using System.Security.Cryptography;
//using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.Media;
namespace SRB2Updater
{
public partial class Launcher : Form
{
private Settings settings = new Settings();
private Debug debug = new Debug();
// The thread inside which the download happens
private Thread thrDownload;
private Thread thrTotal;
// The stream of data retrieved from the web server
private Stream strResponse;
// The stream of data that we write to the harddrive
private Stream strLocal;
// The request to the web server for file information
private HttpWebRequest webRequest;
// The response from the web server containing information about the file
private HttpWebResponse webResponse;
// The progress of the download in percentage
private static int PercentProgress;
private static int OverallPercentProgress;
// Overall progress as a percentage
private static Int64 OverallProgress;
// Progress stored, for calculating overall
private static Int64 CurrentProgress;
// Total File Size of entire update
private static Int64 TotalSize;
// The delegate which we will call from the thread to update the form
private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes);
private delegate void OverallProgessCallback(Int64 BytesRead);
// When to pause
bool goPause = false;
// Download Details
string downFile;
// Updating
bool filesGot = false;
bool downloadStatus = false;
bool doneCalculate = false;
string formTitle = "Sonic Robo Blast 2 Launcher";
bool loadedBat = false;
ProcessStartInfo startinfo = new ProcessStartInfo();
private ServerQuerier sq;
private string MSFail;
public Launcher(string[] args)
{
InitializeComponent();
settings.GetSettings();
sq = new ServerQuerier();
ServerInfoListViewAdder silva = new ServerInfoListViewAdder(sq, this);
try
{
sq.SetMasterServer(settings.msAddress, Convert.ToUInt16(settings.msPort));
}
catch (Exception exception)
{
MSFail = exception.Message;
}
sq.StartListening(silva);
backgroundWorkerQueryServers.RunWorkerAsync();
foreach (string arg in args)
{
if (arg == "-debug")
{
debug.Show();
break;
}
}
RandomBanner();
}
public string getMD5(string filename)
{
StringBuilder sb = new StringBuilder();
FileInfo f = new FileInfo(filename);
FileStream fs = f.OpenRead();
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash(fs);
fs.Close();
foreach (byte hex in hash)
sb.Append(hex.ToString("x2"));
string md5sum = sb.ToString();
return md5sum;
}
public void PlayIt()
{
SoundPlayer player = new SoundPlayer(Properties.Resources.Kotaku);
player.Play();
}
public void RandomBanner()
{
Random random = new Random();
int rand = random.Next(0, 4);
//this.bannerRandom.BackgroundImage = global::SRB2Updater.Properties.Resources.Banner;
switch (rand)
{
case 0:
bannerRandom.BackgroundImage = global::SRB2Updater.Properties.Resources.Banner;
break;
case 1:
bannerRandom.BackgroundImage = global::SRB2Updater.Properties.Resources.Banner2;
break;
case 2:
bannerRandom.BackgroundImage = global::SRB2Updater.Properties.Resources.Banner3;
break;
case 3:
bannerRandom.BackgroundImage = global::SRB2Updater.Properties.Resources.Banner4;
break;
default:
bannerRandom.BackgroundImage = global::SRB2Updater.Properties.Resources.Banner;
break;
}
debug.strRandom = Convert.ToString(rand);
}
private void updateList(bool doCalculate)
{
if (filesGot == false)
{
XmlDataDocument xmlDatadoc = new XmlDataDocument();
xmlDatadoc.DataSet.ReadXml("http://update.srb2.org/files_beta/files_beta.xml");
DataSet ds = new DataSet("Files DataSet");
ds = xmlDatadoc.DataSet;
fileList.DataSource = ds.DefaultViewManager;
fileList.DataMember = "File";
filesGot = true;
}
if (downloadStatus == false)
{
foreach (DataGridViewRow fileRow in fileList.Rows)
{
if (!File.Exists(fileRow.Cells["filename"].Value.ToString()) && fileRow.Cells["filename"].Value.ToString() != "srb2update.update")
{
// fileRow.DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
fileRow.Cells["localmd5"].Value = "not_found";
} else {
if (fileRow.Cells["filename"].Value.ToString() == "srb2update.update")
fileRow.Cells["localmd5"].Value = getMD5("srb2update.exe");
else
fileRow.Cells["localmd5"].Value = getMD5(fileRow.Cells["filename"].Value.ToString());
}
if (fileRow.Cells["localmd5"].Value.ToString() != fileRow.Cells["md5"].Value.ToString())
{
// if (fileRow.Cells["localmd5"].Value.ToString() != "not_found")
// fileRow.DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(224)))), ((int)(((byte)(206)))));
fileRow.Cells["status"].Value = "Queued";
}
else
{
fileRow.Cells["status"].Value = "Up to date";
}
}
if (doCalculate)
{
thrTotal = new Thread(new ParameterizedThreadStart(CalculateTotalSize));
thrTotal.Start(0);
}
if (doneCalculate)
{
foreach (DataGridViewRow fileRow in fileList.Rows)
{
if (fileRow.Cells["localmd5"].Value.ToString() != fileRow.Cells["md5"].Value.ToString())
{
if (fileRow.Cells["optional"].Value.ToString() == "1" && !update_optional.Checked)
fileRow.Cells["Status"].Value = "Skipped (Optional)";
else
{
downFile = fileRow.Cells["filename"].Value.ToString();
thrDownload = new Thread(new ParameterizedThreadStart(Download));
thrDownload.Start(0);
fileRow.Cells["Status"].Value = "Downloading...";
downloadStatus = true;
break;
}
}
else
{
fileRow.Cells["Status"].Value = "Up to date";
}
CurrentProgress = 0;
}
}
}
}
private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
{
// Calculate the download progress in percentages
PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
// Make progress on the progress bar
progress_currentFile.Value = PercentProgress;
// Display the current progress on the form
lblProgress.Text = downFile + " - " + (BytesRead / 1024) + "KB of " + (TotalBytes / 1024) + "KB (" + PercentProgress + "%)";
this.Text = formTitle + " :: Downloading " + downFile + " (" + PercentProgress + "%)";
debug.strPercent = PercentProgress.ToString() + "%";
if (BytesRead >= TotalBytes - 1)
updateList(false);
}
private void UpdateOverallProgress(Int64 BytesRead)
{
// Calculate progress change and add to OverallProgress...
OverallProgress += BytesRead - CurrentProgress;
// Calculate the download progress in percentages
if (TotalSize < 1)
TotalSize = 1;
OverallPercentProgress = Convert.ToInt32((OverallProgress * 100) / TotalSize);
// Make progress on the progress bar
if (OverallPercentProgress > 100)
OverallPercentProgress = 100;
progress_overall.Value = OverallPercentProgress;
if (OverallProgress >= TotalSize)
{
lblProgress.Text = "Done";
btnCheckFiles.Enabled = true;
}
CurrentProgress = BytesRead;
debug.strCurrent = Convert.ToString(CurrentProgress) + " bytes";
debug.strOverall = Convert.ToString(OverallProgress) + " bytes";
debug.strOverallPercentage = Convert.ToString(OverallPercentProgress) + "%";
debug.strRead = Convert.ToString(BytesRead) + " bytes";
debug.strTotal = Convert.ToString(TotalSize) + " bytes";
}
private void CalculateTotalSize(object startpoint)
{
foreach (DataGridViewRow fileRow in fileList.Rows)
{
if ((fileRow.Cells["optional"].Value.ToString() == "0" || update_optional.Checked) && fileRow.Cells["localmd5"].Value.ToString() != fileRow.Cells["md5"].Value.ToString())
{
try
{
// Create a request to the file we are downloading
webRequest = (HttpWebRequest)WebRequest.Create("http://update.srb2.org/files_beta/" + fileRow.Cells["filename"].Value.ToString());
// Set the starting point of the request
webRequest.AddRange(0);
// Set default authentication for retrieving the file
webRequest.Credentials = CredentialCache.DefaultCredentials;
// Retrieve the response from the server
webResponse = (HttpWebResponse)webRequest.GetResponse();
// Ask the server for the file size and store it
Int64 fileSize = webResponse.ContentLength;
TotalSize = TotalSize + fileSize;
}
finally
{
// When the above code has ended, close the streams
webResponse.Close();
}
}
}
doneCalculate = true;
updateList(false);
}
private void Download(object startpoint)
{
try
{
string filename = Convert.ToString(startpoint);
// Create a request to the file we are downloading
webRequest = (HttpWebRequest)WebRequest.Create("http://update.srb2.org/files_beta/" + downFile);
// Set the starting point of the request
webRequest.AddRange(0);
// Set default authentication for retrieving the file
webRequest.Credentials = CredentialCache.DefaultCredentials;
// Retrieve the response from the server
webResponse = (HttpWebResponse)webRequest.GetResponse();
// Ask the server for the file size and store it
Int64 fileSize = webResponse.ContentLength;
// Open the URL for download
strResponse = webResponse.GetResponseStream();
// Create a new file stream where we will be saving the data (local drive)
strLocal = new FileStream(downFile, FileMode.Create, FileAccess.Write, FileShare.None);
// It will store the current number of bytes we retrieved from the server
int bytesSize = 0;
// A buffer for storing and writing the data retrieved from the server
byte[] downBuffer = new byte[2048];
// Loop through the buffer until the buffer is empty
while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
// Write the data from the buffer to the local hard drive
strLocal.Write(downBuffer, 0, bytesSize);
// Invoke the method that updates the form's label and progress bar
this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
this.Invoke(new OverallProgessCallback(this.UpdateOverallProgress), new object[] { strLocal.Length });
if (goPause == true)
{
break;
}
}
}
finally
{
// When the above code has ended, close the streams
strResponse.Close();
strLocal.Close();
// And update the row!
downloadStatus = false;
if (downFile == "srb2update.update" && loadedBat != true)
{
MessageBox.Show("The updater will now restart to apply a patch.", "Self Update", MessageBoxButtons.OK);
CreateUpdaterBat();
startinfo.WindowStyle = ProcessWindowStyle.Hidden;
startinfo.FileName = "srb2update.bat";
System.Diagnostics.Process.Start(startinfo);
downloadStatus = false;
Environment.Exit(0);
} else
updateList(false);
}
}
private void CreateUpdaterBat()
{
File.WriteAllText("srb2update.bat", "ping 127.0.0.1\ncopy srb2update.update srb2update.exe\ndel srb2update.update\nsrb2update.exe\nexit");
}
private void update_Load(object sender, EventArgs e)
{
lblProgress.Text = "Getting File List...";
if (File.Exists("srb2update.bat"))
File.Delete("srb2update.bat");
TotalSize = 0;
CurrentProgress = 0;
OverallPercentProgress = 0;
OverallProgress = 0;
btnCheckFiles.Enabled = false;
updateList(true);
}
private void btnOptions_Click(object sender, EventArgs e)
{
new Options(settings).ShowDialog();
}
private class ServerInfoListViewAdder : ServerQuerier.ServerInfoReceiveHandler
{
private delegate ListViewItem AddToListCallback(ListViewItem lvi);
private Launcher form1;
private Dictionary<byte, string> dicGametypes = new Dictionary<byte, string>();
private static Dictionary<ServerQuerier.ServerInfoVer, List<String>> dicDefaultFiles =
new Dictionary<ServerQuerier.ServerInfoVer, List<String>>();
static ServerInfoListViewAdder()
{
dicDefaultFiles.Add(
ServerQuerier.ServerInfoVer.SIV_PREME,
new List<String>(
new string[] {
"srb2.srb", "sonic.plr", "tails.plr", "knux.plr",
"auto.wpn", "bomb.wpn", "home.wpn", "rail.wpn", "infn.wpn",
"drill.dta", "soar.dta", "music.dta"
})
);
dicDefaultFiles.Add(
ServerQuerier.ServerInfoVer.SIV_ME,
new List<String>(
new string[] {
"srb2.wad", "sonic.plr", "tails.plr", "knux.plr",
"rings.wpn", "drill.dta", "soar.dta", "music.dta"
})
);
}
public ServerInfoListViewAdder(ServerQuerier sq, Launcher form1)
: base(sq)
{
this.form1 = form1;
// Gametypes.
dicGametypes.Add(0, "Co-op");
dicGametypes.Add(1, "Match");
dicGametypes.Add(2, "Race");
dicGametypes.Add(3, "Tag");
dicGametypes.Add(4, "CTF");
dicGametypes.Add(5, "Chaos");
// Don't think these are actually used.
dicGametypes.Add(42, "Team Match");
dicGametypes.Add(43, "Time-Only Race");
}
public override void ProcessServerInfo(ServerQuerier.SRB2ServerInfo srb2si)
{
ListView lv = form1.listViewServers;
// Build a list item.
ListViewItem lvi = new ListViewItem(srb2si.strName);
// So we can get address and whatever else we might need.
lvi.Tag = srb2si;
// Gametype string, or number if not recognised.
if (dicGametypes.ContainsKey(srb2si.byGametype))
lvi.SubItems.Add(dicGametypes[srb2si.byGametype]);
else
lvi.SubItems.Add(Convert.ToString(srb2si.byGametype));
lvi.SubItems.Add(Convert.ToString(srb2si.uiTime));
lvi.SubItems.Add(srb2si.byPlayers + "/" + srb2si.byMaxplayers);
lvi.SubItems.Add(srb2si.strVersion);
// Make the tooltip.
BuildTooltip(lvi, form1.settings.ShowDefaultWads);
// Is the game full?
if (srb2si.byPlayers >= srb2si.byMaxplayers)
lvi.ForeColor = Color.DimGray;
// Modified?
else if (srb2si.bModified)
lvi.ForeColor = Color.Red;
// Thread-safe goodness.
if (lv.InvokeRequired)
{
// Call ourselves in the context of the form's thread.
AddToListCallback addtolistcallback = new AddToListCallback(lv.Items.Add);
lv.Invoke(addtolistcallback, new object[] { lvi });
}
else
{
// Add it!
lv.Items.Add(lvi);
}
}
public override void HandleException(Exception e)
{
}
public static void BuildTooltip(ListViewItem lvi, bool bShowDefaultWads)
{
string strWads = String.Empty;
ServerQuerier.SRB2ServerInfo srb2si = (ServerQuerier.SRB2ServerInfo)lvi.Tag;
foreach (ServerQuerier.AddedWad aw in srb2si.listFiles)
{
List<string> listDefaultFiles = dicDefaultFiles[srb2si.siv];
if (bShowDefaultWads || !listDefaultFiles.Contains(aw.strFilename))
{
strWads += String.Format("\n{0} ({1:f1} KB)", aw.strFilename, Convert.ToSingle(aw.uiSize) / 1024);
if (aw.bImportant)
{
if (aw.downloadtype == ServerQuerier.DownloadTypes.DT_TOOBIG)
strWads += " (too big to download)";
else if (aw.downloadtype == ServerQuerier.DownloadTypes.DT_DISABLED)
strWads += " (downloading disabled)";
}
else strWads += " (unimportant)";
}
}
lvi.ToolTipText = "Current map: " + srb2si.strMapName + "\n";
if (strWads != String.Empty)
lvi.ToolTipText += "Wads added:" + strWads;
else lvi.ToolTipText += "No wads added";
}
}
private void backgroundWorkerQueryServers_DoWork(object sender, DoWorkEventArgs e)
{
MSClient msclient = new MSClient();
try
{
List<MSServerEntry> listServers = msclient.GetServerList(settings.msAddress, Convert.ToUInt16(settings.msPort));
// Query each of the individual servers asynchronously.
foreach (MSServerEntry msse in listServers)
{
sq.Query(msse.strAddress, msse.unPort);
}
}
catch (System.Net.Sockets.SocketException sockexception)
{
MSFail = sockexception.Message;
}
catch (Exception exception)
{
MSFail = exception.Message;
}
}
private void btnRefresh_Click(object sender, EventArgs e)
{
if (!backgroundWorkerQueryServers.IsBusy)
{
// Clear the server list.
listViewServers.Items.Clear();
// Disable the Connect button.
btnConnect.Enabled = false;
// Query the MS and the individual servers in another thread.
backgroundWorkerQueryServers.RunWorkerAsync();
}
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (listViewServers.SelectedItems.Count > 0)
{
ConnectToServerFromListItem(listViewServers.SelectedItems[0]);
}
}
private void ConnectToServerFromListItem(ListViewItem lvi)
{
ServerQuerier.SRB2ServerInfo srb2si = (ServerQuerier.SRB2ServerInfo)lvi.Tag;
// Prompt to get a binary if we need one.
if (!settings.HasBinaryForVersion(srb2si.strVersion) &&
MessageBox.Show("To join this game, you must register an executable file for version " + srb2si.strVersion + ". Would you like to do so?", Text, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes &&
openFileDialog1.ShowDialog() == DialogResult.OK)
{
settings.SetBinary(srb2si.strVersion, openFileDialog1.FileName);
settings.SaveSettings();
}
// Go!
ConnectToServer(srb2si.strAddress, srb2si.unPort, srb2si.strVersion);
}
private void ConnectToServer(string strAddress, ushort unPort, string strVersion)
{
// Make sure we now have a binary.
if (settings.HasBinaryForVersion(strVersion))
{
try
{
string strBinary = settings.GetBinary(strVersion);
string strDirectory = System.IO.Path.GetDirectoryName(strBinary);
if (strDirectory != String.Empty)
System.IO.Directory.SetCurrentDirectory(strDirectory);
System.Diagnostics.Process.Start(strBinary, System.String.Format("-connect {0}:{1} {2}", strAddress, unPort, settings.Params)).Close();
if (settings.CloseOnStart)
Environment.Exit(0);
}
catch (Exception exception)
{
MessageBox.Show("Unable to start SRB2: " + exception.Message + ".", "SRB2 MS Launcher", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private class ListViewSorter : System.Collections.IComparer
{
private int iColumn;
public int Column { get { return iColumn; } }
public SortOrder so;
public ListViewSorter(int iColumn)
{
this.iColumn = iColumn;
so = SortOrder.Ascending;
}
public int Compare(object x, object y)
{
ListViewItem lviX = (ListViewItem)x;
ListViewItem lviY = (ListViewItem)y;
return ((so == SortOrder.Ascending) ? 1 : -1) * String.Compare(lviX.SubItems[iColumn].Text, lviY.SubItems[iColumn].Text);
}
public void ToggleSortOrder()
{
if (so != SortOrder.Ascending)
so = SortOrder.Ascending;
else
so = SortOrder.Descending;
}
}
private void listViewServers_ColumnClick(object sender, ColumnClickEventArgs e)
{
if (listViewServers.ListViewItemSorter != null &&
((ListViewSorter)listViewServers.ListViewItemSorter).Column == e.Column)
{
((ListViewSorter)listViewServers.ListViewItemSorter).ToggleSortOrder();
listViewServers.Sort();
}
else
{
listViewServers.ListViewItemSorter = new ListViewSorter(e.Column);
}
}
private void listViewServers_SelectedIndexChanged(object sender, EventArgs e)
{
btnConnect.Enabled = (listViewServers.SelectedItems.Count > 0);
}
private void listViewServers_ItemActivate(object sender, EventArgs e)
{
ConnectToServerFromListItem(listViewServers.SelectedItems[0]);
}
private void Launcher_FormClosed(object sender, FormClosedEventArgs e)
{
Environment.Exit(0);
}
private Bunny sequence = new Bunny();
private void Launcher_KeyUp(object sender, KeyEventArgs e)
{
if (sequence.IsCompletedBy(e.KeyCode))
{
PlayIt();
}
debug.strKonami = Convert.ToString(sequence.Position);
}
private void btnStartSRB2_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start("srb2win.exe", System.String.Format("{0}", settings.Params)).Close();
if(settings.CloseOnStart)
Environment.Exit(0);
}
}
}