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 dicGametypes = new Dictionary(); private static Dictionary> dicDefaultFiles = new Dictionary>(); static ServerInfoListViewAdder() { dicDefaultFiles.Add( ServerQuerier.ServerInfoVer.SIV_PREME, new List( 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( 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 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 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); } } }