Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a rudimentary core/dll download form #3937

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<Nullable>disable</Nullable>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
Morilli marked this conversation as resolved.
Show resolved Hide resolved
<Reference Include="System.IO.Compression" />
</ItemGroup>
<ItemGroup>
Expand Down Expand Up @@ -267,10 +267,10 @@
<EmbeddedResource Update="LogWindow.resx" DependentUpon="LogWindow.cs" />
<Compile Update="MainForm.cs" SubType="Form" />
<Compile Update="MainForm.Designer.cs" DependentUpon="MainForm.cs" />
<Compile Update="MainForm.Events.cs" DependentUpon="MainForm.cs" SubType="Form" />
<Compile Update="MainForm.Events.cs" DependentUpon="MainForm.cs" />
<Compile Update="MainForm.FileLoader.cs" DependentUpon="MainForm.cs" SubType="Form" />
<Compile Update="MainForm.Hotkey.cs" DependentUpon="MainForm.cs" SubType="Form" />
<Compile Update="MainForm.Movie.cs" DependentUpon="MainForm.cs" SubType="Form" />
<Compile Update="MainForm.Hotkey.cs" DependentUpon="MainForm.cs" />
<Compile Update="MainForm.Movie.cs" DependentUpon="MainForm.cs" />
<EmbeddedResource Update="MainForm.resx" DependentUpon="MainForm.cs" SubType="Designer" />
<Compile Update="movie/EditCommentsForm.cs" SubType="Form" />
<Compile Update="movie/EditCommentsForm.Designer.cs" DependentUpon="EditCommentsForm.cs" />
Expand Down Expand Up @@ -299,6 +299,7 @@
<Compile Update="RetroAchievements/RAIntegrationDownloaderForm.cs" SubType="Form" />
<Compile Update="RetroAchievements/RAIntegrationDownloaderForm.Designer.cs" DependentUpon="RAIntegrationDownloaderForm.cs" />
<EmbeddedResource Update="RetroAchievements/RAIntegrationDownloaderForm.resx" DependentUpon="RAIntegrationDownloaderForm.cs" />
<Compile Update="CoreDownloaderForm.Designer.cs" DependentUpon="CoreDownloaderForm.cs" />
<Compile Update="RetroAchievements/RCheevos.*.cs" DependentUpon="RCheevos.cs" />
<Compile Update="RetroAchievements/RetroAchievements.*.cs" DependentUpon="RetroAchievements.cs" />
<Compile Update="RomStatusPicker.cs" SubType="Form" />
Expand Down Expand Up @@ -330,9 +331,9 @@
<EmbeddedResource Update="tools/Debugger/BreakpointControl.resx" DependentUpon="BreakpointControl.cs" />
<Compile Update="tools/Debugger/GenericDebugger.cs" SubType="Form" />
<Compile Update="tools/Debugger/GenericDebugger.Designer.cs" DependentUpon="GenericDebugger.cs" />
<Compile Update="tools/Debugger/GenericDebugger.Disassembler.cs" DependentUpon="GenericDebugger.cs" SubType="Form" />
<Compile Update="tools/Debugger/GenericDebugger.IControlMainform.cs" DependentUpon="GenericDebugger.cs" SubType="Form" />
<Compile Update="tools/Debugger/GenericDebugger.IToolForm.cs" DependentUpon="GenericDebugger.cs" SubType="Form" />
<Compile Update="tools/Debugger/GenericDebugger.Disassembler.cs" DependentUpon="GenericDebugger.cs" />
<Compile Update="tools/Debugger/GenericDebugger.IControlMainform.cs" DependentUpon="GenericDebugger.cs" />
<Compile Update="tools/Debugger/GenericDebugger.IToolForm.cs" DependentUpon="GenericDebugger.cs" />
<EmbeddedResource Update="tools/Debugger/GenericDebugger.resx" DependentUpon="GenericDebugger.cs" />
<Compile Update="tools/Debugger/RegisterBoxControl.cs" SubType="UserControl" />
<Compile Update="tools/Debugger/RegisterBoxControl.Designer.cs" DependentUpon="RegisterBoxControl.cs" />
Expand Down
74 changes: 74 additions & 0 deletions src/BizHawk.Client.EmuHawk/CoreDownloaderForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 108 additions & 0 deletions src/BizHawk.Client.EmuHawk/CoreDownloaderForm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using BizHawk.Common;
using BizHawk.Common.PathExtensions;

