Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c532118d94 | ||
|
52d6f2e656 | ||
|
c9bc4eaf58 | ||
|
3249f8ff41 | ||
|
1b41b285ac | ||
|
f5a6f45b27 | ||
|
210557951b |
@@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
|
|||||||
|
|
||||||
#### C# Coding Conventions ####
|
#### C# Coding Conventions ####
|
||||||
|
|
||||||
|
# Namespace preferences
|
||||||
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
|
resharper_csharp_namespace_body = block_scoped
|
||||||
|
|
||||||
# var preferences
|
# var preferences
|
||||||
csharp_style_var_elsewhere = false:silent
|
csharp_style_var_elsewhere = false:silent
|
||||||
csharp_style_var_for_built_in_types = false:silent
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
namespace ARMeilleure.Decoders;
|
namespace ARMeilleure.Decoders
|
||||||
|
|
||||||
interface IOpCode32Exception
|
|
||||||
{
|
{
|
||||||
|
interface IOpCode32Exception
|
||||||
|
{
|
||||||
int Id { get; }
|
int Id { get; }
|
||||||
|
}
|
||||||
}
|
}
|
@@ -3,17 +3,17 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageVersion Include="DynamicData" Version="7.12.11" />
|
<PackageVersion Include="DynamicData" Version="7.13.1" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
@@ -48,6 +48,6 @@
|
|||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
{
|
{
|
||||||
var localeStrings = new Dictionary<LocaleKeys, string>();
|
var localeStrings = new Dictionary<LocaleKeys, string>();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
foreach (var item in strings)
|
foreach (var item in strings)
|
||||||
{
|
{
|
||||||
|
@@ -4,13 +4,14 @@ using FluentAvalonia.UI.Controls;
|
|||||||
using ICSharpCode.SharpZipLib.GZip;
|
using ICSharpCode.SharpZipLib.GZip;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Ryujinx.Ava;
|
using Ryujinx.Ava;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
using Ryujinx.Ui.Common.Models.Github;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -31,6 +32,7 @@ namespace Ryujinx.Modules
|
|||||||
internal static class Updater
|
internal static class Updater
|
||||||
{
|
{
|
||||||
private const string GitHubApiURL = "https://api.github.com";
|
private const string GitHubApiURL = "https://api.github.com";
|
||||||
|
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||||
@@ -99,22 +101,16 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
|
||||||
JToken assets = jsonRoot["assets"];
|
_buildVer = fetched.Name;
|
||||||
|
|
||||||
_buildVer = (string)jsonRoot["name"];
|
foreach (var asset in fetched.Assets)
|
||||||
|
|
||||||
foreach (JToken asset in assets)
|
|
||||||
{
|
{
|
||||||
string assetName = (string)asset["name"];
|
if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt))
|
||||||
string assetState = (string)asset["state"];
|
|
||||||
string downloadURL = (string)asset["browser_download_url"];
|
|
||||||
|
|
||||||
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
|
|
||||||
{
|
{
|
||||||
_buildUrl = downloadURL;
|
_buildUrl = asset.BrowserDownloadUrl;
|
||||||
|
|
||||||
if (assetState != "uploaded")
|
if (asset.State != "uploaded")
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
|
@@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
|
||||||
{
|
|
||||||
public class Amiibo
|
|
||||||
{
|
|
||||||
public struct AmiiboJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; }
|
|
||||||
[JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AmiiboApi
|
|
||||||
{
|
|
||||||
[JsonPropertyName("name")] public string Name { get; set; }
|
|
||||||
[JsonPropertyName("head")] public string Head { get; set; }
|
|
||||||
[JsonPropertyName("tail")] public string Tail { get; set; }
|
|
||||||
[JsonPropertyName("image")] public string Image { get; set; }
|
|
||||||
[JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; }
|
|
||||||
[JsonPropertyName("character")] public string Character { get; set; }
|
|
||||||
[JsonPropertyName("gameSeries")] public string GameSeries { get; set; }
|
|
||||||
[JsonPropertyName("type")] public string Type { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetId()
|
|
||||||
{
|
|
||||||
return Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is AmiiboApi amiibo)
|
|
||||||
{
|
|
||||||
return amiibo.Head + amiibo.Tail == Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiGamesSwitch
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameID")] public List<string> GameId { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameName")] public string GameName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiUsage
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Usage")] public string Usage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("write")] public bool Write { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||||
|
|
||||||
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n";
|
Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)) + "\n\n";
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -4,11 +4,11 @@ using Avalonia.Media.Imaging;
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -17,6 +17,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
@@ -31,8 +32,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private readonly StyleableWindow _owner;
|
private readonly StyleableWindow _owner;
|
||||||
|
|
||||||
private Bitmap _amiiboImage;
|
private Bitmap _amiiboImage;
|
||||||
private List<Amiibo.AmiiboApi> _amiiboList;
|
private List<AmiiboApi> _amiiboList;
|
||||||
private AvaloniaList<Amiibo.AmiiboApi> _amiibos;
|
private AvaloniaList<AmiiboApi> _amiibos;
|
||||||
private ObservableCollection<string> _amiiboSeries;
|
private ObservableCollection<string> _amiiboSeries;
|
||||||
|
|
||||||
private int _amiiboSelectedIndex;
|
private int _amiiboSelectedIndex;
|
||||||
@@ -42,6 +43,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _useRandomUuid;
|
private bool _useRandomUuid;
|
||||||
private string _usage;
|
private string _usage;
|
||||||
|
|
||||||
|
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
@@ -52,9 +55,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
||||||
|
|
||||||
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
||||||
_amiiboList = new List<Amiibo.AmiiboApi>();
|
_amiiboList = new List<AmiiboApi>();
|
||||||
_amiiboSeries = new ObservableCollection<string>();
|
_amiiboSeries = new ObservableCollection<string>();
|
||||||
_amiibos = new AvaloniaList<Amiibo.AmiiboApi>();
|
_amiibos = new AvaloniaList<AmiiboApi>();
|
||||||
|
|
||||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AvaloniaList<Amiibo.AmiiboApi> AmiiboList
|
public AvaloniaList<AmiiboApi> AmiiboList
|
||||||
{
|
{
|
||||||
get => _amiibos;
|
get => _amiibos;
|
||||||
set
|
set
|
||||||
@@ -187,9 +190,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (File.Exists(_amiiboJsonPath))
|
if (File.Exists(_amiiboJsonPath))
|
||||||
{
|
{
|
||||||
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@@ -206,7 +209,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
|
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
ParseAmiiboData();
|
ParseAmiiboData();
|
||||||
@@ -223,7 +226,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!ShowAllAmiibo)
|
if (!ShowAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@@ -255,7 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private void SelectLastScannedAmiibo()
|
private void SelectLastScannedAmiibo()
|
||||||
{
|
{
|
||||||
Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
||||||
|
|
||||||
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
|
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
|
||||||
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
|
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
|
||||||
@@ -270,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList
|
List<AmiiboApi> amiiboSortedList = _amiiboList
|
||||||
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
|
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
|
||||||
.OrderBy(amiibo => amiibo.Name).ToList();
|
.OrderBy(amiibo => amiibo.Name).ToList();
|
||||||
|
|
||||||
@@ -280,7 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!_showAllAmiibo)
|
if (!_showAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@@ -314,7 +317,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
||||||
|
|
||||||
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
||||||
|
|
||||||
@@ -326,11 +329,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
bool writable = false;
|
bool writable = false;
|
||||||
|
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (item.GameId.Contains(TitleId))
|
if (item.GameId.Contains(TitleId))
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage)
|
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
||||||
{
|
{
|
||||||
usageString += Environment.NewLine +
|
usageString += Environment.NewLine +
|
||||||
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
||||||
|
@@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _isLoaded;
|
private bool _isLoaded;
|
||||||
private readonly UserControl _owner;
|
private readonly UserControl _owner;
|
||||||
|
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
||||||
public IGamepad SelectedGamepad { get; private set; }
|
public IGamepad SelectedGamepad { get; private set; }
|
||||||
|
|
||||||
@@ -706,10 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream stream = File.OpenRead(path))
|
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
||||||
{
|
|
||||||
config = JsonHelper.Deserialize<InputConfig>(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (JsonException) { }
|
catch (JsonException) { }
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
@@ -775,7 +774,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
|
|
||||||
string jsonString = JsonHelper.Serialize(config, true);
|
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
|
||||||
|
|
||||||
await File.WriteAllTextAsync(path, jsonString);
|
await File.WriteAllTextAsync(path, jsonString);
|
||||||
|
|
||||||
|
@@ -21,7 +21,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
@@ -41,6 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private ulong _titleId;
|
private ulong _titleId;
|
||||||
private string _titleName;
|
private string _titleName;
|
||||||
|
|
||||||
|
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
||||||
{
|
{
|
||||||
get => _downloadableContents;
|
get => _downloadableContents;
|
||||||
@@ -100,7 +101,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath);
|
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -330,10 +331,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
_downloadableContentContainerList.Add(container);
|
_downloadableContentContainerList.Add(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough))
|
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer);
|
||||||
{
|
|
||||||
downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -22,14 +22,13 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels;
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public class TitleUpdateViewModel : BaseModel
|
|
||||||
{
|
{
|
||||||
|
public class TitleUpdateViewModel : BaseModel
|
||||||
|
{
|
||||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
public TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
public readonly string _titleUpdateJsonPath;
|
public readonly string _titleUpdateJsonPath;
|
||||||
private VirtualFileSystem _virtualFileSystem { get; }
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
@@ -40,6 +39,8 @@ public class TitleUpdateViewModel : BaseModel
|
|||||||
private AvaloniaList<object> _views = new();
|
private AvaloniaList<object> _views = new();
|
||||||
private object _selectedUpdate;
|
private object _selectedUpdate;
|
||||||
|
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
||||||
{
|
{
|
||||||
get => _titleUpdates;
|
get => _titleUpdates;
|
||||||
@@ -81,7 +82,7 @@ public class TitleUpdateViewModel : BaseModel
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -112,7 +113,6 @@ public class TitleUpdateViewModel : BaseModel
|
|||||||
|
|
||||||
// NOTE: Save the list again to remove leftovers.
|
// NOTE: Save the list again to remove leftovers.
|
||||||
Save();
|
Save();
|
||||||
|
|
||||||
SortUpdates();
|
SortUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +246,7 @@ public class TitleUpdateViewModel : BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
||||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
if (!strings.TryGetValue("Language", out string languageName))
|
if (!strings.TryGetValue("Language", out string languageName))
|
||||||
{
|
{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
{
|
{
|
||||||
@@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsScanned { get; set; }
|
public bool IsScanned { get; set; }
|
||||||
public Amiibo.AmiiboApi ScannedAmiibo { get; set; }
|
public AmiiboApi ScannedAmiibo { get; set; }
|
||||||
public AmiiboWindowViewModel ViewModel { get; set; }
|
public AmiiboWindowViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ViewModel.AmiiboSelectedIndex > -1)
|
if (ViewModel.AmiiboSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
||||||
ScannedAmiibo = amiibo;
|
ScannedAmiibo = amiibo;
|
||||||
IsScanned = true;
|
IsScanned = true;
|
||||||
Close();
|
Close();
|
||||||
|
@@ -6,11 +6,8 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Button = Avalonia.Controls.Button;
|
using Button = Avalonia.Controls.Button;
|
||||||
|
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
|
||||||
public enum AntiAliasing
|
public enum AntiAliasing
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
|
||||||
public enum AspectRatio
|
public enum AspectRatio
|
||||||
{
|
{
|
||||||
Fixed4x3,
|
Fixed4x3,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
|
||||||
public enum BackendThreading
|
public enum BackendThreading
|
||||||
{
|
{
|
||||||
Auto,
|
Auto,
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(List<DownloadableContentContainer>))]
|
||||||
|
public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||||
public enum GraphicsBackend
|
public enum GraphicsBackend
|
||||||
{
|
{
|
||||||
Vulkan,
|
Vulkan,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))]
|
||||||
public enum GraphicsDebugLevel
|
public enum GraphicsDebugLevel
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))]
|
||||||
public enum GamepadInputId : byte
|
public enum GamepadInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
{
|
{
|
||||||
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
||||||
{
|
{
|
||||||
|
private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
||||||
{
|
{
|
||||||
// Temporary reader to get the backend type
|
// Temporary reader to get the backend type
|
||||||
@@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
|
|
||||||
return motionBackendType switch
|
return motionBackendType switch
|
||||||
{
|
{
|
||||||
MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options),
|
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController),
|
||||||
MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options),
|
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
switch (value.MotionBackend)
|
switch (value.MotionBackend)
|
||||||
{
|
{
|
||||||
case MotionInputBackendType.GamepadDriver:
|
case MotionInputBackendType.GamepadDriver:
|
||||||
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options);
|
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController);
|
||||||
break;
|
break;
|
||||||
case MotionInputBackendType.CemuHook:
|
case MotionInputBackendType.CemuHook:
|
||||||
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options);
|
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(JsonMotionConfigControllerConverter))]
|
||||||
public class MotionConfigController
|
public class MotionConfigController
|
||||||
{
|
{
|
||||||
public MotionInputBackendType MotionBackend { get; set; }
|
public MotionInputBackendType MotionBackend { get; set; }
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(MotionConfigController))]
|
||||||
|
[JsonSerializable(typeof(CemuHookMotionConfigController))]
|
||||||
|
[JsonSerializable(typeof(StandardMotionConfigController))]
|
||||||
|
public partial class MotionConfigJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))]
|
||||||
public enum MotionInputBackendType : byte
|
public enum MotionInputBackendType : byte
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))]
|
||||||
public enum StickInputId : byte
|
public enum StickInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
[Flags]
|
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
|
[Flags]
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
|
||||||
public enum ControllerType : int
|
public enum ControllerType : int
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
|
||||||
public enum InputBackendType
|
public enum InputBackendType
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(JsonInputConfigConverter))]
|
||||||
public class InputConfig : INotifyPropertyChanged
|
public class InputConfig : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(InputConfig))]
|
||||||
|
[JsonSerializable(typeof(StandardKeyboardInputConfig))]
|
||||||
|
[JsonSerializable(typeof(StandardControllerInputConfig))]
|
||||||
|
public partial class InputConfigJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,16 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
class JsonInputConfigConverter : JsonConverter<InputConfig>
|
public class JsonInputConfigConverter : JsonConverter<InputConfig>
|
||||||
{
|
{
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
||||||
{
|
{
|
||||||
// Temporary reader to get the backend type
|
// Temporary reader to get the backend type
|
||||||
@@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
|
|
||||||
return backendType switch
|
return backendType switch
|
||||||
{
|
{
|
||||||
InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options),
|
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig),
|
||||||
InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options),
|
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
switch (value.Backend)
|
switch (value.Backend)
|
||||||
{
|
{
|
||||||
case InputBackendType.WindowKeyboard:
|
case InputBackendType.WindowKeyboard:
|
||||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options);
|
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig);
|
||||||
break;
|
break;
|
||||||
case InputBackendType.GamepadSDL2:
|
case InputBackendType.GamepadSDL2:
|
||||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options);
|
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<Key>))]
|
||||||
public enum Key
|
public enum Key
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
// NOTE: Please don't change this to struct.
|
||||||
|
// This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys.
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync { get; set; }
|
public Key ToggleVsync { get; set; }
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))]
|
||||||
public enum PlayerIndex : int
|
public enum PlayerIndex : int
|
||||||
{
|
{
|
||||||
Player1 = 0,
|
Player1 = 0,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
|
||||||
public enum MemoryManagerMode : byte
|
public enum MemoryManagerMode : byte
|
||||||
{
|
{
|
||||||
SoftwarePageTable,
|
SoftwarePageTable,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))]
|
||||||
public enum ScalingFilter
|
public enum ScalingFilter
|
||||||
{
|
{
|
||||||
Bilinear,
|
Bilinear,
|
||||||
|
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(TitleUpdateMetadata))]
|
||||||
|
public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,22 +1,20 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
internal class DefaultLogFormatter : ILogFormatter
|
internal class DefaultLogFormatter : ILogFormatter
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
public string Format(LogEventArgs args)
|
public string Format(LogEventArgs args)
|
||||||
{
|
{
|
||||||
StringBuilder sb = _stringBuilderPool.Allocate();
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sb.Clear();
|
sb.Clear();
|
||||||
|
|
||||||
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time);
|
sb.Append($@"{args.Time:hh\:mm\:ss\.fff}");
|
||||||
sb.Append($" |{args.Level.ToString()[0]}| ");
|
sb.Append($" |{args.Level.ToString()[0]}| ");
|
||||||
|
|
||||||
if (args.ThreadName != null)
|
if (args.ThreadName != null)
|
||||||
@@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
sb.Append(args.Message);
|
sb.Append(args.Message);
|
||||||
|
|
||||||
if (args.Data != null)
|
if (args.Data is not null)
|
||||||
{
|
{
|
||||||
PropertyInfo[] props = args.Data.GetType().GetProperties();
|
sb.Append(' ');
|
||||||
|
DynamicObjectFormatter.Format(sb, args.Data);
|
||||||
sb.Append(" {");
|
|
||||||
|
|
||||||
foreach (var prop in props)
|
|
||||||
{
|
|
||||||
sb.Append(prop.Name);
|
|
||||||
sb.Append(": ");
|
|
||||||
|
|
||||||
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
|
||||||
{
|
|
||||||
Array array = (Array)prop.GetValue(args.Data);
|
|
||||||
foreach (var item in array)
|
|
||||||
{
|
|
||||||
sb.Append(item.ToString());
|
|
||||||
sb.Append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 2, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append(prop.GetValue(args.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(" ; ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We remove the final ';' from the string
|
|
||||||
if (props.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 3, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_stringBuilderPool.Release(sb);
|
StringBuilderPool.Release(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
internal class DynamicObjectFormatter
|
||||||
|
{
|
||||||
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
|
public static string? Format(object? dynamicObject)
|
||||||
|
{
|
||||||
|
if (dynamicObject is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Format(sb, dynamicObject);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
StringBuilderPool.Release(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Format(StringBuilder sb, object? dynamicObject)
|
||||||
|
{
|
||||||
|
if (dynamicObject is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyInfo[] props = dynamicObject.GetType().GetProperties();
|
||||||
|
|
||||||
|
sb.Append('{');
|
||||||
|
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
sb.Append(prop.Name);
|
||||||
|
sb.Append(": ");
|
||||||
|
|
||||||
|
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
||||||
|
{
|
||||||
|
Array? array = (Array?) prop.GetValue(dynamicObject);
|
||||||
|
|
||||||
|
if (array is not null)
|
||||||
|
{
|
||||||
|
foreach (var item in array)
|
||||||
|
{
|
||||||
|
sb.Append(item);
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Remove(sb.Length - 2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(prop.GetValue(dynamicObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(" ; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We remove the final ';' from the string
|
||||||
|
if (props.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Remove(sb.Length - 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<LogClass>))]
|
||||||
public enum LogClass
|
public enum LogClass
|
||||||
{
|
{
|
||||||
Application,
|
Application,
|
||||||
|
@@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
public readonly string Message;
|
public readonly string Message;
|
||||||
public readonly object Data;
|
public readonly object Data;
|
||||||
|
|
||||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message)
|
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null)
|
||||||
{
|
|
||||||
Level = level;
|
|
||||||
Time = time;
|
|
||||||
ThreadName = threadName;
|
|
||||||
Message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data)
|
|
||||||
{
|
{
|
||||||
Level = level;
|
Level = level;
|
||||||
Time = time;
|
Time = time;
|
||||||
|
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
internal class LogEventArgsJson
|
||||||
|
{
|
||||||
|
public LogLevel Level { get; }
|
||||||
|
public TimeSpan Time { get; }
|
||||||
|
public string ThreadName { get; }
|
||||||
|
|
||||||
|
public string Message { get; }
|
||||||
|
public string Data { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null)
|
||||||
|
{
|
||||||
|
Level = level;
|
||||||
|
Time = time;
|
||||||
|
ThreadName = threadName;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogEventArgsJson FromLogEventArgs(LogEventArgs args)
|
||||||
|
{
|
||||||
|
return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(LogEventArgsJson))]
|
||||||
|
internal partial class LogEventJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))]
|
||||||
public enum LogLevel
|
public enum LogLevel
|
||||||
{
|
{
|
||||||
Debug,
|
Debug,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
using System.IO;
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
@@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
public void Log(object sender, LogEventArgs e)
|
public void Log(object sender, LogEventArgs e)
|
||||||
{
|
{
|
||||||
string text = JsonSerializer.Serialize(e);
|
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
||||||
|
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
||||||
using (BinaryWriter writer = new BinaryWriter(_stream))
|
|
||||||
{
|
|
||||||
writer.Write(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")]
|
||||||
|
[JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")]
|
||||||
|
public partial class CommonJsonContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,62 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using System.IO;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
public class JsonHelper
|
public class JsonHelper
|
||||||
{
|
{
|
||||||
public static JsonNamingPolicy SnakeCase { get; }
|
private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy();
|
||||||
|
private const int DefaultFileWriteBufferSize = 4096;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new serializer options with default settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
|
||||||
|
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
|
||||||
|
/// </remarks>
|
||||||
|
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
|
||||||
|
{
|
||||||
|
JsonSerializerOptions options = new()
|
||||||
|
{
|
||||||
|
DictionaryKeyPolicy = SnakeCasePolicy,
|
||||||
|
PropertyNamingPolicy = SnakeCasePolicy,
|
||||||
|
WriteIndented = indented,
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize(value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough);
|
||||||
|
JsonSerializer.Serialize(file, value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
using FileStream file = File.OpenRead(filePath);
|
||||||
|
return JsonSerializer.Deserialize(file, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
JsonSerializer.Serialize(stream, value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
||||||
{
|
{
|
||||||
@@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
for (int i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.Append("_");
|
builder.Append('_');
|
||||||
builder.Append(char.ToLowerInvariant(c));
|
builder.Append(char.ToLowerInvariant(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonHelper()
|
|
||||||
{
|
|
||||||
SnakeCase = new SnakeCaseNamingPolicy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
JsonSerializerOptions options = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
DictionaryKeyPolicy = SnakeCase,
|
|
||||||
PropertyNamingPolicy = SnakeCase,
|
|
||||||
WriteIndented = prettyPrint,
|
|
||||||
AllowTrailingCommas = true,
|
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip
|
|
||||||
};
|
|
||||||
|
|
||||||
options.Converters.Add(new JsonStringEnumConverter());
|
|
||||||
options.Converters.Add(new JsonInputConfigConverter());
|
|
||||||
options.Converters.Add(new JsonMotionConfigControllerConverter());
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(Stream stream)
|
|
||||||
{
|
|
||||||
using (BinaryReader reader = new BinaryReader(stream))
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T DeserializeFromFile<T>(string path)
|
|
||||||
{
|
|
||||||
return Deserialize<T>(File.ReadAllText(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(string json)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
|
||||||
{
|
|
||||||
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
34
Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
34
Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>.
|
||||||
|
/// Get rid of this converter if dotnet supports similar functionality out of the box.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="TEnum">Type of enum to serialize</typeparam>
|
||||||
|
public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
|
||||||
|
{
|
||||||
|
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var enumValue = reader.GetString();
|
||||||
|
if (string.IsNullOrEmpty(enumValue))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enum.Parse<TEnum>(enumValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
Src1AlphaGl = 0xc902,
|
Src1AlphaGl = 0xc902,
|
||||||
OneMinusSrc1AlphaGl = 0xc903
|
OneMinusSrc1AlphaGl = 0xc903
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BlendFactorExtensions
|
||||||
|
{
|
||||||
|
public static bool IsDualSource(this BlendFactor factor)
|
||||||
|
{
|
||||||
|
switch (factor)
|
||||||
|
{
|
||||||
|
case BlendFactor.Src1Color:
|
||||||
|
case BlendFactor.Src1ColorGl:
|
||||||
|
case BlendFactor.Src1Alpha:
|
||||||
|
case BlendFactor.Src1AlphaGl:
|
||||||
|
case BlendFactor.OneMinusSrc1Color:
|
||||||
|
case BlendFactor.OneMinusSrc1ColorGl:
|
||||||
|
case BlendFactor.OneMinusSrc1Alpha:
|
||||||
|
case BlendFactor.OneMinusSrc1AlphaGl:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
Signal();
|
Signal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the dual-source blend enabled state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enabled">True if blending is enabled and using dual-source blend</param>
|
||||||
|
public void SetDualSourceBlendEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled != _graphics.DualSourceBlendEnable)
|
||||||
|
{
|
||||||
|
_graphics.DualSourceBlendEnable = enabled;
|
||||||
|
|
||||||
|
Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
bool blendIndependent = _state.State.BlendIndependent;
|
bool blendIndependent = _state.State.BlendIndependent;
|
||||||
ColorF blendConstant = _state.State.BlendConstant;
|
ColorF blendConstant = _state.State.BlendConstant;
|
||||||
|
|
||||||
|
bool dualSourceBlendEnabled = false;
|
||||||
|
|
||||||
if (blendIndependent)
|
if (blendIndependent)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
@@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
FilterBlendFactor(blend.AlphaSrcFactor, index),
|
FilterBlendFactor(blend.AlphaSrcFactor, index),
|
||||||
FilterBlendFactor(blend.AlphaDstFactor, index));
|
FilterBlendFactor(blend.AlphaDstFactor, index));
|
||||||
|
|
||||||
|
if (enable &&
|
||||||
|
(blend.ColorSrcFactor.IsDualSource() ||
|
||||||
|
blend.ColorDstFactor.IsDualSource() ||
|
||||||
|
blend.AlphaSrcFactor.IsDualSource() ||
|
||||||
|
blend.AlphaDstFactor.IsDualSource()))
|
||||||
|
{
|
||||||
|
dualSourceBlendEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
_pipeline.BlendDescriptors[index] = descriptor;
|
_pipeline.BlendDescriptors[index] = descriptor;
|
||||||
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
||||||
}
|
}
|
||||||
@@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
FilterBlendFactor(blend.AlphaSrcFactor, 0),
|
FilterBlendFactor(blend.AlphaSrcFactor, 0),
|
||||||
FilterBlendFactor(blend.AlphaDstFactor, 0));
|
FilterBlendFactor(blend.AlphaDstFactor, 0));
|
||||||
|
|
||||||
|
if (enable &&
|
||||||
|
(blend.ColorSrcFactor.IsDualSource() ||
|
||||||
|
blend.ColorDstFactor.IsDualSource() ||
|
||||||
|
blend.AlphaSrcFactor.IsDualSource() ||
|
||||||
|
blend.AlphaDstFactor.IsDualSource()))
|
||||||
|
{
|
||||||
|
dualSourceBlendEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
{
|
{
|
||||||
_pipeline.BlendDescriptors[index] = descriptor;
|
_pipeline.BlendDescriptors[index] = descriptor;
|
||||||
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
|
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryDualSourceBlendEnable()
|
||||||
|
{
|
||||||
|
return _oldSpecState.GraphicsState.DualSourceBlendEnable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public InputTopology QueryPrimitiveTopology()
|
public InputTopology QueryPrimitiveTopology()
|
||||||
{
|
{
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 4368;
|
private const uint CodeGenVersion = 4404;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
|
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryDualSourceBlendEnable()
|
||||||
|
{
|
||||||
|
return _state.GraphicsState.DualSourceBlendEnable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public InputTopology QueryPrimitiveTopology()
|
public InputTopology QueryPrimitiveTopology()
|
||||||
{
|
{
|
||||||
|
@@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Array8<AttributeType> FragmentOutputTypes;
|
public Array8<AttributeType> FragmentOutputTypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether dual source blend is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool DualSourceBlendEnable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU graphics state.
|
/// Creates a new GPU graphics state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
||||||
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
|
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
|
||||||
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
|
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
|
||||||
|
/// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param>
|
||||||
public GpuChannelGraphicsState(
|
public GpuChannelGraphicsState(
|
||||||
bool earlyZForce,
|
bool earlyZForce,
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
@@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
ref Array32<AttributeType> attributeTypes,
|
ref Array32<AttributeType> attributeTypes,
|
||||||
bool hasConstantBufferDrawParameters,
|
bool hasConstantBufferDrawParameters,
|
||||||
bool hasUnalignedStorageBuffer,
|
bool hasUnalignedStorageBuffer,
|
||||||
ref Array8<AttributeType> fragmentOutputTypes)
|
ref Array8<AttributeType> fragmentOutputTypes,
|
||||||
|
bool dualSourceBlendEnable)
|
||||||
{
|
{
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
Topology = topology;
|
Topology = topology;
|
||||||
@@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
||||||
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
||||||
FragmentOutputTypes = fragmentOutputTypes;
|
FragmentOutputTypes = fragmentOutputTypes;
|
||||||
|
DualSourceBlendEnable = dualSourceBlendEnable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Matches(channel, ref poolState, checkTextures, isCompute: false);
|
return Matches(channel, ref poolState, checkTextures, isCompute: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
|
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
|
||||||
(BlendingFactorDest)blend.AlphaDstFactor.Convert());
|
(BlendingFactorDest)blend.AlphaDstFactor.Convert());
|
||||||
|
|
||||||
static bool IsDualSource(BlendFactor factor)
|
|
||||||
{
|
|
||||||
switch (factor)
|
|
||||||
{
|
|
||||||
case BlendFactor.Src1Color:
|
|
||||||
case BlendFactor.Src1ColorGl:
|
|
||||||
case BlendFactor.Src1Alpha:
|
|
||||||
case BlendFactor.Src1AlphaGl:
|
|
||||||
case BlendFactor.OneMinusSrc1Color:
|
|
||||||
case BlendFactor.OneMinusSrc1ColorGl:
|
|
||||||
case BlendFactor.OneMinusSrc1Alpha:
|
|
||||||
case BlendFactor.OneMinusSrc1AlphaGl:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureFramebuffer();
|
EnsureFramebuffer();
|
||||||
|
|
||||||
_framebuffer.SetDualSourceBlend(
|
_framebuffer.SetDualSourceBlend(
|
||||||
IsDualSource(blend.ColorSrcFactor) ||
|
blend.ColorSrcFactor.IsDualSource() ||
|
||||||
IsDualSource(blend.ColorDstFactor) ||
|
blend.ColorDstFactor.IsDualSource() ||
|
||||||
IsDualSource(blend.AlphaSrcFactor) ||
|
blend.AlphaSrcFactor.IsDualSource() ||
|
||||||
IsDualSource(blend.AlphaDstFactor));
|
blend.AlphaDstFactor.IsDualSource());
|
||||||
|
|
||||||
if (_blendConstant != blend.BlendConstant)
|
if (_blendConstant != blend.BlendConstant)
|
||||||
{
|
{
|
||||||
|
@@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int usedAttributes = context.Config.UsedOutputAttributes;
|
int usedAttributes = context.Config.UsedOutputAttributes;
|
||||||
|
|
||||||
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
||||||
|
{
|
||||||
|
int firstOutput = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
int mask = 3 << firstOutput;
|
||||||
|
|
||||||
|
if ((usedAttributes & mask) == mask)
|
||||||
|
{
|
||||||
|
usedAttributes &= ~mask;
|
||||||
|
DeclareOutputDualSourceBlendAttribute(context, firstOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (usedAttributes != 0)
|
while (usedAttributes != 0)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
@@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr)
|
||||||
|
{
|
||||||
|
string name = $"{DefaultNames.OAttributePrefix}{attr}";
|
||||||
|
string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}";
|
||||||
|
|
||||||
|
context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};");
|
||||||
|
context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
|
||||||
|
}
|
||||||
|
|
||||||
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
||||||
{
|
{
|
||||||
foreach (int attr in attrs.Order())
|
foreach (int attr in attrs.Order())
|
||||||
|
@@ -6,6 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||||
@@ -622,8 +623,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
||||||
{
|
{
|
||||||
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
||||||
|
|
||||||
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
|
||||||
|
{
|
||||||
|
int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
|
||||||
|
int index = location - firstLocation;
|
||||||
|
int mask = 3 << firstLocation;
|
||||||
|
|
||||||
|
if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
|
||||||
|
{
|
||||||
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
|
||||||
|
context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isOutAttr)
|
if (!isOutAttr)
|
||||||
{
|
{
|
||||||
|
@@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries dual source blend state.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if blending is enabled with a dual source blend equation, false otherwise</returns>
|
||||||
|
bool QueryDualSourceBlendEnable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
153
Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs
Normal file
153
Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
class VulkanDebugMessenger : IDisposable
|
||||||
|
{
|
||||||
|
private static string[] _excludedMessages = new string[]
|
||||||
|
{
|
||||||
|
// NOTE: Done on purpose right now.
|
||||||
|
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
||||||
|
// TODO: Figure out if fixable
|
||||||
|
"VUID-vkCmdDrawIndexed-None-04584",
|
||||||
|
// TODO: Might be worth looking into making this happy to possibly optimize copies.
|
||||||
|
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
|
||||||
|
// TODO: Fix this, it's causing too much noise right now.
|
||||||
|
"VUID-VkSubpassDependency-srcSubpass-00867"
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly Vk _api;
|
||||||
|
private readonly Instance _instance;
|
||||||
|
private readonly GraphicsDebugLevel _logLevel;
|
||||||
|
private readonly ExtDebugUtils _debugUtils;
|
||||||
|
private readonly DebugUtilsMessengerEXT? _debugUtilsMessenger;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public VulkanDebugMessenger(Vk api, Instance instance, GraphicsDebugLevel logLevel)
|
||||||
|
{
|
||||||
|
_api = api;
|
||||||
|
_instance = instance;
|
||||||
|
_logLevel = logLevel;
|
||||||
|
|
||||||
|
_api.TryGetInstanceExtension(instance, out _debugUtils);
|
||||||
|
|
||||||
|
Result result = TryInitialize(out _debugUtilsMessenger);
|
||||||
|
|
||||||
|
if (result != Result.Success)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, $"Vulkan debug messenger initialization failed with error {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle)
|
||||||
|
{
|
||||||
|
debugUtilsMessengerHandle = null;
|
||||||
|
|
||||||
|
if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None)
|
||||||
|
{
|
||||||
|
var messageType = _logLevel switch
|
||||||
|
{
|
||||||
|
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
||||||
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
||||||
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
||||||
|
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
||||||
|
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
||||||
|
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
||||||
|
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageSeverity = _logLevel switch
|
||||||
|
{
|
||||||
|
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
||||||
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
||||||
|
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
||||||
|
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
|
||||||
|
};
|
||||||
|
|
||||||
|
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
||||||
|
MessageType = messageType,
|
||||||
|
MessageSeverity = messageSeverity
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
debugUtilsMessengerCreateInfo.PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(UserCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugUtilsMessengerEXT messengerHandle = default;
|
||||||
|
|
||||||
|
Result result = _debugUtils.CreateDebugUtilsMessenger(_instance, SpanHelpers.AsReadOnlySpan(ref debugUtilsMessengerCreateInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref messengerHandle));
|
||||||
|
|
||||||
|
if (result == Result.Success)
|
||||||
|
{
|
||||||
|
debugUtilsMessengerHandle = messengerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static uint UserCallback(
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
||||||
|
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
||||||
|
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData)
|
||||||
|
{
|
||||||
|
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
||||||
|
|
||||||
|
foreach (string excludedMessagePart in _excludedMessages)
|
||||||
|
{
|
||||||
|
if (msg.Contains(excludedMessagePart))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
||||||
|
{
|
||||||
|
Logger.Debug?.Print(LogClass.Gpu, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (_debugUtilsMessenger.HasValue)
|
||||||
|
{
|
||||||
|
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger.Value, Span<AllocationCallbacks>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -47,19 +47,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
KhrSwapchain.ExtensionName
|
KhrSwapchain.ExtensionName
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string[] _excludedMessages = new string[]
|
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions)
|
||||||
{
|
|
||||||
// NOTE: Done on purpose right now.
|
|
||||||
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
|
||||||
// TODO: Figure out if fixable
|
|
||||||
"VUID-vkCmdDrawIndexed-None-04584",
|
|
||||||
// TODO: Might be worth looking into making this happy to possibly optimize copies.
|
|
||||||
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
|
|
||||||
// TODO: Fix this, it's causing too much noise right now.
|
|
||||||
"VUID-VkSubpassDependency-srcSubpass-00867"
|
|
||||||
};
|
|
||||||
|
|
||||||
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger)
|
|
||||||
{
|
{
|
||||||
var enabledLayers = new List<string>();
|
var enabledLayers = new List<string>();
|
||||||
|
|
||||||
@@ -95,7 +83,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
var enabledExtensions = requiredExtensions;
|
||||||
|
|
||||||
|
if (api.IsInstanceExtensionPresent("VK_EXT_debug_utils"))
|
||||||
|
{
|
||||||
|
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||||
|
|
||||||
@@ -145,47 +138,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger);
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe static uint DebugMessenger(
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
|
||||||
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
||||||
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
||||||
void* pUserData)
|
|
||||||
{
|
|
||||||
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
|
||||||
|
|
||||||
foreach (string excludedMessagePart in _excludedMessages)
|
|
||||||
{
|
|
||||||
if (msg.Contains(excludedMessagePart))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
|
||||||
{
|
|
||||||
Logger.Info?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
|
||||||
{
|
|
||||||
Logger.Debug?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId)
|
internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId)
|
||||||
{
|
{
|
||||||
uint physicalDeviceCount;
|
uint physicalDeviceCount;
|
||||||
@@ -671,61 +626,5 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
|
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe static void CreateDebugMessenger(
|
|
||||||
Vk api,
|
|
||||||
GraphicsDebugLevel logLevel,
|
|
||||||
Instance instance,
|
|
||||||
out ExtDebugUtils debugUtils,
|
|
||||||
out DebugUtilsMessengerEXT debugUtilsMessenger)
|
|
||||||
{
|
|
||||||
debugUtils = default;
|
|
||||||
|
|
||||||
if (logLevel != GraphicsDebugLevel.None)
|
|
||||||
{
|
|
||||||
if (!api.TryGetInstanceExtension(instance, out debugUtils))
|
|
||||||
{
|
|
||||||
debugUtilsMessenger = default;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var filterLogType = logLevel switch
|
|
||||||
{
|
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var filterLogSeverity = logLevel switch
|
|
||||||
{
|
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
|
||||||
MessageType = filterLogType,
|
|
||||||
MessageSeverity = filterLogSeverity,
|
|
||||||
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger)
|
|
||||||
};
|
|
||||||
|
|
||||||
debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
debugUtilsMessenger = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
||||||
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
||||||
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
||||||
internal ExtDebugUtils DebugUtilsApi { get; private set; }
|
|
||||||
|
|
||||||
internal uint QueueFamilyIndex { get; private set; }
|
internal uint QueueFamilyIndex { get; private set; }
|
||||||
internal Queue Queue { get; private set; }
|
internal Queue Queue { get; private set; }
|
||||||
@@ -57,11 +56,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal HashSet<ITexture> Textures { get; }
|
internal HashSet<ITexture> Textures { get; }
|
||||||
internal HashSet<SamplerHolder> Samplers { get; }
|
internal HashSet<SamplerHolder> Samplers { get; }
|
||||||
|
|
||||||
|
private VulkanDebugMessenger _debugMessenger;
|
||||||
private Counters _counters;
|
private Counters _counters;
|
||||||
private SyncManager _syncManager;
|
private SyncManager _syncManager;
|
||||||
|
|
||||||
private PipelineFull _pipeline;
|
private PipelineFull _pipeline;
|
||||||
private DebugUtilsMessengerEXT _debugUtilsMessenger;
|
|
||||||
|
|
||||||
internal HelperShader HelperShader { get; private set; }
|
internal HelperShader HelperShader { get; private set; }
|
||||||
internal PipelineFull PipelineInternal => _pipeline;
|
internal PipelineFull PipelineInternal => _pipeline;
|
||||||
@@ -345,9 +344,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Api = api;
|
Api = api;
|
||||||
|
|
||||||
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger);
|
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions());
|
||||||
|
_debugMessenger = new VulkanDebugMessenger(api, _instance, logLevel);
|
||||||
DebugUtilsApi = debugUtils;
|
|
||||||
|
|
||||||
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
|
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
|
||||||
{
|
{
|
||||||
@@ -794,11 +792,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
MemoryAllocator.Dispose();
|
MemoryAllocator.Dispose();
|
||||||
|
|
||||||
if (_debugUtilsMessenger.Handle != 0)
|
|
||||||
{
|
|
||||||
DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var shader in Shaders)
|
foreach (var shader in Shaders)
|
||||||
{
|
{
|
||||||
shader.Dispose();
|
shader.Dispose();
|
||||||
@@ -818,6 +811,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Api.DestroyDevice(_device, null);
|
Api.DestroyDevice(_device, null);
|
||||||
|
|
||||||
|
_debugMessenger.Dispose();
|
||||||
|
|
||||||
// Last step destroy the instance
|
// Last step destroy the instance
|
||||||
Api.DestroyInstance(_instance, null);
|
Api.DestroyInstance(_instance, null);
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
{
|
{
|
||||||
@@ -13,29 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
{
|
{
|
||||||
private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
|
private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
|
||||||
|
|
||||||
private struct ProfilesJson
|
private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
{
|
|
||||||
[JsonPropertyName("profiles")]
|
|
||||||
public List<UserProfileJson> Profiles { get; set; }
|
|
||||||
[JsonPropertyName("last_opened")]
|
|
||||||
public string LastOpened { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct UserProfileJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("user_id")]
|
|
||||||
public string UserId { get; set; }
|
|
||||||
[JsonPropertyName("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonPropertyName("account_state")]
|
|
||||||
public AccountState AccountState { get; set; }
|
|
||||||
[JsonPropertyName("online_play_state")]
|
|
||||||
public AccountState OnlinePlayState { get; set; }
|
|
||||||
[JsonPropertyName("last_modified_timestamp")]
|
|
||||||
public long LastModifiedTimestamp { get; set; }
|
|
||||||
[JsonPropertyName("image")]
|
|
||||||
public byte[] Image { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserId LastOpened { get; set; }
|
public UserId LastOpened { get; set; }
|
||||||
|
|
||||||
@@ -47,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
|
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson);
|
||||||
|
|
||||||
foreach (var profile in profilesJson.Profiles)
|
foreach (var profile in profilesJson.Profiles)
|
||||||
{
|
{
|
||||||
@@ -92,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true));
|
JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(ProfilesJson))]
|
||||||
|
internal partial class ProfilesJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AccountState>))]
|
||||||
public enum AccountState
|
public enum AccountState
|
||||||
{
|
{
|
||||||
Closed,
|
Closed,
|
||||||
|
10
Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs
Normal file
10
Ryujinx.HLE/HOS/Services/Account/Acc/Types/ProfilesJson.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types
|
||||||
|
{
|
||||||
|
internal struct ProfilesJson
|
||||||
|
{
|
||||||
|
public List<UserProfileJson> Profiles { get; set; }
|
||||||
|
public string LastOpened { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types
|
||||||
|
{
|
||||||
|
internal struct UserProfileJson
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public AccountState AccountState { get; set; }
|
||||||
|
public AccountState OnlinePlayState { get; set; }
|
||||||
|
public long LastModifiedTimestamp { get; set; }
|
||||||
|
public byte[] Image { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(VirtualAmiiboFile))]
|
||||||
|
internal partial class AmiiboJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Mii;
|
using Ryujinx.HLE.HOS.Services.Mii;
|
||||||
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
@@ -8,8 +9,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
{
|
{
|
||||||
@@ -17,6 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||||||
{
|
{
|
||||||
private static uint _openedApplicationAreaId;
|
private static uint _openedApplicationAreaId;
|
||||||
|
|
||||||
|
private static readonly AmiiboJsonSerializerContext SerializerContext = AmiiboJsonSerializerContext.Default;
|
||||||
|
|
||||||
public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid)
|
public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid)
|
||||||
{
|
{
|
||||||
if (useRandomUuid)
|
if (useRandomUuid)
|
||||||
@@ -173,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||||||
|
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
{
|
{
|
||||||
virtualAmiiboFile = JsonSerializer.Deserialize<VirtualAmiiboFile>(File.ReadAllText(filePath), new JsonSerializerOptions(JsonSerializerDefaults.General));
|
virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, SerializerContext.VirtualAmiiboFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -197,8 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||||||
private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile)
|
private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile)
|
||||||
{
|
{
|
||||||
string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json");
|
string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json");
|
||||||
|
JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, SerializerContext.VirtualAmiiboFile);
|
||||||
File.WriteAllText(filePath, JsonSerializer.Serialize(virtualAmiiboFile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,9 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
{
|
{
|
||||||
public static class PartitionFileSystemExtensions
|
public static class PartitionFileSystemExtensions
|
||||||
{
|
{
|
||||||
|
private static readonly DownloadableContentJsonSerializerContext ContentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
internal static (bool, ProcessResult) TryLoad(this PartitionFileSystem partitionFileSystem, Switch device, string path, out string errorMessage)
|
internal static (bool, ProcessResult) TryLoad(this PartitionFileSystem partitionFileSystem, Switch device, string path, out string errorMessage)
|
||||||
{
|
{
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
@@ -85,7 +88,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
if (File.Exists(titleUpdateMetadataPath))
|
||||||
{
|
{
|
||||||
string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
|
string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected;
|
||||||
if (File.Exists(updatePath))
|
if (File.Exists(updatePath))
|
||||||
{
|
{
|
||||||
PartitionFileSystem updatePartitionFileSystem = new(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage());
|
PartitionFileSystem updatePartitionFileSystem = new(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage());
|
||||||
@@ -139,7 +142,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
||||||
if (File.Exists(addOnContentMetadataPath))
|
if (File.Exists(addOnContentMetadataPath))
|
||||||
{
|
{
|
||||||
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(addOnContentMetadataPath);
|
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, ContentSerializerContext.ListDownloadableContentContainer);
|
||||||
|
|
||||||
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
||||||
{
|
{
|
||||||
|
@@ -405,7 +405,16 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
// Once everything is loaded, we can load cheats.
|
// Once everything is loaded, we can load cheats.
|
||||||
device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine);
|
device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine);
|
||||||
|
|
||||||
return new ProcessResult(metaLoader, applicationControlProperties, diskCacheEnabled, allowCodeMemoryForJit, processContextFactory.DiskCacheLoadState, process.Pid, meta.MainThreadPriority, meta.MainThreadStackSize);
|
return new ProcessResult(
|
||||||
|
metaLoader,
|
||||||
|
applicationControlProperties,
|
||||||
|
diskCacheEnabled,
|
||||||
|
allowCodeMemoryForJit,
|
||||||
|
processContextFactory.DiskCacheLoadState,
|
||||||
|
process.Pid,
|
||||||
|
meta.MainThreadPriority,
|
||||||
|
meta.MainThreadStackSize,
|
||||||
|
device.System.State.DesiredTitleLanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)
|
public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
{
|
{
|
||||||
public struct ProcessResult
|
public struct ProcessResult
|
||||||
{
|
{
|
||||||
public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0);
|
public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish);
|
||||||
|
|
||||||
private readonly byte _mainThreadPriority;
|
private readonly byte _mainThreadPriority;
|
||||||
private readonly uint _mainThreadStackSize;
|
private readonly uint _mainThreadStackSize;
|
||||||
@@ -35,7 +36,8 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
IDiskCacheLoadState diskCacheLoadState,
|
IDiskCacheLoadState diskCacheLoadState,
|
||||||
ulong pid,
|
ulong pid,
|
||||||
byte mainThreadPriority,
|
byte mainThreadPriority,
|
||||||
uint mainThreadStackSize)
|
uint mainThreadStackSize,
|
||||||
|
TitleLanguage titleLanguage)
|
||||||
{
|
{
|
||||||
_mainThreadPriority = mainThreadPriority;
|
_mainThreadPriority = mainThreadPriority;
|
||||||
_mainThreadStackSize = mainThreadStackSize;
|
_mainThreadStackSize = mainThreadStackSize;
|
||||||
@@ -50,7 +52,17 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
{
|
{
|
||||||
ulong programId = metaLoader.GetProgramId();
|
ulong programId = metaLoader.GetProgramId();
|
||||||
|
|
||||||
|
if (ApplicationControlProperties.Title.ItemsRo.Length > 0)
|
||||||
|
{
|
||||||
|
var langIndex = ApplicationControlProperties.Title.ItemsRo.Length > (int)titleLanguage ? (int)titleLanguage : 0;
|
||||||
|
|
||||||
|
Name = ApplicationControlProperties.Title[langIndex].NameString.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Name = metaLoader.GetProgramName();
|
Name = metaLoader.GetProgramName();
|
||||||
|
}
|
||||||
|
|
||||||
ProgramId = programId;
|
ProgramId = programId;
|
||||||
ProgramIdText = $"{programId:x16}";
|
ProgramIdText = $"{programId:x16}";
|
||||||
Is64Bit = metaLoader.IsProgram64Bit();
|
Is64Bit = metaLoader.IsProgram64Bit();
|
||||||
|
@@ -56,6 +56,8 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
private static bool _enableKeyboard;
|
private static bool _enableKeyboard;
|
||||||
private static bool _enableMouse;
|
private static bool _enableMouse;
|
||||||
|
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Version = ReleaseInformation.GetVersion();
|
Version = ReleaseInformation.GetVersion();
|
||||||
@@ -285,10 +287,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream stream = File.OpenRead(path))
|
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
||||||
{
|
|
||||||
config = JsonHelper.Deserialize<InputConfig>(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
|
10
Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs
Normal file
10
Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.App.Common
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(ApplicationMetadata))]
|
||||||
|
internal partial class ApplicationJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem;
|
|||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
@@ -22,7 +23,6 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.App.Common
|
namespace Ryujinx.Ui.App.Common
|
||||||
@@ -42,6 +42,9 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
private Language _desiredTitleLanguage;
|
private Language _desiredTitleLanguage;
|
||||||
private CancellationTokenSource _cancellationToken;
|
private CancellationTokenSource _cancellationToken;
|
||||||
|
|
||||||
|
private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
|
public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
@@ -489,14 +492,12 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
|
|
||||||
appMetadata = new ApplicationMetadata();
|
appMetadata = new ApplicationMetadata();
|
||||||
|
|
||||||
using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough);
|
JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata);
|
||||||
|
|
||||||
JsonHelper.Serialize(stream, appMetadata, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
appMetadata = JsonHelper.DeserializeFromFile<ApplicationMetadata>(metadataFile);
|
appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata);
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
@@ -509,9 +510,7 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
{
|
{
|
||||||
modifyFunction(appMetadata);
|
modifyFunction(appMetadata);
|
||||||
|
|
||||||
using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough);
|
JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata);
|
||||||
|
|
||||||
JsonHelper.Serialize(stream, appMetadata, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return appMetadata;
|
return appMetadata;
|
||||||
@@ -890,7 +889,7 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
|
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
if (File.Exists(titleUpdateMetadataPath))
|
||||||
{
|
{
|
||||||
updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
|
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected;
|
||||||
|
|
||||||
if (File.Exists(updatePath))
|
if (File.Exists(updatePath))
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Ui.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))]
|
||||||
public enum AudioBackend
|
public enum AudioBackend
|
||||||
{
|
{
|
||||||
Dummy,
|
Dummy,
|
||||||
|
@@ -5,7 +5,7 @@ using Ryujinx.Common.Utilities;
|
|||||||
using Ryujinx.Ui.Common.Configuration.System;
|
using Ryujinx.Ui.Common.Configuration.System;
|
||||||
using Ryujinx.Ui.Common.Configuration.Ui;
|
using Ryujinx.Ui.Common.Configuration.Ui;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
{
|
{
|
||||||
@@ -321,14 +321,14 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
|
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
|
||||||
/// TODO: Remove this when those older versions aren't in use anymore.
|
/// TODO: Remove this when those older versions aren't in use anymore.
|
||||||
public List<object> KeyboardConfig { get; set; }
|
public List<JsonObject> KeyboardConfig { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Legacy controller control bindings
|
/// Legacy controller control bindings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
|
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
|
||||||
/// TODO: Remove this when those older versions aren't in use anymore.
|
/// TODO: Remove this when those older versions aren't in use anymore.
|
||||||
public List<object> ControllerConfig { get; set; }
|
public List<JsonObject> ControllerConfig { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input configurations
|
/// Input configurations
|
||||||
@@ -354,11 +354,12 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
/// Loads a configuration file from disk
|
/// Loads a configuration file from disk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to the JSON configuration file</param>
|
/// <param name="path">The path to the JSON configuration file</param>
|
||||||
|
/// <param name="configurationFileFormat">Parsed configuration file</param>
|
||||||
public static bool TryLoad(string path, out ConfigurationFileFormat configurationFileFormat)
|
public static bool TryLoad(string path, out ConfigurationFileFormat configurationFileFormat)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path);
|
configurationFileFormat = JsonHelper.DeserializeFromFile(path, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat);
|
||||||
|
|
||||||
return configurationFileFormat.Version != 0;
|
return configurationFileFormat.Version != 0;
|
||||||
}
|
}
|
||||||
@@ -376,8 +377,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
/// <param name="path">The path to the JSON configuration file</param>
|
/// <param name="path">The path to the JSON configuration file</param>
|
||||||
public void SaveConfig(string path)
|
public void SaveConfig(string path)
|
||||||
{
|
{
|
||||||
using FileStream fileStream = File.Create(path, 4096, FileOptions.WriteThrough);
|
JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat);
|
||||||
JsonHelper.Serialize(fileStream, this, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
|
{
|
||||||
|
internal static class ConfigurationFileFormatSettings
|
||||||
|
{
|
||||||
|
public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(ConfigurationFileFormat))]
|
||||||
|
internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ using Ryujinx.Ui.Common.Configuration.Ui;
|
|||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
{
|
{
|
||||||
@@ -631,8 +632,8 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
EnableKeyboard = Hid.EnableKeyboard,
|
EnableKeyboard = Hid.EnableKeyboard,
|
||||||
EnableMouse = Hid.EnableMouse,
|
EnableMouse = Hid.EnableMouse,
|
||||||
Hotkeys = Hid.Hotkeys,
|
Hotkeys = Hid.Hotkeys,
|
||||||
KeyboardConfig = new List<object>(),
|
KeyboardConfig = new List<JsonObject>(),
|
||||||
ControllerConfig = new List<object>(),
|
ControllerConfig = new List<JsonObject>(),
|
||||||
InputConfig = Hid.InputConfig,
|
InputConfig = Hid.InputConfig,
|
||||||
GraphicsBackend = Graphics.GraphicsBackend,
|
GraphicsBackend = Graphics.GraphicsBackend,
|
||||||
PreferredGpu = Graphics.PreferredGpu
|
PreferredGpu = Graphics.PreferredGpu
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Ui.Common.Configuration.System
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Configuration.System
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<Language>))]
|
||||||
public enum Language
|
public enum Language
|
||||||
{
|
{
|
||||||
Japanese,
|
Japanese,
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Ui.Common.Configuration.System
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Configuration.System
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<Region>))]
|
||||||
public enum Region
|
public enum Region
|
||||||
{
|
{
|
||||||
Japan,
|
Japan,
|
||||||
|
57
Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs
Normal file
57
Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Amiibo
|
||||||
|
{
|
||||||
|
public struct AmiiboApi : IEquatable<AmiiboApi>
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("head")]
|
||||||
|
public string Head { get; set; }
|
||||||
|
[JsonPropertyName("tail")]
|
||||||
|
public string Tail { get; set; }
|
||||||
|
[JsonPropertyName("image")]
|
||||||
|
public string Image { get; set; }
|
||||||
|
[JsonPropertyName("amiiboSeries")]
|
||||||
|
public string AmiiboSeries { get; set; }
|
||||||
|
[JsonPropertyName("character")]
|
||||||
|
public string Character { get; set; }
|
||||||
|
[JsonPropertyName("gameSeries")]
|
||||||
|
public string GameSeries { get; set; }
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("release")]
|
||||||
|
public Dictionary<string, string> Release { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("gamesSwitch")]
|
||||||
|
public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetId()
|
||||||
|
{
|
||||||
|
return Head + Tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(AmiiboApi other)
|
||||||
|
{
|
||||||
|
return Head + Tail == other.Head + other.Tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is AmiiboApi other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Head, Tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs
Normal file
15
Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Amiibo
|
||||||
|
{
|
||||||
|
public class AmiiboApiGamesSwitch
|
||||||
|
{
|
||||||
|
[JsonPropertyName("amiiboUsage")]
|
||||||
|
public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
||||||
|
[JsonPropertyName("gameID")]
|
||||||
|
public List<string> GameId { get; set; }
|
||||||
|
[JsonPropertyName("gameName")]
|
||||||
|
public string GameName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs
Normal file
12
Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Amiibo
|
||||||
|
{
|
||||||
|
public class AmiiboApiUsage
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Usage")]
|
||||||
|
public string Usage { get; set; }
|
||||||
|
[JsonPropertyName("write")]
|
||||||
|
public bool Write { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs
Normal file
14
Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Amiibo
|
||||||
|
{
|
||||||
|
public struct AmiiboJson
|
||||||
|
{
|
||||||
|
[JsonPropertyName("amiibo")]
|
||||||
|
public List<AmiiboApi> Amiibo { get; set; }
|
||||||
|
[JsonPropertyName("lastUpdated")]
|
||||||
|
public DateTime LastUpdated { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Amiibo
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(AmiiboJson))]
|
||||||
|
public partial class AmiiboJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Ui.Common.Models.Github
|
||||||
|
{
|
||||||
|
public class GithubReleaseAssetJsonResponse
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
public string BrowserDownloadUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Github
|
||||||
|
{
|
||||||
|
public class GithubReleasesJsonResponse
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public List<GithubReleaseAssetJsonResponse> Assets { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Common.Models.Github
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||||
|
public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -2,14 +2,14 @@ using Gtk;
|
|||||||
using ICSharpCode.SharpZipLib.GZip;
|
using ICSharpCode.SharpZipLib.GZip;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui;
|
using Ryujinx.Ui;
|
||||||
|
using Ryujinx.Ui.Common.Models.Github;
|
||||||
using Ryujinx.Ui.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -38,6 +38,8 @@ namespace Ryujinx.Modules
|
|||||||
private static string _buildUrl;
|
private static string _buildUrl;
|
||||||
private static long _buildSize;
|
private static long _buildSize;
|
||||||
|
|
||||||
|
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
// On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
|
// On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
|
||||||
private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" };
|
private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" };
|
||||||
|
|
||||||
@@ -107,22 +109,16 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
// Fetch latest build information
|
// Fetch latest build information
|
||||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
|
||||||
JToken assets = jsonRoot["assets"];
|
_buildVer = fetched.Name;
|
||||||
|
|
||||||
_buildVer = (string)jsonRoot["name"];
|
foreach (var asset in fetched.Assets)
|
||||||
|
|
||||||
foreach (JToken asset in assets)
|
|
||||||
{
|
{
|
||||||
string assetName = (string)asset["name"];
|
if (asset.Name.StartsWith("ryujinx") && asset.Name.EndsWith(_platformExt))
|
||||||
string assetState = (string)asset["state"];
|
|
||||||
string downloadURL = (string)asset["browser_download_url"];
|
|
||||||
|
|
||||||
if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt))
|
|
||||||
{
|
{
|
||||||
_buildUrl = downloadURL;
|
_buildUrl = asset.BrowserDownloadUrl;
|
||||||
|
|
||||||
if (assetState != "uploaded")
|
if (asset.State != "uploaded")
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
|
@@ -31,7 +31,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
{
|
{
|
||||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||||
|
|
||||||
_patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString));
|
_patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -3,6 +3,7 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
using Ryujinx.Ui.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -11,65 +12,15 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi;
|
||||||
|
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Windows
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
public partial class AmiiboWindow : Window
|
public partial class AmiiboWindow : Window
|
||||||
{
|
{
|
||||||
private struct AmiiboJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiibo")]
|
|
||||||
public List<AmiiboApi> Amiibo { get; set; }
|
|
||||||
[JsonPropertyName("lastUpdated")]
|
|
||||||
public DateTime LastUpdated { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct AmiiboApi
|
|
||||||
{
|
|
||||||
[JsonPropertyName("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonPropertyName("head")]
|
|
||||||
public string Head { get; set; }
|
|
||||||
[JsonPropertyName("tail")]
|
|
||||||
public string Tail { get; set; }
|
|
||||||
[JsonPropertyName("image")]
|
|
||||||
public string Image { get; set; }
|
|
||||||
[JsonPropertyName("amiiboSeries")]
|
|
||||||
public string AmiiboSeries { get; set; }
|
|
||||||
[JsonPropertyName("character")]
|
|
||||||
public string Character { get; set; }
|
|
||||||
[JsonPropertyName("gameSeries")]
|
|
||||||
public string GameSeries { get; set; }
|
|
||||||
[JsonPropertyName("type")]
|
|
||||||
public string Type { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("release")]
|
|
||||||
public Dictionary<string, string> Release { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gamesSwitch")]
|
|
||||||
public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AmiiboApiGamesSwitch
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiiboUsage")]
|
|
||||||
public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
|
||||||
[JsonPropertyName("gameID")]
|
|
||||||
public List<string> GameId { get; set; }
|
|
||||||
[JsonPropertyName("gameName")]
|
|
||||||
public string GameName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AmiiboApiUsage
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Usage")]
|
|
||||||
public string Usage { get; set; }
|
|
||||||
[JsonPropertyName("write")]
|
|
||||||
public bool Write { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string DEFAULT_JSON = "{ \"amiibo\": [] }";
|
private const string DEFAULT_JSON = "{ \"amiibo\": [] }";
|
||||||
|
|
||||||
public string AmiiboId { get; private set; }
|
public string AmiiboId { get; private set; }
|
||||||
@@ -96,6 +47,8 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
private List<AmiiboApi> _amiiboList;
|
private List<AmiiboApi> _amiiboList;
|
||||||
|
|
||||||
|
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo")
|
public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo")
|
||||||
{
|
{
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
|
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
|
||||||
@@ -127,9 +80,9 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
if (File.Exists(_amiiboJsonPath))
|
if (File.Exists(_amiiboJsonPath))
|
||||||
{
|
{
|
||||||
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@@ -148,7 +101,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo;
|
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
if (LastScannedAmiiboShowAll)
|
if (LastScannedAmiiboShowAll)
|
||||||
|
@@ -115,6 +115,8 @@ namespace Ryujinx.Ui.Windows
|
|||||||
private bool _mousePressed;
|
private bool _mousePressed;
|
||||||
private bool _middleMousePressed;
|
private bool _middleMousePressed;
|
||||||
|
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { }
|
public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { }
|
||||||
|
|
||||||
private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin"))
|
private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin"))
|
||||||
@@ -1120,10 +1122,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream stream = File.OpenRead(path))
|
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
||||||
{
|
|
||||||
config = JsonHelper.Deserialize<InputConfig>(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (JsonException) { }
|
catch (JsonException) { }
|
||||||
}
|
}
|
||||||
@@ -1145,9 +1144,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
if (profileDialog.Run() == (int)ResponseType.Ok)
|
if (profileDialog.Run() == (int)ResponseType.Ok)
|
||||||
{
|
{
|
||||||
string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName);
|
string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName);
|
||||||
string jsonString;
|
string jsonString = JsonHelper.Serialize(inputConfig, SerializerContext.InputConfig);
|
||||||
|
|
||||||
jsonString = JsonHelper.Serialize(inputConfig, true);
|
|
||||||
|
|
||||||
File.WriteAllText(path, jsonString);
|
File.WriteAllText(path, jsonString);
|
||||||
}
|
}
|
||||||
|
@@ -7,15 +7,13 @@ using LibHac.Tools.Fs;
|
|||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Windows
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
@@ -26,6 +24,8 @@ namespace Ryujinx.Ui.Windows
|
|||||||
private readonly string _dlcJsonPath;
|
private readonly string _dlcJsonPath;
|
||||||
private readonly List<DownloadableContentContainer> _dlcContainerList;
|
private readonly List<DownloadableContentContainer> _dlcContainerList;
|
||||||
|
|
||||||
|
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
#pragma warning disable CS0649, IDE0044
|
#pragma warning disable CS0649, IDE0044
|
||||||
[GUI] Label _baseTitleInfoLabel;
|
[GUI] Label _baseTitleInfoLabel;
|
||||||
[GUI] TreeView _dlcTreeView;
|
[GUI] TreeView _dlcTreeView;
|
||||||
@@ -45,7 +45,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_dlcJsonPath);
|
_dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -260,10 +260,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
while (_dlcTreeView.Model.IterNext(ref parentIter));
|
while (_dlcTreeView.Model.IterNext(ref parentIter));
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream dlcJsonStream = File.Create(_dlcJsonPath, 4096, FileOptions.WriteThrough))
|
JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer);
|
||||||
{
|
|
||||||
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_dlcContainerList, true)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ using LibHac.Ns;
|
|||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.Ui.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
@@ -15,10 +16,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Windows
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
@@ -32,6 +31,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
private TitleUpdateMetadata _titleUpdateWindowData;
|
private TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
|
|
||||||
private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary;
|
private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary;
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
#pragma warning disable CS0649, IDE0044
|
#pragma warning disable CS0649, IDE0044
|
||||||
[GUI] Label _baseTitleInfoLabel;
|
[GUI] Label _baseTitleInfoLabel;
|
||||||
@@ -54,7 +54,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_updateJsonPath);
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -193,10 +193,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream dlcJsonStream = File.Create(_updateJsonPath, 4096, FileOptions.WriteThrough))
|
JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
|
||||||
{
|
|
||||||
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
|
||||||
}
|
|
||||||
|
|
||||||
_parent.UpdateGameTable();
|
_parent.UpdateGameTable();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user