Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8198b99935 | ||
|
460f96967d | ||
|
7ca779a26d | ||
|
b5032b3c91 | ||
|
f0a3dff136 | ||
|
f659dcb9d8 | ||
|
a34fb0e939 | ||
|
21ce8a9b80 | ||
|
9ecbee8032 | ||
|
80519af67d | ||
|
26e30faff3 | ||
|
0992310b76 | ||
|
009c1101d2 | ||
|
ba95ee54ab |
@@ -63,10 +63,6 @@ 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
|
||||||
|
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -112,6 +112,17 @@ jobs:
|
|||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create tag
|
||||||
|
uses: actions/github-script@v5
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||||
|
sha: context.sha
|
||||||
|
})
|
||||||
|
|
||||||
flatpak_release:
|
flatpak_release:
|
||||||
uses: ./.github/workflows/flatpak.yml
|
uses: ./.github/workflows/flatpak.yml
|
||||||
needs: release
|
needs: release
|
||||||
|
@@ -34,6 +34,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
private static uint GetXcr0Eax()
|
private static uint GetXcr0Eax()
|
||||||
{
|
{
|
||||||
|
if (!FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave))
|
||||||
|
{
|
||||||
|
// XSAVE feature required for xgetbv
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> asmGetXcr0 = new byte[]
|
ReadOnlySpan<byte> asmGetXcr0 = new byte[]
|
||||||
{
|
{
|
||||||
0x31, 0xc9, // xor ecx, ecx
|
0x31, 0xc9, // xor ecx, ecx
|
||||||
@@ -70,6 +76,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Sse42 = 1 << 20,
|
Sse42 = 1 << 20,
|
||||||
Popcnt = 1 << 23,
|
Popcnt = 1 << 23,
|
||||||
Aes = 1 << 25,
|
Aes = 1 << 25,
|
||||||
|
Xsave = 1 << 26,
|
||||||
Osxsave = 1 << 27,
|
Osxsave = 1 << 27,
|
||||||
Avx = 1 << 28,
|
Avx = 1 << 28,
|
||||||
F16c = 1 << 29
|
F16c = 1 << 29
|
||||||
@@ -118,9 +125,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
||||||
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
||||||
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
||||||
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128);
|
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128);
|
||||||
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
||||||
public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Osxsave)
|
public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave)
|
||||||
&& Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm);
|
&& Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm);
|
||||||
public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F;
|
public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F;
|
||||||
public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F;
|
public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F;
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
namespace ARMeilleure.Decoders
|
namespace ARMeilleure.Decoders;
|
||||||
|
|
||||||
|
interface IOpCode32Exception
|
||||||
{
|
{
|
||||||
interface IOpCode32Exception
|
int Id { get; }
|
||||||
{
|
|
||||||
int Id { get; }
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -34,7 +34,7 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
private ManualResetEvent _updateRequiredEvent;
|
||||||
private uint _outputStream;
|
private uint _outputStream;
|
||||||
|
private bool _hasSetupError;
|
||||||
private SDL_AudioCallback _callbackDelegate;
|
private SDL_AudioCallback _callbackDelegate;
|
||||||
private int _bytesPerFrame;
|
private int _bytesPerFrame;
|
||||||
private uint _sampleCount;
|
private uint _sampleCount;
|
||||||
@@ -42,7 +43,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||||
{
|
{
|
||||||
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
||||||
bool needAudioSetup = _outputStream == 0 ||
|
bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
|
||||||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
||||||
|
|
||||||
if (needAudioSetup)
|
if (needAudioSetup)
|
||||||
@@ -51,12 +52,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
|
|
||||||
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
||||||
|
|
||||||
if (newOutputStream == 0)
|
_hasSetupError = newOutputStream == 0;
|
||||||
{
|
|
||||||
// No stream in place, this is unexpected.
|
if (!_hasSetupError)
|
||||||
throw new InvalidOperationException($"OpenStream failed with error: \"{SDL_GetError()}\"");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_outputStream != 0)
|
if (_outputStream != 0)
|
||||||
{
|
{
|
||||||
@@ -151,11 +149,20 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
{
|
{
|
||||||
EnsureAudioStreamSetup(buffer);
|
EnsureAudioStreamSetup(buffer);
|
||||||
|
|
||||||
SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer));
|
if (_outputStream != 0)
|
||||||
|
{
|
||||||
|
SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer));
|
||||||
|
|
||||||
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
||||||
|
|
||||||
_queuedBuffers.Enqueue(driverBuffer);
|
_queuedBuffers.Enqueue(driverBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Interlocked.Add(ref _playedSampleCount, GetSampleCount(buffer));
|
||||||
|
|
||||||
|
_updateRequiredEvent.Set();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetVolume(float volume)
|
public override void SetVolume(float volume)
|
||||||
|
@@ -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(languageJson, CommonJsonContext.Default.StringDictionary);
|
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
||||||
|
|
||||||
foreach (var item in strings)
|
foreach (var item in strings)
|
||||||
{
|
{
|
||||||
|
@@ -4,14 +4,13 @@ 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;
|
||||||
@@ -32,7 +31,6 @@ 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");
|
||||||
@@ -101,16 +99,22 @@ 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);
|
||||||
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
|
JObject jsonRoot = JObject.Parse(fetchedJson);
|
||||||
_buildVer = fetched.Name;
|
JToken assets = jsonRoot["assets"];
|
||||||
|
|
||||||
foreach (var asset in fetched.Assets)
|
_buildVer = (string)jsonRoot["name"];
|
||||||
|
|
||||||
|
foreach (JToken asset in assets)
|
||||||
{
|
{
|
||||||
if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt))
|
string assetName = (string)asset["name"];
|
||||||
{
|
string assetState = (string)asset["state"];
|
||||||
_buildUrl = asset.BrowserDownloadUrl;
|
string downloadURL = (string)asset["browser_download_url"];
|
||||||
|
|
||||||
if (asset.State != "uploaded")
|
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
|
||||||
|
{
|
||||||
|
_buildUrl = downloadURL;
|
||||||
|
|
||||||
|
if (assetState != "uploaded")
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
|
72
Ryujinx.Ava/UI/Models/Amiibo.cs
Normal file
72
Ryujinx.Ava/UI/Models/Amiibo.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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(patreonJsonString, CommonJsonContext.Default.StringArray) + "\n\n");
|
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\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,7 +17,6 @@ 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
|
||||||
{
|
{
|
||||||
@@ -32,8 +31,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private readonly StyleableWindow _owner;
|
private readonly StyleableWindow _owner;
|
||||||
|
|
||||||
private Bitmap _amiiboImage;
|
private Bitmap _amiiboImage;
|
||||||
private List<AmiiboApi> _amiiboList;
|
private List<Amiibo.AmiiboApi> _amiiboList;
|
||||||
private AvaloniaList<AmiiboApi> _amiibos;
|
private AvaloniaList<Amiibo.AmiiboApi> _amiibos;
|
||||||
private ObservableCollection<string> _amiiboSeries;
|
private ObservableCollection<string> _amiiboSeries;
|
||||||
|
|
||||||
private int _amiiboSelectedIndex;
|
private int _amiiboSelectedIndex;
|
||||||
@@ -42,8 +41,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _showAllAmiibo;
|
private bool _showAllAmiibo;
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -55,9 +52,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<AmiiboApi>();
|
_amiiboList = new List<Amiibo.AmiiboApi>();
|
||||||
_amiiboSeries = new ObservableCollection<string>();
|
_amiiboSeries = new ObservableCollection<string>();
|
||||||
_amiibos = new AvaloniaList<AmiiboApi>();
|
_amiibos = new AvaloniaList<Amiibo.AmiiboApi>();
|
||||||
|
|
||||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
||||||
|
|
||||||
@@ -97,7 +94,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AvaloniaList<AmiiboApi> AmiiboList
|
public AvaloniaList<Amiibo.AmiiboApi> AmiiboList
|
||||||
{
|
{
|
||||||
get => _amiibos;
|
get => _amiibos;
|
||||||
set
|
set
|
||||||
@@ -190,9 +187,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (File.Exists(_amiiboJsonPath))
|
if (File.Exists(_amiiboJsonPath))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@@ -209,7 +206,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
|
_amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
ParseAmiiboData();
|
ParseAmiiboData();
|
||||||
@@ -226,7 +223,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!ShowAllAmiibo)
|
if (!ShowAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@@ -258,7 +255,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private void SelectLastScannedAmiibo()
|
private void SelectLastScannedAmiibo()
|
||||||
{
|
{
|
||||||
AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
Amiibo.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);
|
||||||
@@ -273,7 +270,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AmiiboApi> amiiboSortedList = _amiiboList
|
List<Amiibo.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();
|
||||||
|
|
||||||
@@ -283,7 +280,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!_showAllAmiibo)
|
if (!_showAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@@ -317,7 +314,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
||||||
|
|
||||||
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
||||||
|
|
||||||
@@ -329,11 +326,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
bool writable = false;
|
bool writable = false;
|
||||||
|
|
||||||
foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (item.GameId.Contains(TitleId))
|
if (item.GameId.Contains(TitleId))
|
||||||
{
|
{
|
||||||
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage)
|
||||||
{
|
{
|
||||||
usageString += Environment.NewLine +
|
usageString += Environment.NewLine +
|
||||||
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
||||||
|
@@ -51,8 +51,6 @@ 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; }
|
||||||
|
|
||||||
@@ -708,7 +706,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
using (Stream stream = File.OpenRead(path))
|
||||||
|
{
|
||||||
|
config = JsonHelper.Deserialize<InputConfig>(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (JsonException) { }
|
catch (JsonException) { }
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
@@ -774,7 +775,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
|
|
||||||
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
|
string jsonString = JsonHelper.Serialize(config, true);
|
||||||
|
|
||||||
await File.WriteAllTextAsync(path, jsonString);
|
await File.WriteAllTextAsync(path, jsonString);
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ 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;
|
||||||
|
|
||||||
@@ -40,8 +41,6 @@ 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;
|
||||||
@@ -101,7 +100,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer);
|
_downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -331,7 +330,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
_downloadableContentContainerList.Add(container);
|
_downloadableContentContainerList.Add(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer);
|
using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough))
|
||||||
|
{
|
||||||
|
downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,228 +25,226 @@ 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 readonly string _titleUpdateJsonPath;
|
||||||
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
|
private ulong _titleId { get; }
|
||||||
|
private string _titleName { get; }
|
||||||
|
|
||||||
|
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||||
|
private AvaloniaList<object> _views = new();
|
||||||
|
private object _selectedUpdate;
|
||||||
|
|
||||||
|
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
||||||
{
|
{
|
||||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
get => _titleUpdates;
|
||||||
public readonly string _titleUpdateJsonPath;
|
set
|
||||||
private VirtualFileSystem _virtualFileSystem { get; }
|
|
||||||
private ulong _titleId { get; }
|
|
||||||
private string _titleName { get; }
|
|
||||||
|
|
||||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
|
||||||
private AvaloniaList<object> _views = new();
|
|
||||||
private object _selectedUpdate;
|
|
||||||
|
|
||||||
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
|
||||||
|
|
||||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
|
||||||
{
|
{
|
||||||
get => _titleUpdates;
|
_titleUpdates = value;
|
||||||
set
|
OnPropertyChanged();
|
||||||
{
|
|
||||||
_titleUpdates = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AvaloniaList<object> Views
|
public AvaloniaList<object> Views
|
||||||
|
{
|
||||||
|
get => _views;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get => _views;
|
_views = value;
|
||||||
set
|
OnPropertyChanged();
|
||||||
{
|
|
||||||
_views = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public object SelectedUpdate
|
public object SelectedUpdate
|
||||||
|
{
|
||||||
|
get => _selectedUpdate;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get => _selectedUpdate;
|
_selectedUpdate = value;
|
||||||
set
|
OnPropertyChanged();
|
||||||
{
|
|
||||||
_selectedUpdate = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
|
{
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
|
_titleId = titleId;
|
||||||
|
_titleName = titleName;
|
||||||
|
|
||||||
|
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
||||||
|
|
||||||
_titleId = titleId;
|
|
||||||
_titleName = titleName;
|
|
||||||
|
|
||||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
|
||||||
|
|
||||||
_titleUpdateWindowData = new TitleUpdateMetadata
|
|
||||||
{
|
|
||||||
Selected = "",
|
|
||||||
Paths = new List<string>()
|
|
||||||
};
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadUpdates();
|
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
private void LoadUpdates()
|
|
||||||
{
|
{
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
||||||
|
|
||||||
|
_titleUpdateWindowData = new TitleUpdateMetadata
|
||||||
{
|
{
|
||||||
AddUpdate(path);
|
Selected = "",
|
||||||
}
|
Paths = new List<string>()
|
||||||
|
};
|
||||||
|
|
||||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
|
||||||
|
|
||||||
SelectedUpdate = selected;
|
|
||||||
|
|
||||||
// NOTE: Save the list again to remove leftovers.
|
|
||||||
Save();
|
Save();
|
||||||
SortUpdates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SortUpdates()
|
LoadUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadUpdates()
|
||||||
|
{
|
||||||
|
foreach (string path in _titleUpdateWindowData.Paths)
|
||||||
{
|
{
|
||||||
var list = TitleUpdates.ToList();
|
AddUpdate(path);
|
||||||
|
}
|
||||||
|
|
||||||
list.Sort((first, second) =>
|
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
||||||
|
|
||||||
|
SelectedUpdate = selected;
|
||||||
|
|
||||||
|
// NOTE: Save the list again to remove leftovers.
|
||||||
|
Save();
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortUpdates()
|
||||||
|
{
|
||||||
|
var list = TitleUpdates.ToList();
|
||||||
|
|
||||||
|
list.Sort((first, second) =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
return -1;
|
||||||
{
|
}
|
||||||
return -1;
|
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
||||||
}
|
{
|
||||||
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
return 1;
|
||||||
{
|
}
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
||||||
});
|
});
|
||||||
|
|
||||||
Views.Clear();
|
Views.Clear();
|
||||||
Views.Add(new BaseModel());
|
Views.Add(new BaseModel());
|
||||||
Views.AddRange(list);
|
Views.AddRange(list);
|
||||||
|
|
||||||
if (SelectedUpdate == null)
|
if (SelectedUpdate == null)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[0];
|
||||||
|
}
|
||||||
|
else if (!TitleUpdates.Contains(SelectedUpdate))
|
||||||
|
{
|
||||||
|
if (Views.Count > 1)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
SelectedUpdate = Views[0];
|
SelectedUpdate = Views[0];
|
||||||
}
|
}
|
||||||
else if (!TitleUpdates.Contains(SelectedUpdate))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddUpdate(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
||||||
|
{
|
||||||
|
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (Views.Count > 1)
|
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
||||||
|
|
||||||
|
if (controlNca != null && patchNca != null)
|
||||||
{
|
{
|
||||||
SelectedUpdate = Views[1];
|
ApplicationControlProperty controlData = new();
|
||||||
|
|
||||||
|
using UniqueRef<IFile> nacpFile = new();
|
||||||
|
|
||||||
|
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
|
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddUpdate(string path)
|
|
||||||
{
|
|
||||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
|
||||||
{
|
|
||||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
|
||||||
{
|
|
||||||
ApplicationControlProperty controlData = new();
|
|
||||||
|
|
||||||
using UniqueRef<IFile> nacpFile = new();
|
|
||||||
|
|
||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
|
|
||||||
public void RemoveUpdate(TitleUpdateModel update)
|
|
||||||
{
|
|
||||||
TitleUpdates.Remove(update);
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Add()
|
|
||||||
{
|
|
||||||
OpenFileDialog dialog = new()
|
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
Dispatcher.UIThread.Post(async () =>
|
||||||
AllowMultiple = true
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Filters.Add(new FileDialogFilter
|
|
||||||
{
|
|
||||||
Name = "NSP",
|
|
||||||
Extensions = { "nsp" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
{
|
||||||
foreach (string file in files)
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
||||||
{
|
});
|
||||||
AddUpdate(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Paths.Clear();
|
|
||||||
_titleUpdateWindowData.Selected = "";
|
|
||||||
|
|
||||||
foreach (TitleUpdateModel update in TitleUpdates)
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
|
||||||
|
|
||||||
if (update == SelectedUpdate)
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Selected = update.Path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveUpdate(TitleUpdateModel update)
|
||||||
|
{
|
||||||
|
TitleUpdates.Remove(update);
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Add()
|
||||||
|
{
|
||||||
|
OpenFileDialog dialog = new()
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||||
|
AllowMultiple = true
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.Filters.Add(new FileDialogFilter
|
||||||
|
{
|
||||||
|
Name = "NSP",
|
||||||
|
Extensions = { "nsp" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
|
{
|
||||||
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
AddUpdate(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Paths.Clear();
|
||||||
|
_titleUpdateWindowData.Selected = "";
|
||||||
|
|
||||||
|
foreach (TitleUpdateModel update in TitleUpdates)
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||||
|
|
||||||
|
if (update == SelectedUpdate)
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Selected = update.Path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
||||||
|
}
|
||||||
}
|
}
|
@@ -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(languageJson, CommonJsonContext.Default.StringDictionary);
|
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
||||||
|
|
||||||
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 AmiiboApi ScannedAmiibo { get; set; }
|
public Amiibo.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)
|
||||||
{
|
{
|
||||||
AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
||||||
ScannedAmiibo = amiibo;
|
ScannedAmiibo = amiibo;
|
||||||
IsScanned = true;
|
IsScanned = true;
|
||||||
Close();
|
Close();
|
||||||
|
@@ -6,8 +6,11 @@ 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,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Common.Configuration
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
|
|
||||||
public enum AspectRatio
|
public enum AspectRatio
|
||||||
{
|
{
|
||||||
Fixed4x3,
|
Fixed4x3,
|
||||||
|
@@ -1,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Common.Configuration
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
|
|
||||||
public enum BackendThreading
|
public enum BackendThreading
|
||||||
{
|
{
|
||||||
Auto,
|
Auto,
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
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,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Common.Configuration
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
|
||||||
public enum GraphicsBackend
|
public enum GraphicsBackend
|
||||||
{
|
{
|
||||||
Vulkan,
|
Vulkan,
|
||||||
|
@@ -1,9 +1,5 @@
|
|||||||
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,4 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -7,8 +6,6 @@ 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
|
||||||
@@ -55,8 +52,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
|
|
||||||
return motionBackendType switch
|
return motionBackendType switch
|
||||||
{
|
{
|
||||||
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController),
|
MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options),
|
||||||
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController),
|
MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -66,10 +63,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, SerializerContext.StandardMotionConfigController);
|
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options);
|
||||||
break;
|
break;
|
||||||
case MotionInputBackendType.CemuHook:
|
case MotionInputBackendType.CemuHook:
|
||||||
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController);
|
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
||||||
|
@@ -1,8 +1,5 @@
|
|||||||
using System.Text.Json.Serialization;
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
|
|
||||||
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; }
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
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,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
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,12 +1,9 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using System;
|
using System;
|
||||||
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
|
|
||||||
[Flags]
|
[Flags]
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
public enum ControllerType : int
|
public enum ControllerType : int
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@@ -1,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
|
|
||||||
public enum InputBackendType
|
public enum InputBackendType
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
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>
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
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,16 +1,13 @@
|
|||||||
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
|
||||||
{
|
{
|
||||||
public class JsonInputConfigConverter : JsonConverter<InputConfig>
|
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
|
||||||
@@ -57,8 +54,8 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
|
|
||||||
return backendType switch
|
return backendType switch
|
||||||
{
|
{
|
||||||
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig),
|
InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options),
|
||||||
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig),
|
InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -68,10 +65,10 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
switch (value.Backend)
|
switch (value.Backend)
|
||||||
{
|
{
|
||||||
case InputBackendType.WindowKeyboard:
|
case InputBackendType.WindowKeyboard:
|
||||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig);
|
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options);
|
||||||
break;
|
break;
|
||||||
case InputBackendType.GamepadSDL2:
|
case InputBackendType.GamepadSDL2:
|
||||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig);
|
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
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,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Common.Configuration
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
|
|
||||||
public enum MemoryManagerMode : byte
|
public enum MemoryManagerMode : byte
|
||||||
{
|
{
|
||||||
SoftwarePageTable,
|
SoftwarePageTable,
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
|
||||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
||||||
[JsonSerializable(typeof(TitleUpdateMetadata))]
|
|
||||||
public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,20 +1,22 @@
|
|||||||
using System.Text;
|
using System;
|
||||||
|
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.Append($@"{args.Time:hh\:mm\:ss\.fff}");
|
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time);
|
||||||
sb.Append($" |{args.Level.ToString()[0]}| ");
|
sb.Append($" |{args.Level.ToString()[0]}| ");
|
||||||
|
|
||||||
if (args.ThreadName != null)
|
if (args.ThreadName != null)
|
||||||
@@ -25,17 +27,53 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
sb.Append(args.Message);
|
sb.Append(args.Message);
|
||||||
|
|
||||||
if (args.Data is not null)
|
if (args.Data != null)
|
||||||
{
|
{
|
||||||
sb.Append(' ');
|
PropertyInfo[] props = args.Data.GetType().GetProperties();
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,84 +0,0 @@
|
|||||||
#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,9 +1,5 @@
|
|||||||
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,7 +11,15 @@ 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, object data = null)
|
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,9 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
|
||||||
{
|
|
||||||
[JsonSerializable(typeof(LogEventArgsJson))]
|
|
||||||
internal partial class LogEventJsonSerializerContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,9 +1,5 @@
|
|||||||
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 Ryujinx.Common.Utilities;
|
using System.IO;
|
||||||
using System.IO;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
@@ -25,8 +25,12 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
public void Log(object sender, LogEventArgs e)
|
public void Log(object sender, LogEventArgs e)
|
||||||
{
|
{
|
||||||
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
string text = JsonSerializer.Serialize(e);
|
||||||
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(_stream))
|
||||||
|
{
|
||||||
|
writer.Write(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@@ -1,18 +1,21 @@
|
|||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInterop
|
namespace Ryujinx.Common.SystemInterop
|
||||||
{
|
{
|
||||||
public partial class StdErrAdapter : IDisposable
|
public partial class StdErrAdapter : IDisposable
|
||||||
{
|
{
|
||||||
private bool _disposable = false;
|
private bool _disposable = false;
|
||||||
private UnixStream _pipeReader;
|
private Stream _pipeReader;
|
||||||
private UnixStream _pipeWriter;
|
private Stream _pipeWriter;
|
||||||
private Thread _worker;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private Task _worker;
|
||||||
|
|
||||||
public StdErrAdapter()
|
public StdErrAdapter()
|
||||||
{
|
{
|
||||||
@@ -31,37 +34,39 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
(int readFd, int writeFd) = MakePipe();
|
(int readFd, int writeFd) = MakePipe();
|
||||||
dup2(writeFd, stdErrFileno);
|
dup2(writeFd, stdErrFileno);
|
||||||
|
|
||||||
_pipeReader = new UnixStream(readFd);
|
_pipeReader = CreateFileDescriptorStream(readFd);
|
||||||
_pipeWriter = new UnixStream(writeFd);
|
_pipeWriter = CreateFileDescriptorStream(writeFd);
|
||||||
|
|
||||||
_worker = new Thread(EventWorker);
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_worker = Task.Run(async () => await EventWorkerAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||||
_disposable = true;
|
_disposable = true;
|
||||||
_worker.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
private void EventWorker()
|
private async Task EventWorkerAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
TextReader reader = new StreamReader(_pipeReader);
|
using TextReader reader = new StreamReader(_pipeReader, leaveOpen: true);
|
||||||
string line;
|
string line;
|
||||||
while ((line = reader.ReadLine()) != null)
|
while (cancellationToken.IsCancellationRequested == false && (line = await reader.ReadLineAsync(cancellationToken)) != null)
|
||||||
{
|
{
|
||||||
Logger.Error?.PrintRawMsg(line);
|
Logger.Error?.PrintRawMsg(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
private void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposable)
|
if (_disposable)
|
||||||
{
|
{
|
||||||
|
_disposable = false;
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_worker.Wait(0);
|
||||||
_pipeReader?.Close();
|
_pipeReader?.Close();
|
||||||
_pipeWriter?.Close();
|
_pipeWriter?.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_disposable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,11 +79,11 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
private static partial int dup2(int fd, int fd2);
|
private static partial int dup2(int fd, int fd2);
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static unsafe partial int pipe(int* pipefd);
|
private static partial int pipe(Span<int> pipefd);
|
||||||
|
|
||||||
private static unsafe (int, int) MakePipe()
|
private static (int, int) MakePipe()
|
||||||
{
|
{
|
||||||
int *pipefd = stackalloc int[2];
|
Span<int> pipefd = stackalloc int[2];
|
||||||
|
|
||||||
if (pipe(pipefd) == 0)
|
if (pipe(pipefd) == 0)
|
||||||
{
|
{
|
||||||
@@ -89,5 +94,16 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
throw new();
|
throw new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private static Stream CreateFileDescriptorStream(int fd)
|
||||||
|
{
|
||||||
|
return new FileStream(
|
||||||
|
new SafeFileHandle((IntPtr)fd, ownsHandle: true),
|
||||||
|
FileAccess.ReadWrite
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,155 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInterop
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("linux")]
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
public partial class UnixStream : Stream, IDisposable
|
|
||||||
{
|
|
||||||
private const int InvalidFd = -1;
|
|
||||||
|
|
||||||
private int _fd;
|
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial long read(int fd, IntPtr buf, ulong count);
|
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial long write(int fd, IntPtr buf, ulong count);
|
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial int close(int fd);
|
|
||||||
|
|
||||||
public UnixStream(int fd)
|
|
||||||
{
|
|
||||||
if (InvalidFd == fd)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid file descriptor");
|
|
||||||
}
|
|
||||||
|
|
||||||
_fd = fd;
|
|
||||||
|
|
||||||
CanRead = read(fd, IntPtr.Zero, 0) != -1;
|
|
||||||
CanWrite = write(fd, IntPtr.Zero, 0) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
~UnixStream()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanRead { get; }
|
|
||||||
public override bool CanWrite { get; }
|
|
||||||
public override bool CanSeek => false;
|
|
||||||
|
|
||||||
public override long Length => throw new NotSupportedException();
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => throw new NotSupportedException();
|
|
||||||
set => throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override unsafe int Read([In, Out] byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Length == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
long r = 0;
|
|
||||||
fixed (byte* buf = &buffer[offset])
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
r = read(_fd, (IntPtr)buf, (ulong)count);
|
|
||||||
} while (ShouldRetry(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override unsafe void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed (byte* buf = &buffer[offset])
|
|
||||||
{
|
|
||||||
long r = 0;
|
|
||||||
do {
|
|
||||||
r = write(_fd, (IntPtr)buf, (ulong)count);
|
|
||||||
} while (ShouldRetry(r));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
if (_fd == InvalidFd)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Flush();
|
|
||||||
|
|
||||||
int r;
|
|
||||||
do {
|
|
||||||
r = close(_fd);
|
|
||||||
} while (ShouldRetry(r));
|
|
||||||
|
|
||||||
_fd = InvalidFd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDisposable.Dispose()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ShouldRetry(long r)
|
|
||||||
{
|
|
||||||
if (r == -1)
|
|
||||||
{
|
|
||||||
const int eintr = 4;
|
|
||||||
|
|
||||||
int errno = Marshal.GetLastPInvokeError();
|
|
||||||
|
|
||||||
if (errno == eintr)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SystemException($"Operation failed with error 0x{errno:X}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
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,62 +1,15 @@
|
|||||||
using System.IO;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
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.Metadata;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
public class JsonHelper
|
public class JsonHelper
|
||||||
{
|
{
|
||||||
private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy();
|
public static JsonNamingPolicy SnakeCase { get; }
|
||||||
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
|
||||||
{
|
{
|
||||||
@@ -67,7 +20,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
for (int i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -81,7 +34,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.Append('_');
|
builder.Append("_");
|
||||||
builder.Append(char.ToLowerInvariant(c));
|
builder.Append(char.ToLowerInvariant(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,5 +47,64 @@ 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,34 +0,0 @@
|
|||||||
#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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
int firstInstance = (int)_state.State.FirstInstance;
|
int firstInstance = (int)_state.State.FirstInstance;
|
||||||
|
|
||||||
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
|
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
|
||||||
|
|
||||||
if (inlineIndexCount != 0)
|
if (inlineIndexCount != 0)
|
||||||
{
|
{
|
||||||
@@ -670,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
if (indexedInline)
|
if (indexedInline)
|
||||||
{
|
{
|
||||||
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
|
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
|
||||||
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
|
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
|
||||||
|
|
||||||
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
|
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
|
||||||
|
@@ -11,9 +11,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct IbStreamer
|
struct IbStreamer
|
||||||
{
|
{
|
||||||
|
private const int BufferCapacity = 256; // Must be a power of 2.
|
||||||
|
|
||||||
private BufferHandle _inlineIndexBuffer;
|
private BufferHandle _inlineIndexBuffer;
|
||||||
private int _inlineIndexBufferSize;
|
private int _inlineIndexBufferSize;
|
||||||
private int _inlineIndexCount;
|
private int _inlineIndexCount;
|
||||||
|
private uint[] _buffer;
|
||||||
|
private int _bufferOffset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if any index buffer data has been pushed.
|
/// Indicates if any index buffer data has been pushed.
|
||||||
@@ -38,9 +42,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// Gets the number of elements on the current inline index buffer,
|
/// Gets the number of elements on the current inline index buffer,
|
||||||
/// while also reseting it to zero for the next draw.
|
/// while also reseting it to zero for the next draw.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="renderer">Host renderer</param>
|
||||||
/// <returns>Inline index bufffer count</returns>
|
/// <returns>Inline index bufffer count</returns>
|
||||||
public int GetAndResetInlineIndexCount()
|
public int GetAndResetInlineIndexCount(IRenderer renderer)
|
||||||
{
|
{
|
||||||
|
UpdateRemaining(renderer);
|
||||||
int temp = _inlineIndexCount;
|
int temp = _inlineIndexCount;
|
||||||
_inlineIndexCount = 0;
|
_inlineIndexCount = 0;
|
||||||
return temp;
|
return temp;
|
||||||
@@ -58,16 +64,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
byte i2 = (byte)(argument >> 16);
|
byte i2 = (byte)(argument >> 16);
|
||||||
byte i3 = (byte)(argument >> 24);
|
byte i3 = (byte)(argument >> 24);
|
||||||
|
|
||||||
Span<uint> data = stackalloc uint[4];
|
int offset = _inlineIndexCount;
|
||||||
|
|
||||||
data[0] = i0;
|
PushData(renderer, offset, i0);
|
||||||
data[1] = i1;
|
PushData(renderer, offset + 1, i1);
|
||||||
data[2] = i2;
|
PushData(renderer, offset + 2, i2);
|
||||||
data[3] = i3;
|
PushData(renderer, offset + 3, i3);
|
||||||
|
|
||||||
int offset = _inlineIndexCount * 4;
|
|
||||||
|
|
||||||
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
|
|
||||||
|
|
||||||
_inlineIndexCount += 4;
|
_inlineIndexCount += 4;
|
||||||
}
|
}
|
||||||
@@ -82,14 +84,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
ushort i0 = (ushort)argument;
|
ushort i0 = (ushort)argument;
|
||||||
ushort i1 = (ushort)(argument >> 16);
|
ushort i1 = (ushort)(argument >> 16);
|
||||||
|
|
||||||
Span<uint> data = stackalloc uint[2];
|
int offset = _inlineIndexCount;
|
||||||
|
|
||||||
data[0] = i0;
|
PushData(renderer, offset, i0);
|
||||||
data[1] = i1;
|
PushData(renderer, offset + 1, i1);
|
||||||
|
|
||||||
int offset = _inlineIndexCount * 4;
|
|
||||||
|
|
||||||
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
|
|
||||||
|
|
||||||
_inlineIndexCount += 2;
|
_inlineIndexCount += 2;
|
||||||
}
|
}
|
||||||
@@ -103,13 +101,61 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
uint i0 = (uint)argument;
|
uint i0 = (uint)argument;
|
||||||
|
|
||||||
Span<uint> data = stackalloc uint[1];
|
int offset = _inlineIndexCount++;
|
||||||
|
|
||||||
data[0] = i0;
|
PushData(renderer, offset, i0);
|
||||||
|
}
|
||||||
|
|
||||||
int offset = _inlineIndexCount++ * 4;
|
/// <summary>
|
||||||
|
/// Pushes a 32-bit value to the index buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="renderer">Host renderer</param>
|
||||||
|
/// <param name="offset">Offset where the data should be written, in 32-bit words</param>
|
||||||
|
/// <param name="value">Index value to be written</param>
|
||||||
|
private void PushData(IRenderer renderer, int offset, uint value)
|
||||||
|
{
|
||||||
|
if (_buffer == null)
|
||||||
|
{
|
||||||
|
_buffer = new uint[BufferCapacity];
|
||||||
|
}
|
||||||
|
|
||||||
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
|
// We upload data in chunks.
|
||||||
|
// If we are at the start of a chunk, then the buffer might be full,
|
||||||
|
// in that case we need to submit any existing data before overwriting the buffer.
|
||||||
|
int subOffset = offset & (BufferCapacity - 1);
|
||||||
|
|
||||||
|
if (subOffset == 0 && offset != 0)
|
||||||
|
{
|
||||||
|
int baseOffset = (offset - BufferCapacity) * sizeof(uint);
|
||||||
|
BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, BufferCapacity * sizeof(uint));
|
||||||
|
renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer[subOffset] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes sure that any pending data is submitted to the GPU before the index buffer is used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="renderer">Host renderer</param>
|
||||||
|
private void UpdateRemaining(IRenderer renderer)
|
||||||
|
{
|
||||||
|
int offset = _inlineIndexCount;
|
||||||
|
if (offset == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = offset & (BufferCapacity - 1);
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
count = BufferCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
int baseOffset = (offset - count) * sizeof(uint);
|
||||||
|
int length = count * sizeof(uint);
|
||||||
|
BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, length);
|
||||||
|
renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer).Slice(0, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -117,12 +163,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="renderer">Host renderer</param>
|
/// <param name="renderer">Host renderer</param>
|
||||||
/// <param name="offset">Offset where the data will be written</param>
|
/// <param name="offset">Offset where the data will be written</param>
|
||||||
|
/// <param name="length">Number of bytes that will be written</param>
|
||||||
/// <returns>Buffer handle</returns>
|
/// <returns>Buffer handle</returns>
|
||||||
private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset)
|
private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset, int length)
|
||||||
{
|
{
|
||||||
// Calculate a reasonable size for the buffer that can fit all the data,
|
// Calculate a reasonable size for the buffer that can fit all the data,
|
||||||
// and that also won't require frequent resizes if we need to push more data.
|
// and that also won't require frequent resizes if we need to push more data.
|
||||||
int size = BitUtils.AlignUp(offset + 0x10, 0x200);
|
int size = BitUtils.AlignUp(offset + length + 0x10, 0x200);
|
||||||
|
|
||||||
if (_inlineIndexBuffer == BufferHandle.Null)
|
if (_inlineIndexBuffer == BufferHandle.Null)
|
||||||
{
|
{
|
||||||
|
@@ -130,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return ref descriptor;
|
return ref descriptor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texture.SynchronizeMemory();
|
||||||
|
}
|
||||||
|
|
||||||
Items[id] = texture;
|
Items[id] = texture;
|
||||||
|
|
||||||
@@ -233,7 +237,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queues a request to update a texture's mapping.
|
/// Queues a request to update a texture's mapping.
|
||||||
/// Mapping is updated later to avoid deleting the texture if it is still sparsely mapped.
|
/// Mapping is updated later to avoid deleting the texture if it is still sparsely mapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="texture">Texture with potential mapping change</param>
|
/// <param name="texture">Texture with potential mapping change</param>
|
||||||
|
@@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else if (texture is TextureView view)
|
else if (texture is TextureView view)
|
||||||
{
|
{
|
||||||
view.Storage.InsertBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||||
|
|
||||||
_textureRefs[binding] = view.GetImageView();
|
_textureRefs[binding] = view.GetImageView();
|
||||||
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
|
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
|
||||||
|
@@ -322,7 +322,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
GAL.Format.S8Uint => ImageAspectFlags.StencilBit,
|
GAL.Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||||
GAL.Format.D24UnormS8Uint or
|
GAL.Format.D24UnormS8Uint or
|
||||||
GAL.Format.D32FloatS8Uint or
|
GAL.Format.D32FloatS8Uint or
|
||||||
GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit,
|
GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit,
|
||||||
_ => ImageAspectFlags.ColorBit
|
_ => ImageAspectFlags.ColorBit
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -218,5 +218,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AccessFlags.DepthStencilAttachmentWriteBit,
|
AccessFlags.DepthStencilAttachmentWriteBit,
|
||||||
PipelineStageFlags.ColorAttachmentOutputBit);
|
PipelineStageFlags.ColorAttachmentOutputBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InsertClearBarrier(CommandBufferScoped cbs, int index)
|
||||||
|
{
|
||||||
|
if (_colors != null)
|
||||||
|
{
|
||||||
|
int realIndex = Array.IndexOf(AttachmentIndices, index);
|
||||||
|
|
||||||
|
if (realIndex != -1)
|
||||||
|
{
|
||||||
|
_colors[realIndex].Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertClearBarrierDS(CommandBufferScoped cbs)
|
||||||
|
{
|
||||||
|
_depthStencil?.Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.EarlyFragmentTestsBit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -226,6 +226,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
|
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
|
||||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||||
|
|
||||||
|
FramebufferParams.InsertClearBarrier(Cbs, index);
|
||||||
|
|
||||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +258,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var attachment = new ClearAttachment(flags, 0, clearValue);
|
var attachment = new ClearAttachment(flags, 0, clearValue);
|
||||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||||
|
|
||||||
|
FramebufferParams.InsertClearBarrierDS(Cbs);
|
||||||
|
|
||||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private AccessFlags _lastModificationAccess;
|
private AccessFlags _lastModificationAccess;
|
||||||
private PipelineStageFlags _lastModificationStage;
|
private PipelineStageFlags _lastModificationStage;
|
||||||
|
private AccessFlags _lastReadAccess;
|
||||||
|
private PipelineStageFlags _lastReadStage;
|
||||||
|
|
||||||
private int _viewsCount;
|
private int _viewsCount;
|
||||||
private ulong _size;
|
private ulong _size;
|
||||||
@@ -440,31 +442,39 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_lastModificationStage = stage;
|
_lastModificationStage = stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InsertBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
||||||
{
|
{
|
||||||
|
if (_lastReadAccess != AccessFlags.NoneKhr)
|
||||||
|
{
|
||||||
|
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
|
TextureView.InsertImageBarrier(
|
||||||
|
_gd.Api,
|
||||||
|
cbs.CommandBuffer,
|
||||||
|
_imageAuto.Get(cbs).Value,
|
||||||
|
_lastReadAccess,
|
||||||
|
dstAccessFlags,
|
||||||
|
_lastReadStage,
|
||||||
|
dstStageFlags,
|
||||||
|
aspectFlags,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
_info.GetLayers(),
|
||||||
|
_info.Levels);
|
||||||
|
|
||||||
|
_lastReadAccess = AccessFlags.NoneKhr;
|
||||||
|
_lastReadStage = PipelineStageFlags.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
||||||
|
{
|
||||||
|
_lastReadAccess |= dstAccessFlags;
|
||||||
|
_lastReadStage |= dstStageFlags;
|
||||||
|
|
||||||
if (_lastModificationAccess != AccessFlags.NoneKhr)
|
if (_lastModificationAccess != AccessFlags.NoneKhr)
|
||||||
{
|
{
|
||||||
ImageAspectFlags aspectFlags;
|
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
if (_info.Format.IsDepthOrStencil())
|
|
||||||
{
|
|
||||||
if (_info.Format == GAL.Format.S8Uint)
|
|
||||||
{
|
|
||||||
aspectFlags = ImageAspectFlags.StencilBit;
|
|
||||||
}
|
|
||||||
else if (_info.Format == GAL.Format.D16Unorm || _info.Format == GAL.Format.D32Float)
|
|
||||||
{
|
|
||||||
aspectFlags = ImageAspectFlags.DepthBit;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
aspectFlags = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
aspectFlags = ImageAspectFlags.ColorBit;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureView.InsertImageBarrier(
|
TextureView.InsertImageBarrier(
|
||||||
_gd.Api,
|
_gd.Api,
|
||||||
|
@@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private const string AppName = "Ryujinx.Graphics.Vulkan";
|
private const string AppName = "Ryujinx.Graphics.Vulkan";
|
||||||
private const int QueuesCount = 2;
|
private const int QueuesCount = 2;
|
||||||
|
|
||||||
public static string[] DesirableExtensions { get; } = new string[]
|
private static readonly string[] _desirableExtensions = new string[]
|
||||||
{
|
{
|
||||||
ExtConditionalRendering.ExtensionName,
|
ExtConditionalRendering.ExtensionName,
|
||||||
ExtExtendedDynamicState.ExtensionName,
|
ExtExtendedDynamicState.ExtensionName,
|
||||||
@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_KHR_portability_subset", // By spec, we should enable this if present.
|
"VK_KHR_portability_subset", // By spec, we should enable this if present.
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string[] RequiredExtensions { get; } = new string[]
|
private static readonly string[] _requiredExtensions = new string[]
|
||||||
{
|
{
|
||||||
KhrSwapchain.ExtensionName
|
KhrSwapchain.ExtensionName
|
||||||
};
|
};
|
||||||
@@ -337,14 +337,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
|
string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
|
||||||
|
|
||||||
if (RequiredExtensions.Contains(extensionName))
|
if (_requiredExtensions.Contains(extensionName))
|
||||||
{
|
{
|
||||||
extensionMatches++;
|
extensionMatches++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extensionMatches == RequiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
|
return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
||||||
@@ -626,7 +626,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pExtendedFeatures = &featuresCustomBorderColor;
|
pExtendedFeatures = &featuresCustomBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledExtensions = RequiredExtensions.Union(DesirableExtensions.Intersect(supportedExtensions)).ToArray();
|
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray();
|
||||||
|
|
||||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
||||||
|
|
||||||
@@ -672,11 +672,6 @@ 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 static CommandBufferPool CreateCommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex)
|
|
||||||
{
|
|
||||||
return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal unsafe static void CreateDebugMessenger(
|
internal unsafe static void CreateDebugMessenger(
|
||||||
Vk api,
|
Vk api,
|
||||||
GraphicsDebugLevel logLevel,
|
GraphicsDebugLevel logLevel,
|
||||||
|
@@ -168,7 +168,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
|
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Capabilities.SupportsSubgroupSizeControl)
|
bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control");
|
||||||
|
|
||||||
|
if (supportsSubgroupSizeControl)
|
||||||
{
|
{
|
||||||
properties2.PNext = &propertiesSubgroupSizeControl;
|
properties2.PNext = &propertiesSubgroupSizeControl;
|
||||||
}
|
}
|
||||||
@@ -292,7 +294,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
|
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
|
||||||
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
|
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
|
||||||
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
|
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
|
||||||
supportedExtensions.Contains("VK_EXT_subgroup_size_control"),
|
supportsSubgroupSizeControl,
|
||||||
featuresShaderInt8.ShaderInt8,
|
featuresShaderInt8.ShaderInt8,
|
||||||
supportedExtensions.Contains("VK_EXT_shader_stencil_export"),
|
supportedExtensions.Contains("VK_EXT_shader_stencil_export"),
|
||||||
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
|
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
|
||||||
@@ -318,7 +320,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
||||||
|
|
||||||
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||||
|
|
||||||
DescriptorSetManager = new DescriptorSetManager(_device);
|
DescriptorSetManager = new DescriptorSetManager(_device);
|
||||||
|
|
||||||
|
@@ -13,7 +13,6 @@ 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.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
@@ -25,13 +24,14 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using static Ryujinx.HLE.HOS.ModLoader;
|
using static Ryujinx.HLE.HOS.ModLoader;
|
||||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
using ApplicationId = LibHac.Ncm.ApplicationId;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
|
using JsonHelper = Common.Utilities.JsonHelper;
|
||||||
|
|
||||||
public class ApplicationLoader
|
public class ApplicationLoader
|
||||||
{
|
{
|
||||||
// Binaries from exefs are loaded into mem in this order. Do not change.
|
// Binaries from exefs are loaded into mem in this order. Do not change.
|
||||||
@@ -57,10 +57,6 @@ namespace Ryujinx.HLE.HOS
|
|||||||
private string _displayVersion;
|
private string _displayVersion;
|
||||||
private BlitStruct<ApplicationControlProperty> _controlData;
|
private BlitStruct<ApplicationControlProperty> _controlData;
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions SerializerOptions = JsonHelper.GetDefaultSerializerOptions();
|
|
||||||
private static readonly DownloadableContentJsonSerializerContext ContentSerializerContext = new(SerializerOptions);
|
|
||||||
private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(SerializerOptions);
|
|
||||||
|
|
||||||
public BlitStruct<ApplicationControlProperty> ControlData => _controlData;
|
public BlitStruct<ApplicationControlProperty> ControlData => _controlData;
|
||||||
public string TitleName => _titleName;
|
public string TitleName => _titleName;
|
||||||
public string DisplayVersion => _displayVersion;
|
public string DisplayVersion => _displayVersion;
|
||||||
@@ -201,7 +197,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
if (File.Exists(titleUpdateMetadataPath))
|
||||||
{
|
{
|
||||||
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected;
|
updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
|
||||||
|
|
||||||
if (File.Exists(updatePath))
|
if (File.Exists(updatePath))
|
||||||
{
|
{
|
||||||
@@ -415,7 +411,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
if (File.Exists(titleAocMetadataPath))
|
if (File.Exists(titleAocMetadataPath))
|
||||||
{
|
{
|
||||||
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(titleAocMetadataPath, ContentSerializerContext.ListDownloadableContentContainer);
|
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath);
|
||||||
|
|
||||||
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
||||||
{
|
{
|
||||||
|
@@ -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,7 +13,29 @@ 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 static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private struct ProfilesJson
|
||||||
|
{
|
||||||
|
[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; }
|
||||||
|
|
||||||
@@ -25,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson);
|
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
|
||||||
|
|
||||||
foreach (var profile in profilesJson.Profiles)
|
foreach (var profile in profilesJson.Profiles)
|
||||||
{
|
{
|
||||||
@@ -70,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson);
|
File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,11 +0,0 @@
|
|||||||
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,9 +1,5 @@
|
|||||||
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,
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
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,6 +1,5 @@
|
|||||||
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;
|
||||||
@@ -9,6 +8,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 System.Text.Json;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
{
|
{
|
||||||
@@ -16,8 +17,6 @@ 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)
|
||||||
@@ -174,7 +173,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||||||
|
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
{
|
{
|
||||||
virtualAmiiboFile = JsonHelper.DeserializeFromFile(filePath, SerializerContext.VirtualAmiiboFile);
|
virtualAmiiboFile = JsonSerializer.Deserialize<VirtualAmiiboFile>(File.ReadAllText(filePath), new JsonSerializerOptions(JsonSerializerDefaults.General));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -198,7 +197,8 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Nim
|
|||||||
// CreateServerInterface(pid, handle<unknown>, u64) -> object<nn::ec::IShopServiceAccessServer>
|
// CreateServerInterface(pid, handle<unknown>, u64) -> object<nn::ec::IShopServiceAccessServer>
|
||||||
public ResultCode CreateServerInterface(ServiceCtx context)
|
public ResultCode CreateServerInterface(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
// Close transfer memory immediately as we don't use it.
|
||||||
|
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
|
||||||
|
|
||||||
MakeObject(context, new IShopServiceAccessServer());
|
MakeObject(context, new IShopServiceAccessServer());
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceNim);
|
Logger.Stub?.PrintStub(LogClass.ServiceNim);
|
||||||
|
@@ -56,8 +56,6 @@ 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();
|
||||||
@@ -287,7 +285,10 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
using (Stream stream = File.OpenRead(path))
|
||||||
|
{
|
||||||
|
config = JsonHelper.Deserialize<InputConfig>(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
|
@@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Generators.Kernel
|
namespace Ryujinx.Horizon.Generators.Kernel
|
||||||
{
|
{
|
||||||
@@ -151,24 +152,15 @@ namespace Ryujinx.Horizon.Generators.Kernel
|
|||||||
GenerateMethod32(generator, context.Compilation, method);
|
GenerateMethod32(generator, context.Compilation, method);
|
||||||
GenerateMethod64(generator, context.Compilation, method);
|
GenerateMethod64(generator, context.Compilation, method);
|
||||||
|
|
||||||
foreach (var attributeList in method.AttributeLists)
|
foreach (AttributeSyntax attribute in method.AttributeLists.SelectMany(attributeList =>
|
||||||
|
attributeList.Attributes.Where(attribute =>
|
||||||
|
GetCanonicalTypeName(context.Compilation, attribute) == TypeSvcAttribute)))
|
||||||
{
|
{
|
||||||
foreach (var attribute in attributeList.Attributes)
|
syscalls.AddRange(from attributeArg in attribute.ArgumentList.Arguments
|
||||||
{
|
where attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression
|
||||||
if (GetCanonicalTypeName(context.Compilation, attribute) != TypeSvcAttribute)
|
select (LiteralExpressionSyntax)attributeArg.Expression
|
||||||
{
|
into numericLiteral
|
||||||
continue;
|
select new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text));
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var attributeArg in attribute.ArgumentList.Arguments)
|
|
||||||
{
|
|
||||||
if (attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression)
|
|
||||||
{
|
|
||||||
LiteralExpressionSyntax numericLiteral = (LiteralExpressionSyntax)attributeArg.Expression;
|
|
||||||
syscalls.Add(new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,28 +502,14 @@ namespace Ryujinx.Horizon.Generators.Kernel
|
|||||||
|
|
||||||
private static string GenerateCastFromUInt64(string value, string canonicalTargetTypeName, string targetTypeName)
|
private static string GenerateCastFromUInt64(string value, string canonicalTargetTypeName, string targetTypeName)
|
||||||
{
|
{
|
||||||
if (canonicalTargetTypeName == TypeSystemBoolean)
|
return canonicalTargetTypeName == TypeSystemBoolean ? $"({value} & 1) != 0" : $"({targetTypeName}){value}";
|
||||||
{
|
|
||||||
return $"({value} & 1) != 0";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"({targetTypeName}){value}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsPointerSized(Compilation compilation, ParameterSyntax parameterSyntax)
|
private static bool IsPointerSized(Compilation compilation, ParameterSyntax parameterSyntax)
|
||||||
{
|
{
|
||||||
foreach (var attributeList in parameterSyntax.AttributeLists)
|
return parameterSyntax.AttributeLists.Any(attributeList =>
|
||||||
{
|
attributeList.Attributes.Any(attribute =>
|
||||||
foreach (var attribute in attributeList.Attributes)
|
GetCanonicalTypeName(compilation, attribute) == TypePointerSizedAttribute));
|
||||||
{
|
|
||||||
if (GetCanonicalTypeName(compilation, attribute) == TypePointerSizedAttribute)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(GeneratorInitializationContext context)
|
public void Initialize(GeneratorInitializationContext context)
|
||||||
|
@@ -16,38 +16,37 @@ namespace Ryujinx.Horizon.Generators.Kernel
|
|||||||
|
|
||||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||||
{
|
{
|
||||||
if (syntaxNode is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Count != 0)
|
if (!(syntaxNode is ClassDeclarationSyntax classDeclaration) || classDeclaration.AttributeLists.Count == 0)
|
||||||
{
|
{
|
||||||
foreach (var attributeList in classDeclaration.AttributeLists)
|
return;
|
||||||
{
|
}
|
||||||
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl"))
|
|
||||||
{
|
|
||||||
foreach (var memberDeclaration in classDeclaration.Members)
|
|
||||||
{
|
|
||||||
if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
|
|
||||||
{
|
|
||||||
VisitMethod(methodDeclaration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
if (!classDeclaration.AttributeLists.Any(attributeList =>
|
||||||
}
|
attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl")))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var memberDeclaration in classDeclaration.Members)
|
||||||
|
{
|
||||||
|
if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
|
||||||
|
{
|
||||||
|
VisitMethod(methodDeclaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VisitMethod(MethodDeclarationSyntax methodDeclaration)
|
private void VisitMethod(MethodDeclarationSyntax methodDeclaration)
|
||||||
{
|
{
|
||||||
if (methodDeclaration.AttributeLists.Count != 0)
|
if (methodDeclaration.AttributeLists.Count == 0)
|
||||||
{
|
{
|
||||||
foreach (var attributeList in methodDeclaration.AttributeLists)
|
return;
|
||||||
{
|
}
|
||||||
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc"))
|
|
||||||
{
|
if (methodDeclaration.AttributeLists.Any(attributeList =>
|
||||||
SvcImplementations.Add(methodDeclaration);
|
attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc")))
|
||||||
break;
|
{
|
||||||
}
|
SvcImplementations.Add(methodDeclaration);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,12 +40,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
|||||||
var runtimeMetadata = context.Processor.GetRuntimeMetadata();
|
var runtimeMetadata = context.Processor.GetRuntimeMetadata();
|
||||||
Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata);
|
Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata);
|
||||||
|
|
||||||
if (result.IsFailure)
|
return result.IsFailure ? result : _invoke(ref context, _processor, runtimeMetadata, inRawData, ref outHeader);
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _invoke(ref context, _processor, runtimeMetadata, inRawData, ref outHeader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData)
|
public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData)
|
||||||
|
@@ -53,7 +53,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
|||||||
|
|
||||||
public static void SerializeArg<T>(Span<byte> outRawData, int offset, T value) where T : unmanaged
|
public static void SerializeArg<T>(Span<byte> outRawData, int offset, T value) where T : unmanaged
|
||||||
{
|
{
|
||||||
MemoryMarshal.Cast<byte, T>(outRawData.Slice(offset, Unsafe.SizeOf<T>()))[0] = (T)value;
|
MemoryMarshal.Cast<byte, T>(outRawData.Slice(offset, Unsafe.SizeOf<T>()))[0] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SerializeCopyHandle(HipcMessageData response, int index, int value)
|
public static void SerializeCopyHandle(HipcMessageData response, int index, int value)
|
||||||
|
@@ -41,10 +41,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
|||||||
{
|
{
|
||||||
_args = args;
|
_args = args;
|
||||||
|
|
||||||
for (int i = 0; i < args.Length; i++)
|
foreach (CommandArg argInfo in args)
|
||||||
{
|
{
|
||||||
var argInfo = args[i];
|
|
||||||
|
|
||||||
switch (argInfo.Type)
|
switch (argInfo.Type)
|
||||||
{
|
{
|
||||||
case CommandArgType.Buffer:
|
case CommandArgType.Buffer:
|
||||||
@@ -239,14 +237,13 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
|||||||
{
|
{
|
||||||
return mode == HipcBufferMode.NonSecure;
|
return mode == HipcBufferMode.NonSecure;
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice))
|
|
||||||
|
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice))
|
||||||
{
|
{
|
||||||
return mode == HipcBufferMode.NonDevice;
|
return mode == HipcBufferMode.NonDevice;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return mode == HipcBufferMode.Normal;
|
||||||
return mode == HipcBufferMode.Normal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetOutBuffers(HipcMessageData response, bool[] isBufferMapAlias)
|
public void SetOutBuffers(HipcMessageData response, bool[] isBufferMapAlias)
|
||||||
@@ -261,28 +258,30 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
|||||||
}
|
}
|
||||||
|
|
||||||
var flags = _args[i].BufferFlags;
|
var flags = _args[i].BufferFlags;
|
||||||
if (flags.HasFlag(HipcBufferFlags.Out))
|
if (!flags.HasFlag(HipcBufferFlags.Out))
|
||||||
{
|
{
|
||||||
var buffer = _bufferRanges[i];
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.Pointer))
|
var buffer = _bufferRanges[i];
|
||||||
|
|
||||||
|
if (flags.HasFlag(HipcBufferFlags.Pointer))
|
||||||
|
{
|
||||||
|
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
||||||
|
}
|
||||||
|
else if (flags.HasFlag(HipcBufferFlags.AutoSelect))
|
||||||
|
{
|
||||||
|
if (!isBufferMapAlias[i])
|
||||||
{
|
{
|
||||||
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(HipcBufferFlags.AutoSelect))
|
else
|
||||||
{
|
{
|
||||||
if (!isBufferMapAlias[i])
|
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex);
|
||||||
{
|
|
||||||
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recvPointerIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recvPointerIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,15 +338,17 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
|||||||
|
|
||||||
int inObjectIndex = 0;
|
int inObjectIndex = 0;
|
||||||
|
|
||||||
for (int i = 0; i < _args.Length; i++)
|
foreach (CommandArg t in _args)
|
||||||
{
|
{
|
||||||
if (_args[i].Type == CommandArgType.InObject)
|
if (t.Type != CommandArgType.InObject)
|
||||||
{
|
{
|
||||||
int index = inObjectIndex++;
|
continue;
|
||||||
var inObject = inObjects[index];
|
|
||||||
|
|
||||||
objects[index] = inObject?.ServiceObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int index = inObjectIndex++;
|
||||||
|
var inObject = inObjects[index];
|
||||||
|
|
||||||
|
objects[index] = inObject?.ServiceObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@@ -37,12 +37,7 @@ namespace Ryujinx.Horizon.Sm.Impl
|
|||||||
|
|
||||||
result = GetServiceImpl(out handle, ref _services[serviceIndex]);
|
result = GetServiceImpl(out handle, ref _services[serviceIndex]);
|
||||||
|
|
||||||
if (result == KernelResult.SessionCountExceeded)
|
return result == KernelResult.SessionCountExceeded ? SmResult.OutOfSessions : result;
|
||||||
{
|
|
||||||
return SmResult.OutOfSessions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo)
|
private Result GetServiceImpl(out int handle, ref ServiceInfo serviceInfo)
|
||||||
@@ -61,13 +56,7 @@ namespace Ryujinx.Horizon.Sm.Impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validation with GetProcessInfo etc.
|
// TODO: Validation with GetProcessInfo etc.
|
||||||
|
return HasServiceInfo(name) ? SmResult.AlreadyRegistered : RegisterServiceImpl(out handle, processId, name, maxSessions, isLight);
|
||||||
if (HasServiceInfo(name))
|
|
||||||
{
|
|
||||||
return SmResult.AlreadyRegistered;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RegisterServiceImpl(out handle, processId, name, maxSessions, isLight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result RegisterServiceForSelf(out int handle, ServiceName name, int maxSessions)
|
public Result RegisterServiceForSelf(out int handle, ServiceName name, int maxSessions)
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.App.Common
|
|
||||||
{
|
|
||||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
||||||
[JsonSerializable(typeof(ApplicationMetadata))]
|
|
||||||
internal partial class ApplicationJsonSerializerContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -10,7 +10,6 @@ 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;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
@@ -23,6 +22,7 @@ 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,8 +42,6 @@ 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());
|
|
||||||
|
|
||||||
public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
|
public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
@@ -492,12 +490,14 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
|
|
||||||
appMetadata = new ApplicationMetadata();
|
appMetadata = new ApplicationMetadata();
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata);
|
using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough);
|
||||||
|
|
||||||
|
JsonHelper.Serialize(stream, appMetadata, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata);
|
appMetadata = JsonHelper.DeserializeFromFile<ApplicationMetadata>(metadataFile);
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
@@ -510,7 +510,9 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
{
|
{
|
||||||
modifyFunction(appMetadata);
|
modifyFunction(appMetadata);
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata);
|
using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough);
|
||||||
|
|
||||||
|
JsonHelper.Serialize(stream, appMetadata, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return appMetadata;
|
return appMetadata;
|
||||||
|
@@ -1,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
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.Text.Json.Nodes;
|
using System.IO;
|
||||||
|
|
||||||
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<JsonObject> KeyboardConfig { get; set; }
|
public List<object> 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<JsonObject> ControllerConfig { get; set; }
|
public List<object> ControllerConfig { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input configurations
|
/// Input configurations
|
||||||
@@ -354,12 +354,11 @@ 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(path, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat);
|
configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path);
|
||||||
|
|
||||||
return configurationFileFormat.Version != 0;
|
return configurationFileFormat.Version != 0;
|
||||||
}
|
}
|
||||||
@@ -377,7 +376,8 @@ 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)
|
||||||
{
|
{
|
||||||
JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat);
|
using FileStream fileStream = File.Create(path, 4096, FileOptions.WriteThrough);
|
||||||
|
JsonHelper.Serialize(fileStream, this, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration
|
|
||||||
{
|
|
||||||
internal static class ConfigurationFileFormatSettings
|
|
||||||
{
|
|
||||||
public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration
|
|
||||||
{
|
|
||||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
||||||
[JsonSerializable(typeof(ConfigurationFileFormat))]
|
|
||||||
internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -9,7 +9,6 @@ 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
|
||||||
{
|
{
|
||||||
@@ -632,8 +631,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<JsonObject>(),
|
KeyboardConfig = new List<object>(),
|
||||||
ControllerConfig = new List<JsonObject>(),
|
ControllerConfig = new List<object>(),
|
||||||
InputConfig = Hid.InputConfig,
|
InputConfig = Hid.InputConfig,
|
||||||
GraphicsBackend = Graphics.GraphicsBackend,
|
GraphicsBackend = Graphics.GraphicsBackend,
|
||||||
PreferredGpu = Graphics.PreferredGpu
|
PreferredGpu = Graphics.PreferredGpu
|
||||||
|
@@ -1,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Ui.Common.Configuration.System
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration.System
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<Language>))]
|
|
||||||
public enum Language
|
public enum Language
|
||||||
{
|
{
|
||||||
Japanese,
|
Japanese,
|
||||||
|
@@ -1,9 +1,5 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
namespace Ryujinx.Ui.Common.Configuration.System
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration.System
|
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(TypedStringEnumConverter<Region>))]
|
|
||||||
public enum Region
|
public enum Region
|
||||||
{
|
{
|
||||||
Japan,
|
Japan,
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,9 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Models.Amiibo
|
|
||||||
{
|
|
||||||
[JsonSerializable(typeof(AmiiboJson))]
|
|
||||||
public partial class AmiiboJsonSerializerContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Ui.Common.Models.Github
|
|
||||||
{
|
|
||||||
public class GithubReleaseAssetJsonResponse
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string State { get; set; }
|
|
||||||
public string BrowserDownloadUrl { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Models.Github
|
|
||||||
{
|
|
||||||
public class GithubReleasesJsonResponse
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public List<GithubReleaseAssetJsonResponse> Assets { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,9 +0,0 @@
|
|||||||
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,8 +38,6 @@ 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" };
|
||||||
|
|
||||||
@@ -109,16 +107,22 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
// Fetch latest build information
|
// Fetch latest build information
|
||||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||||
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
|
JObject jsonRoot = JObject.Parse(fetchedJson);
|
||||||
_buildVer = fetched.Name;
|
JToken assets = jsonRoot["assets"];
|
||||||
|
|
||||||
foreach (var asset in fetched.Assets)
|
_buildVer = (string)jsonRoot["name"];
|
||||||
|
|
||||||
|
foreach (JToken asset in assets)
|
||||||
{
|
{
|
||||||
if (asset.Name.StartsWith("ryujinx") && asset.Name.EndsWith(_platformExt))
|
string assetName = (string)asset["name"];
|
||||||
{
|
string assetState = (string)asset["state"];
|
||||||
_buildUrl = asset.BrowserDownloadUrl;
|
string downloadURL = (string)asset["browser_download_url"];
|
||||||
|
|
||||||
if (asset.State != "uploaded")
|
if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt))
|
||||||
|
{
|
||||||
|
_buildUrl = downloadURL;
|
||||||
|
|
||||||
|
if (assetState != "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(patreonJsonString, CommonJsonContext.Default.StringArray));
|
_patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -3,7 +3,6 @@ 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;
|
||||||
@@ -12,15 +11,65 @@ 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;
|
using System.Text.Json.Serialization;
|
||||||
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; }
|
||||||
@@ -47,8 +96,6 @@ 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");
|
||||||
@@ -80,9 +127,9 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
if (File.Exists(_amiiboJsonPath))
|
if (File.Exists(_amiiboJsonPath))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@@ -101,7 +148,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
|
_amiiboList = JsonHelper.Deserialize<AmiiboJson>(amiiboJsonString).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
if (LastScannedAmiiboShowAll)
|
if (LastScannedAmiiboShowAll)
|
||||||
|
@@ -115,8 +115,6 @@ 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"))
|
||||||
@@ -1122,7 +1120,10 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
using (Stream stream = File.OpenRead(path))
|
||||||
|
{
|
||||||
|
config = JsonHelper.Deserialize<InputConfig>(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (JsonException) { }
|
catch (JsonException) { }
|
||||||
}
|
}
|
||||||
@@ -1144,7 +1145,9 @@ 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 = JsonHelper.Serialize(inputConfig, SerializerContext.InputConfig);
|
string jsonString;
|
||||||
|
|
||||||
|
jsonString = JsonHelper.Serialize(inputConfig, true);
|
||||||
|
|
||||||
File.WriteAllText(path, jsonString);
|
File.WriteAllText(path, jsonString);
|
||||||
}
|
}
|
||||||
|
@@ -7,13 +7,15 @@ 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 GUI = Gtk.Builder.ObjectAttribute;
|
using System.Text;
|
||||||
|
|
||||||
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
|
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Windows
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
@@ -24,8 +26,6 @@ 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(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer);
|
_dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_dlcJsonPath);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -260,7 +260,10 @@ namespace Ryujinx.Ui.Windows
|
|||||||
while (_dlcTreeView.Model.IterNext(ref parentIter));
|
while (_dlcTreeView.Model.IterNext(ref parentIter));
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer);
|
using (FileStream dlcJsonStream = File.Create(_dlcJsonPath, 4096, FileOptions.WriteThrough))
|
||||||
|
{
|
||||||
|
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_dlcContainerList, true)));
|
||||||
|
}
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ 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.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
@@ -15,8 +14,10 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using System.Text;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
|
||||||
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
|
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Windows
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,6 @@ 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;
|
||||||
@@ -53,7 +53,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata);
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_updateJsonPath);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -192,7 +192,10 @@ namespace Ryujinx.Ui.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
|
using (FileStream dlcJsonStream = File.Create(_updateJsonPath, 4096, FileOptions.WriteThrough))
|
||||||
|
{
|
||||||
|
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
||||||
|
}
|
||||||
|
|
||||||
_parent.UpdateGameTable();
|
_parent.UpdateGameTable();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user