diff --git a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.cs b/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.cs
index f908be73542..f534f4f711f 100644
--- a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.cs
+++ b/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.cs
@@ -1,162 +1,38 @@
using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Windows.Forms;
using BizHawk.Common;
+using BizHawk.Common.IOExtensions;
namespace BizHawk.Client.EmuHawk
{
- ///
- /// Downloads FFmpeg
- ///
- public partial class FFmpegDownloaderForm : Form
+ public sealed class FFmpegDownloaderForm : DownloaderForm
{
- public FFmpegDownloaderForm()
- {
- InitializeComponent();
-
- txtLocation.Text = FFmpegService.FFmpegPath;
- txtUrl.Text = FFmpegService.Url;
-
- if (OSTailoredCode.IsUnixHost) textBox1.Text = string.Join("\n", textBox1.Text.Split('\n').Take(3)) + "\n\n(Linux user: If installing manually, you can use a symlink.)";
- }
-
- private int pct = 0;
- private bool exiting = false;
- private bool succeeded = false;
- private bool failed = false;
-
- private void ThreadProc()
- {
- Download();
- }
-
- private void Download()
- {
- //the temp file is owned by this thread
- var fn = TempFileManager.GetTempFilename("ffmpeg_download", ".7z", false);
-
- try
- {
- DirectoryInfo parentDir = new(Path.GetDirectoryName(FFmpegService.FFmpegPath)!);
- if (!parentDir.Exists) parentDir.Create();
- using var fs = File.Create(FFmpegService.FFmpegPath); // check writable before bothering with the download
- using (var evt = new ManualResetEvent(false))
- {
- using (var client = new System.Net.WebClient())
- {
- System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
- client.DownloadFileAsync(new Uri(FFmpegService.Url), fn);
- client.DownloadProgressChanged += (object sender, System.Net.DownloadProgressChangedEventArgs e) =>
- {
- pct = e.ProgressPercentage;
- };
- client.DownloadFileCompleted += (object sender, System.ComponentModel.AsyncCompletedEventArgs e) =>
- {
- //we don't really need a status. we'll just try to unzip it when it's done
- evt.Set();
- };
-
- for (; ; )
- {
- if (evt.WaitOne(10))
- break;
-
- //if the gui thread ordered an exit, cancel the download and wait for it to acknowledge
- if (exiting)
- {
- client.CancelAsync();
- evt.WaitOne();
- break;
- }
- }
- }
- }
-
- //throw new Exception("test of download failure");
+ protected override string ComponentName
+ => "FFmpeg";
- //if we were ordered to exit, bail without wasting any more time
- if (exiting)
- return;
+ protected override string DownloadTemp { get; }
+ = TempFileManager.GetTempFilename("ffmpeg_download", ".7z", delete: false);
- //try acquiring file
- using (var hf = new HawkFile(fn))
- {
- using (var exe = OSTailoredCode.IsUnixHost ? hf.BindArchiveMember("ffmpeg") : hf.BindFirstOf(".exe"))
- {
- //last chance. exiting, don't dump the new ffmpeg file
- if (exiting)
- return;
- exe!.GetStream().CopyTo(fs);
- fs.Dispose();
- if (OSTailoredCode.IsUnixHost)
- {
- OSTailoredCode.ConstructSubshell("chmod", $"+x {FFmpegService.FFmpegPath}", checkStdout: false).Start();
- Thread.Sleep(50); // Linux I/O flush idk
- }
- }
- }
-
- //make sure it worked
- if (!FFmpegService.QueryServiceAvailable()) throw new Exception("download failed");
-
- succeeded = true;
- }
- catch (Exception e)
- {
- failed = true;
- Util.DebugWriteLine($"FFmpeg download failed with:\n{e}");
- }
- finally
- {
- try { File.Delete(fn); }
- catch { }
- }
- }
-
- private void btnDownload_Click(object sender, EventArgs e)
+ public FFmpegDownloaderForm()
{
- btnDownload.Text = "Downloading...";
- btnDownload.Enabled = false;
- failed = false;
- succeeded = false;
- pct = 0;
- var t = new Thread(ThreadProc);
- t.Start();
+ Description = "BizHawk relies on a specific version of FFmpeg. No other version will do. The wrong version will be ignored. There is no way to override this behavior."
+ + "\n\nThe required version could not be found."
+ + (OSTailoredCode.IsUnixHost
+ ? "\n\n(Linux user: If installing manually, you can use a symlink.)"
+ : "\n\nUse this dialog to download it automatically, or download it yourself from the URL below and place it in the specified location.");
+ DownloadFrom = FFmpegService.Url;
+ DownloadTo = FFmpegService.FFmpegPath;
}
- private void btnCancel_Click(object sender, EventArgs e)
- {
- Close();
- }
+ protected override Stream GetExtractionStream(HawkFile downloaded)
+ => (OSTailoredCode.IsUnixHost
+ ? downloaded.BindArchiveMember("ffmpeg")!
+ : downloaded.BindFirstOf(".exe")).GetStream();
- protected override void OnClosed(EventArgs e)
- {
- //inform the worker thread that it needs to try terminating without doing anything else
- //(it will linger on in background for a bit til it can service this)
- exiting = true;
- }
-
- private void timer1_Tick(object sender, EventArgs e)
- {
- //if it's done, close the window. the user will be smart enough to reopen it
- if (succeeded)
- Close();
- if (failed)
- {
- failed = false;
- pct = 0;
- btnDownload.Text = "FAILED - Download Again";
- btnDownload.Enabled = true;
- }
- progressBar1.Value = pct;
- }
+ protected override bool PostChmodCheck()
+ => FFmpegService.QueryServiceAvailable();
- private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
- {
- Util.OpenUrlExternal(FFmpegService.Url);
- }
+ protected override bool PreChmodCheck(FileStream extracted)
+ => SHA256Checksum.ComputeDigestHex(extracted.ReadAllBytes()) == FFmpegService.DownloadSHA256Checksum;
}
}
-
diff --git a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.resx b/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.resx
deleted file mode 100644
index a14f7748ebb..00000000000
--- a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.resx
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- BizHawk relies on a specific version of FFmpeg. No other version will do. The wrong version will be ignored. There is no way to override this behavior.
-
-The required version could not be found.
-
-Use this dialog to download it automatically, or download it yourself from the URL below and place it in the specified location
-
-
-
-
- 17, 17
-
-
\ No newline at end of file
diff --git a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.Designer.cs b/src/BizHawk.Client.EmuHawk/DownloaderForm.Designer.cs
similarity index 92%
rename from src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.Designer.cs
rename to src/BizHawk.Client.EmuHawk/DownloaderForm.Designer.cs
index 79f079f9efe..3ebda1cbecb 100644
--- a/src/BizHawk.Client.EmuHawk/AVOut/FFmpegDownloaderForm.Designer.cs
+++ b/src/BizHawk.Client.EmuHawk/DownloaderForm.Designer.cs
@@ -1,6 +1,6 @@
namespace BizHawk.Client.EmuHawk
{
- partial class FFmpegDownloaderForm
+ partial class DownloaderForm
{
///
/// Required designer variable.
@@ -29,7 +29,6 @@ protected override void Dispose(bool disposing)
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FFmpegDownloaderForm));
this.btnCancel = new System.Windows.Forms.Button();
this.btnDownload = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
@@ -73,7 +72,7 @@ private void InitializeComponent()
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(699, 95);
this.textBox1.TabIndex = 10;
- this.textBox1.Text = resources.GetString("textBox1.Text");
+ this.textBox1.Text = "%description%";
//
// label3
//
@@ -112,6 +111,7 @@ private void InitializeComponent()
this.txtLocation.ReadOnly = true;
this.txtLocation.Size = new System.Drawing.Size(613, 20);
this.txtLocation.TabIndex = 15;
+ this.txtLocation.Text = "%downloadTo%";
//
// label1
//
@@ -138,8 +138,9 @@ private void InitializeComponent()
this.txtUrl.ReadOnly = true;
this.txtUrl.Size = new System.Drawing.Size(613, 20);
this.txtUrl.TabIndex = 18;
+ this.txtUrl.Text = "%downloadFrom%";
//
- // FFmpegDownloaderForm
+ // DownloaderForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
@@ -159,10 +160,10 @@ private void InitializeComponent()
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(300, 160);
- this.Name = "FFmpegDownloaderForm";
+ this.Name = "DownloaderForm";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Download FFmpeg";
+ this.Text = "%windowTitle%";
this.ResumeLayout(false);
this.PerformLayout();
diff --git a/src/BizHawk.Client.EmuHawk/DownloaderForm.cs b/src/BizHawk.Client.EmuHawk/DownloaderForm.cs
new file mode 100644
index 00000000000..d406fc13662
--- /dev/null
+++ b/src/BizHawk.Client.EmuHawk/DownloaderForm.cs
@@ -0,0 +1,191 @@
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Windows.Forms;
+
+using BizHawk.Common;
+
+namespace BizHawk.Client.EmuHawk
+{
+ public partial class DownloaderForm : Form
+ {
+ protected virtual string ComponentName { get; } = null!;
+
+ public string Description
+ {
+ get => textBox1.Text;
+ init => textBox1.Text = value;
+ }
+
+ public string DownloadFrom
+ {
+ get => txtUrl.Text;
+ init => txtUrl.Text = value;
+ }
+
+ protected virtual string DownloadTemp { get; } = null!;
+
+ public string DownloadTo
+ {
+ get => txtLocation.Text;
+ init => txtLocation.Text = value;
+ }
+
+ public DownloaderForm()
+ {
+ InitializeComponent();
+ Load += (_, _) => Text = $"Download {ComponentName ?? "COMPONENT UNSET"}";
+ }
+
+ private int _pct = 0;
+ private bool _exiting = false;
+ private bool _succeeded = false;
+ private bool _failed = false;
+
+ private Thread/*?*/ _thread = null;
+
+ public bool DownloadSucceeded()
+ {
+ // block until the thread dies
+ while (_thread is { IsAlive: true })
+ {
+ Thread.Sleep(1);
+ }
+
+ return _succeeded;
+ }
+
+ private void ThreadProc()
+ {
+ Download();
+ }
+
+ private void Download()
+ {
+ try
+ {
+ DirectoryInfo parentDir = new(Path.GetDirectoryName(DownloadTo)!);
+ if (!parentDir.Exists) parentDir.Create();
+ // check writable before bothering with the download
+ if (File.Exists(DownloadTo)) File.Delete(DownloadTo);
+ using var fs = File.Create(DownloadTo);
+ using (var evt = new ManualResetEvent(false))
+ {
+ using var client = new WebClient();
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
+ client.DownloadFileAsync(new Uri(DownloadFrom), DownloadTemp);
+ client.DownloadProgressChanged += (_, progressArgs) => _pct = progressArgs.ProgressPercentage;
+ client.DownloadFileCompleted += (_, _) => evt.Set(); // we don't really need a status, we'll just try to unzip it when it's done
+
+ while (true)
+ {
+ if (evt.WaitOne(10)) break;
+
+ //if the gui thread ordered an exit, cancel the download and wait for it to acknowledge
+ if (_exiting)
+ {
+ client.CancelAsync();
+ evt.WaitOne();
+ break;
+ }
+ }
+ }
+
+// throw new Exception("test of download failure");
+
+ //if we were ordered to exit, bail without wasting any more time
+ if (_exiting) return;
+
+ //try acquiring file
+ using (HawkFile hf = new(DownloadTemp))
+ {
+ using var exStream = GetExtractionStream(hf);
+ //last chance. exiting, don't dump the new file
+ if (_exiting) return;
+ exStream.CopyTo(fs);
+ fs.Position = 0L;
+ if (!PreChmodCheck(fs)) throw new Exception("download failed (pre-chmod validation)");
+ fs.Dispose();
+ if (OSTailoredCode.IsUnixHost)
+ {
+ var proc = OSTailoredCode.ConstructSubshell("chmod", $"+x {DownloadTo}", checkStdout: false);
+ proc.Start();
+ proc.WaitForExit();
+ }
+ }
+
+ //make sure it worked
+ if (!PostChmodCheck()) throw new Exception("download failed (post-chmod validation)");
+
+ _succeeded = true;
+ }
+ catch (Exception e)
+ {
+ _failed = true;
+ Util.DebugWriteLine($"{ComponentName} download failed with:\n{e}");
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(DownloadTemp);
+ }
+ catch
+ {
+ // ignore
+ }
+ }
+ }
+
+ protected virtual Stream GetExtractionStream(HawkFile downloaded)
+ => throw new NotImplementedException();
+
+ protected virtual bool PostChmodCheck()
+ => true;
+
+ protected virtual bool PreChmodCheck(FileStream extracted)
+ => true;
+
+ private void btnDownload_Click(object sender, EventArgs e)
+ {
+ btnDownload.Text = "Downloading...";
+ btnDownload.Enabled = false;
+ _failed = false;
+ _succeeded = false;
+ _pct = 0;
+ _thread = new(ThreadProc);
+ _thread.Start();
+ }
+
+ private void btnCancel_Click(object sender, EventArgs e)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ //inform the worker thread that it needs to try terminating without doing anything else
+ //(it will linger on in background for a bit til it can service this)
+ _exiting = true;
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ //if it's done, close the window. the user will be smart enough to reopen it
+ if (_succeeded) Close();
+ if (_failed)
+ {
+ _failed = false;
+ _pct = 0;
+ btnDownload.Text = "FAILED - Download Again";
+ btnDownload.Enabled = true;
+ }
+ progressBar1.Value = _pct;
+ }
+
+ private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ Util.OpenUrlExternal(DownloadFrom);
+ }
+ }
+}
diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.resx b/src/BizHawk.Client.EmuHawk/DownloaderForm.resx
similarity index 94%
rename from src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.resx
rename to src/BizHawk.Client.EmuHawk/DownloaderForm.resx
index aac33d5a29f..29dcb1b3a35 100644
--- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.resx
+++ b/src/BizHawk.Client.EmuHawk/DownloaderForm.resx
@@ -117,7 +117,4 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- 17, 17
-
\ No newline at end of file
diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.Designer.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.Designer.cs
deleted file mode 100644
index ad2438afc4a..00000000000
--- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.Designer.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-namespace BizHawk.Client.EmuHawk
-{
- partial class RAIntegrationDownloaderForm
- {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- this.components = new System.ComponentModel.Container();
- this.btnCancel = new System.Windows.Forms.Button();
- this.btnDownload = new System.Windows.Forms.Button();
- this.label3 = new BizHawk.WinForms.Controls.LocLabelEx();
- this.linkLabel1 = new System.Windows.Forms.LinkLabel();
- this.progressBar1 = new System.Windows.Forms.ProgressBar();
- this.timer1 = new System.Windows.Forms.Timer(this.components);
- this.txtLocation = new System.Windows.Forms.TextBox();
- this.label1 = new System.Windows.Forms.Label();
- this.label2 = new System.Windows.Forms.Label();
- this.txtUrl = new System.Windows.Forms.TextBox();
- this.SuspendLayout();
- //
- // btnCancel
- //
- this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.btnCancel.Location = new System.Drawing.Point(667, 86);
- this.btnCancel.Name = "btnCancel";
- this.btnCancel.Size = new System.Drawing.Size(75, 23);
- this.btnCancel.TabIndex = 7;
- this.btnCancel.Text = "Cancel";
- this.btnCancel.UseVisualStyleBackColor = true;
- this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
- //
- // btnDownload
- //
- this.btnDownload.Location = new System.Drawing.Point(9, 86);
- this.btnDownload.Name = "btnDownload";
- this.btnDownload.Size = new System.Drawing.Size(186, 23);
- this.btnDownload.TabIndex = 6;
- this.btnDownload.Text = "Download";
- this.btnDownload.UseVisualStyleBackColor = true;
- this.btnDownload.Click += new System.EventHandler(this.btnDownload_Click);
- //
- // label3
- //
- this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label3.Location = new System.Drawing.Point(6, -35);
- this.label3.MaximumSize = new System.Drawing.Size(260, 0);
- this.label3.Name = "label3";
- //
- // linkLabel1
- //
- this.linkLabel1.AutoSize = true;
- this.linkLabel1.Location = new System.Drawing.Point(715, 61);
- this.linkLabel1.Name = "linkLabel1";
- this.linkLabel1.Size = new System.Drawing.Size(27, 13);
- this.linkLabel1.TabIndex = 12;
- this.linkLabel1.TabStop = true;
- this.linkLabel1.Text = "Link";
- this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
- //
- // progressBar1
- //
- this.progressBar1.Location = new System.Drawing.Point(201, 86);
- this.progressBar1.Name = "progressBar1";
- this.progressBar1.Size = new System.Drawing.Size(186, 23);
- this.progressBar1.TabIndex = 13;
- //
- // timer1
- //
- this.timer1.Enabled = true;
- this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
- //
- // txtLocation
- //
- this.txtLocation.Location = new System.Drawing.Point(95, 12);
- this.txtLocation.Name = "txtLocation";
- this.txtLocation.ReadOnly = true;
- this.txtLocation.Size = new System.Drawing.Size(613, 20);
- this.txtLocation.TabIndex = 15;
- //
- // label1
- //
- this.label1.AutoSize = true;
- this.label1.Location = new System.Drawing.Point(9, 15);
- this.label1.Name = "label1";
- this.label1.Size = new System.Drawing.Size(80, 13);
- this.label1.TabIndex = 16;
- this.label1.Text = "Local Location:";
- //
- // label2
- //
- this.label2.AutoSize = true;
- this.label2.Location = new System.Drawing.Point(57, 41);
- this.label2.Name = "label2";
- this.label2.Size = new System.Drawing.Size(32, 13);
- this.label2.TabIndex = 17;
- this.label2.Text = "URL:";
- //
- // txtUrl
- //
- this.txtUrl.Location = new System.Drawing.Point(96, 38);
- this.txtUrl.Name = "txtUrl";
- this.txtUrl.ReadOnly = true;
- this.txtUrl.Size = new System.Drawing.Size(613, 20);
- this.txtUrl.TabIndex = 18;
- //
- // RAIntegrationDownloaderForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.CancelButton = this.btnCancel;
- this.ClientSize = new System.Drawing.Size(754, 121);
- this.Controls.Add(this.progressBar1);
- this.Controls.Add(this.txtUrl);
- this.Controls.Add(this.btnDownload);
- this.Controls.Add(this.label2);
- this.Controls.Add(this.linkLabel1);
- this.Controls.Add(this.label1);
- this.Controls.Add(this.txtLocation);
- this.Controls.Add(this.btnCancel);
- this.Controls.Add(this.label3);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.MinimumSize = new System.Drawing.Size(300, 160);
- this.Name = "RAIntegrationDownloaderForm";
- this.ShowIcon = false;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Download RAIntegration";
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
-
- #endregion
- private BizHawk.WinForms.Controls.LocLabelEx label3;
- private System.Windows.Forms.Button btnCancel;
- private System.Windows.Forms.Button btnDownload;
- private System.Windows.Forms.LinkLabel linkLabel1;
- private System.Windows.Forms.ProgressBar progressBar1;
- private System.Windows.Forms.Timer timer1;
- private System.Windows.Forms.TextBox txtLocation;
- private System.Windows.Forms.Label label1;
- private System.Windows.Forms.Label label2;
- private System.Windows.Forms.TextBox txtUrl;
- }
-}
\ No newline at end of file
diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.cs
index cea938757ed..44a1a4f9a63 100644
--- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.cs
+++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegrationDownloaderForm.cs
@@ -1,166 +1,32 @@
using System.IO;
-using System.Threading;
-using System.Windows.Forms;
using BizHawk.Common;
using BizHawk.Common.PathExtensions;
+using BizHawk.Common.StringExtensions;
namespace BizHawk.Client.EmuHawk
{
- ///
- /// Downloads RAIntegration (largely a copy paste of ffmpeg's downloader)
- ///
- public partial class RAIntegrationDownloaderForm : Form
+ public sealed class RAIntegrationDownloaderForm : DownloaderForm
{
- public RAIntegrationDownloaderForm(string url)
- {
- _path = Path.Combine(PathUtils.DataDirectoryPath, "dll", "RA_Integration-x64.dll");
- _url = url;
-
- InitializeComponent();
-
- txtLocation.Text = _path;
- txtUrl.Text = _url;
- }
-
- private readonly string _path;
- private readonly string _url;
-
- private int _pct = 0;
- private bool _exiting = false;
- private bool _succeeded = false;
- private bool _failed = false;
- private Thread _thread;
-
- public bool DownloadSucceeded()
- {
- // block until the thread dies
- while (_thread?.IsAlive ?? false)
- {
- Thread.Sleep(1);
- }
-
- return _succeeded;
- }
-
- private void ThreadProc()
- {
- Download();
- }
-
- private void Download()
- {
- //the temp file is owned by this thread
- var fn = TempFileManager.GetTempFilename("RAIntegration_download", ".dll", false);
-
- try
- {
- using (var evt = new ManualResetEvent(false))
- {
- using var client = new System.Net.WebClient();
- System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
- client.DownloadFileAsync(new Uri(_url), fn);
- client.DownloadProgressChanged += (object sender, System.Net.DownloadProgressChangedEventArgs e) =>
- {
- _pct = e.ProgressPercentage;
- };
- client.DownloadFileCompleted += (object sender, System.ComponentModel.AsyncCompletedEventArgs e) =>
- {
- //we don't really need a status. we'll just try to unzip it when it's done
- evt.Set();
- };
-
- for (; ; )
- {
- if (evt.WaitOne(10))
- break;
-
- //if the gui thread ordered an exit, cancel the download and wait for it to acknowledge
- if (_exiting)
- {
- client.CancelAsync();
- evt.WaitOne();
- break;
- }
- }
- }
-
- //throw new Exception("test of download failure");
+ protected override string ComponentName
+ => "RAIntegration";
- //if we were ordered to exit, bail without wasting any more time
- if (_exiting)
- return;
+ protected override string DownloadTemp { get; }
+ = TempFileManager.GetTempFilename("RAIntegration_download", ".dll", delete: false);
- //try acquiring file
- using (var dll = new HawkFile(fn))
- {
- var data = dll!.ReadAllBytes();
-
- //last chance. exiting, don't dump the new RAIntegration file
- if (_exiting)
- return;
-
- DirectoryInfo parentDir = new(Path.GetDirectoryName(_path)!);
- if (!parentDir.Exists) parentDir.Create();
- if (File.Exists(_path)) File.Delete(_path);
- File.WriteAllBytes(_path, data);
- }
-
- _succeeded = true;
- }
- catch
- {
- _failed = true;
- }
- finally
- {
- try { File.Delete(fn); }
- catch { }
- }
- }
-
- private void btnDownload_Click(object sender, EventArgs e)
- {
- btnDownload.Text = "Downloading...";
- btnDownload.Enabled = false;
- _failed = false;
- _succeeded = false;
- _pct = 0;
- _thread = new Thread(ThreadProc);
- _thread.Start();
- }
-
- private void btnCancel_Click(object sender, EventArgs e)
- {
- Close();
- }
-
- protected override void OnClosed(EventArgs e)
+ public RAIntegrationDownloaderForm(string downloadFrom)
{
- //inform the worker thread that it needs to try terminating without doing anything else
- //(it will linger on in background for a bit til it can service this)
- _exiting = true;
- }
-
- private void timer1_Tick(object sender, EventArgs e)
- {
- //if it's done, close the window. the user will be smart enough to reopen it
- if (_succeeded)
- Close();
- if (_failed)
+ var downloadDomainName = downloadFrom.RemovePrefix("https://").SubstringBefore('/');
+ if (!(downloadDomainName is "retroachievements.org" || downloadDomainName.EndsWith(".retroachievements.org")))
{
- _failed = false;
- _pct = 0;
- btnDownload.Text = "FAILED - Download Again";
- btnDownload.Enabled = true;
+ throw new ArgumentException(paramName: nameof(downloadFrom), message: "untrusted hostname");
}
- progressBar1.Value = _pct;
+ Description = string.Empty;
+ DownloadFrom = downloadFrom;
+ DownloadTo = Path.Combine(PathUtils.DataDirectoryPath, "dll", "RA_Integration-x64.dll");
}
- private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
- {
- Util.OpenUrlExternal(_url);
- }
+ protected override Stream GetExtractionStream(HawkFile downloaded)
+ => downloaded.GetStream();
}
}
-
diff --git a/src/BizHawk.Common/FFmpegService.cs b/src/BizHawk.Common/FFmpegService.cs
index 8557cc150be..f6e71e7179b 100644
--- a/src/BizHawk.Common/FFmpegService.cs
+++ b/src/BizHawk.Common/FFmpegService.cs
@@ -17,8 +17,15 @@ public static class FFmpegService
private const string BIN_HOST_URI_WIN_X64 = "https://github.com/TASEmulators/ffmpeg-binaries/raw/master/ffmpeg-4.4.1-static-windows-x64.7z";
+ private const string BIN_SHA256_LINUX_X64 = "3EA58083710F63BF920B16C7D5D24AE081E7D731F57A656FED11AF0410D4EB48";
+
+ private const string BIN_SHA256_WIN_X64 = "8436760AF8F81C95EFF92D854A7684E6D3CEDB872888420359FC45C8EB2664AC";
+
private const string VERSION = "ffmpeg version 4.4.1";
+ public static string DownloadSHA256Checksum
+ => OSTailoredCode.IsUnixHost ? BIN_SHA256_LINUX_X64 : BIN_SHA256_WIN_X64;
+
public static string FFmpegPath => Path.Combine(PathUtils.DataDirectoryPath, "dll", OSTailoredCode.IsUnixHost ? "ffmpeg" : "ffmpeg.exe");
public static readonly string Url = OSTailoredCode.IsUnixHost ? BIN_HOST_URI_LINUX_X64 : BIN_HOST_URI_WIN_X64;