namespace BizHawk.Client.EmuHawk
{
public partial class CoreDownloaderForm : Form
{
// TODO edit
private static readonly string BaseUrl = $"https://github.com/Morilli/BizHawk-extra/raw/{VersionInfo.MainVersion}/";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having FFmpeg in https://github.com/TASEmulators/ffmpeg-binaries makes sense because it doesn't change. Before hosting any core binaries in a GitHub repo like that, we need to plan ahead and decide on a versioning scheme, a way to check compatibility (in case it's downloaded manually), and an expiration date for URIs (#2235). We also need to decide how dev builds should behave.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, this is one of the things that need to be decided before this can work. In this implementation I was thinking of having a separate repository (just like ffmpeg-binaries) that hosts downloadable dlls for each released bizhawk version in a separate branch, so for release 2.9.2 you would have a 2.9.2 branch with the binaries from that release. Those would of course stay static after the release.

It would probably also be possible to fetch the binaries from the BizHawk repo itself using the release tag, but that feels a little more volatile in regards to future-proofness.
I see your point in that potentially breaking this download in the future because we don't control the url anymore would be bad, but I don't see how this case here differs from the ffmpeg download case.
Compatibility checking could be implemented but I personally wouldn't bother, because as long as we serve proper downloads compatibility should be guaranteed, and if people download stuff on their own, well, that's their own problem (you can already replace any non-bizhawk dlls and cores with different versions).

We also need to decide how dev builds should behave.

I say ignore that case completely, dev builds can stay being full-size downloads and one dev 2.9.2 has little meaning when it could span an entire year of changes, so those downloads don't need to work (or be valid working files) for dev builds.

private static readonly string OutputPath = Path.Combine(PathUtils.DataDirectoryPath, "dll");
Morilli marked this conversation as resolved.
Show resolved Hide resolved

private static readonly string Encore = OSTailoredCode.IsUnixHost ? "libencore.so" : "encore.dll";

private static readonly List<string> AvailableDownloads = [ Encore, "libmamearcade.wbx.zst" ];

private readonly CancellationTokenSource _cancellationTokenSource = new();

public CoreDownloaderForm()
{
InitializeComponent();
}

private void OnLoad(object sender, EventArgs e)
{
for (int i = 0; i < AvailableDownloads.Count; i++)
{
string availableDownload = AvailableDownloads[i];
bool alreadyDownloaded = File.Exists(Path.Combine(OutputPath, availableDownload));
groupBox1.Controls.Add(new CheckBox
{
Text = availableDownload,
Location = new Point(10, 20 + 30 * i),
Checked = alreadyDownloaded,
AutoCheck = false,
AutoSize = true,
});
var downloadButton = new Button
{
Text = "Download",
Location = new Point(150, 19 + 30 * i),
Size = new Size(160, 22),
Enabled = !alreadyDownloaded,
};
downloadButton.Click += DownloadClick;
groupBox1.Controls.Add(downloadButton);
}
}

private async void DownloadClick(object sender, EventArgs e)
{
var sourceButton = (Button)sender;
sourceButton.Enabled = false;
sourceButton.Text = "Downloading...";

// fixme what is this
int downloadIndex = (sourceButton.Location.Y - 19) / 30;
string toDownload = AvailableDownloads[downloadIndex];

try
{
await Download(toDownload);
}
catch (TaskCanceledException)
{
// this is fine; the download was canceled because the form was closed
}
catch (Exception ex)
{
sourceButton.Enabled = true;
sourceButton.Text = "Download";
MessageBox.Show(this, $"Failed to download {toDownload}:\n{ex}", "Download failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

if (File.Exists(Path.Combine(OutputPath, toDownload)))
{
sourceButton.Text = "Download complete!";
((CheckBox)groupBox1.Controls[downloadIndex * 2]).Checked = true;
}
}

private async Task Download(string name)
{
string tempFilename = TempFileManager.GetTempFilename(name, delete: false);

using var httpClient = new HttpClient();
using var stream = await httpClient.GetStreamAsync(BaseUrl + name);
using (var tempFileStream = File.Create(tempFilename))
{
await stream.CopyToAsync(tempFileStream, 256 * 1024, _cancellationTokenSource.Token);
}
File.Move(tempFilename, Path.Combine(OutputPath, name));
}

private void OnClosing(object sender, FormClosingEventArgs e)
{
_cancellationTokenSource.Cancel();
}
}
}
120 changes: 120 additions & 0 deletions src/BizHawk.Client.EmuHawk/CoreDownloaderForm.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

Version 2.0

The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.

Example:

... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>

There are any number of "resheader" rows that contain simple
name/value pairs.

Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.

The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:

Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.

mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
Loading