|
|
@@ -1,3 +1,4 @@
|
|
|
+using Gdk;
|
|
|
using Gtk;
|
|
|
using Ryujinx.Common;
|
|
|
using Ryujinx.Common.Configuration;
|
|
|
@@ -13,7 +14,9 @@ using System.Linq;
|
|
|
using System.Net.Http;
|
|
|
using System.Reflection;
|
|
|
using System.Text;
|
|
|
+using System.Text.Json;
|
|
|
using System.Threading.Tasks;
|
|
|
+using Window = Gtk.Window;
|
|
|
|
|
|
namespace Ryujinx.Ui.Windows
|
|
|
{
|
|
|
@@ -49,11 +52,11 @@ namespace Ryujinx.Ui.Windows
|
|
|
|
|
|
public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo")
|
|
|
{
|
|
|
- Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
|
|
|
+ Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
|
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
- _httpClient = new HttpClient()
|
|
|
+ _httpClient = new HttpClient
|
|
|
{
|
|
|
Timeout = TimeSpan.FromSeconds(30),
|
|
|
};
|
|
|
@@ -64,7 +67,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
_amiiboList = new List<AmiiboApi>();
|
|
|
|
|
|
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
|
|
- _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
|
|
|
+ _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes);
|
|
|
|
|
|
_scanButton.Sensitive = false;
|
|
|
_randomUuidCheckBox.Sensitive = false;
|
|
|
@@ -72,17 +75,25 @@ namespace Ryujinx.Ui.Windows
|
|
|
_ = LoadContentAsync();
|
|
|
}
|
|
|
|
|
|
- private bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson)
|
|
|
+ private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson)
|
|
|
{
|
|
|
+ if (string.IsNullOrEmpty(json))
|
|
|
+ {
|
|
|
+ amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
try
|
|
|
{
|
|
|
- amiiboJson = JsonHelper.Deserialize<AmiiboJson>(json, _serializerContext.AmiiboJson);
|
|
|
+ amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
- catch
|
|
|
+ catch (JsonException exception)
|
|
|
{
|
|
|
- amiiboJson = JsonHelper.Deserialize<AmiiboJson>(DefaultJson, _serializerContext.AmiiboJson);
|
|
|
+ Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}");
|
|
|
+ amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
@@ -92,27 +103,41 @@ namespace Ryujinx.Ui.Windows
|
|
|
{
|
|
|
bool localIsValid = false;
|
|
|
bool remoteIsValid = false;
|
|
|
- AmiiboJson amiiboJson = JsonHelper.Deserialize<AmiiboJson>(DefaultJson, _serializerContext.AmiiboJson);
|
|
|
+ AmiiboJson amiiboJson = new();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- localIsValid = TryGetAmiiboJson(File.ReadAllText(_amiiboJsonPath), out amiiboJson);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (File.Exists(_amiiboJsonPath))
|
|
|
+ {
|
|
|
+ localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception exception)
|
|
|
+ {
|
|
|
+ Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}");
|
|
|
+ }
|
|
|
|
|
|
if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated))
|
|
|
{
|
|
|
remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson);
|
|
|
}
|
|
|
}
|
|
|
- catch
|
|
|
+ catch (Exception exception)
|
|
|
{
|
|
|
if (!(localIsValid || remoteIsValid))
|
|
|
{
|
|
|
+ Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
|
|
|
+
|
|
|
// Neither local or remote files are valid JSON, close window.
|
|
|
ShowInfoDialog();
|
|
|
Close();
|
|
|
}
|
|
|
else if (!remoteIsValid)
|
|
|
{
|
|
|
+ Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}");
|
|
|
+
|
|
|
// Only the local file is valid, the local one should be used
|
|
|
// but the user should be warned.
|
|
|
ShowInfoDialog();
|
|
|
@@ -196,11 +221,18 @@ namespace Ryujinx.Ui.Windows
|
|
|
|
|
|
private async Task<bool> NeedsUpdate(DateTime oldLastModified)
|
|
|
{
|
|
|
- HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
|
|
|
+ try
|
|
|
+ {
|
|
|
+ HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
|
|
|
|
|
|
- if (response.IsSuccessStatusCode)
|
|
|
+ if (response.IsSuccessStatusCode)
|
|
|
+ {
|
|
|
+ return response.Content.Headers.LastModified != oldLastModified;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (HttpRequestException exception)
|
|
|
{
|
|
|
- return response.Content.Headers.LastModified != oldLastModified;
|
|
|
+ Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}");
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
@@ -208,29 +240,37 @@ namespace Ryujinx.Ui.Windows
|
|
|
|
|
|
private async Task<string> DownloadAmiiboJson()
|
|
|
{
|
|
|
- HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
|
|
|
-
|
|
|
- if (response.IsSuccessStatusCode)
|
|
|
+ try
|
|
|
{
|
|
|
- string amiiboJsonString = await response.Content.ReadAsStringAsync();
|
|
|
+ HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
|
|
|
|
|
|
- using (FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
|
|
|
+ if (response.IsSuccessStatusCode)
|
|
|
{
|
|
|
- dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
|
|
|
+ string amiiboJsonString = await response.Content.ReadAsStringAsync();
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough);
|
|
|
+ dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
|
|
|
+ }
|
|
|
+ catch (Exception exception)
|
|
|
+ {
|
|
|
+ Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'");
|
|
|
+ }
|
|
|
+
|
|
|
+ return amiiboJsonString;
|
|
|
}
|
|
|
|
|
|
- return amiiboJsonString;
|
|
|
+ Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
|
|
|
}
|
|
|
- else
|
|
|
+ catch (HttpRequestException exception)
|
|
|
{
|
|
|
- Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
|
|
|
-
|
|
|
- GtkDialog.CreateInfoDialog($"Amiibo API", "An error occured while fetching information from the API.");
|
|
|
-
|
|
|
- Close();
|
|
|
+ Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}");
|
|
|
}
|
|
|
|
|
|
- return DefaultJson;
|
|
|
+ GtkDialog.CreateInfoDialog("Amiibo API", "An error occured while fetching information from the API.");
|
|
|
+
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
private async Task UpdateAmiiboPreview(string imageUrl)
|
|
|
@@ -240,7 +280,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
if (response.IsSuccessStatusCode)
|
|
|
{
|
|
|
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
|
|
|
- Gdk.Pixbuf amiiboPreview = new(amiiboPreviewBytes);
|
|
|
+ Pixbuf amiiboPreview = new(amiiboPreviewBytes);
|
|
|
|
|
|
float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width,
|
|
|
(float)_amiiboImage.AllocatedHeight / amiiboPreview.Height);
|
|
|
@@ -248,7 +288,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
int resizeHeight = (int)(amiiboPreview.Height * ratio);
|
|
|
int resizeWidth = (int)(amiiboPreview.Width * ratio);
|
|
|
|
|
|
- _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear);
|
|
|
+ _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, InterpType.Bilinear);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -258,7 +298,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
|
|
|
private static void ShowInfoDialog()
|
|
|
{
|
|
|
- GtkDialog.CreateInfoDialog($"Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.");
|
|
|
+ GtkDialog.CreateInfoDialog("Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.");
|
|
|
}
|
|
|
|
|
|
//
|
|
|
@@ -314,7 +354,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
{
|
|
|
AmiiboId = _amiiboCharsComboBox.ActiveId;
|
|
|
|
|
|
- _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
|
|
|
+ _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes);
|
|
|
|
|
|
string imageUrl = _amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image;
|
|
|
|
|
|
@@ -354,7 +394,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
|
|
|
private void ShowAllCheckBox_Clicked(object sender, EventArgs e)
|
|
|
{
|
|
|
- _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
|
|
|
+ _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes);
|
|
|
|
|
|
_amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed;
|
|
|
_amiiboCharsComboBox.Changed -= CharacterComboBox_Changed;
|
|
|
@@ -365,7 +405,7 @@ namespace Ryujinx.Ui.Windows
|
|
|
_scanButton.Sensitive = false;
|
|
|
_randomUuidCheckBox.Sensitive = false;
|
|
|
|
|
|
- new Task(() => ParseAmiiboData()).Start();
|
|
|
+ new Task(ParseAmiiboData).Start();
|
|
|
}
|
|
|
|
|
|
private void ScanButton_Pressed(object sender, EventArgs args)
|