Compare commits

..

No commits in common. "master" and "Canary-1.3.84" have entirely different histories.

25 changed files with 507 additions and 515 deletions

View file

@ -243,7 +243,3 @@ jobs:
- name: Send notification webhook
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|FF4500|${{ secrets.CANARY_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
- name: Notify update server of new builds
run: |
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=canary' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'

View file

@ -228,7 +228,3 @@ jobs:
- name: Send notification webhook
run: |
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|32cd32|${{ secrets.STABLE_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
- name: Notify update server of new builds
run: |
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=stable' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'

View file

@ -42,8 +42,6 @@
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.29" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.29" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.6.0" />

View file

@ -7,8 +7,8 @@
# Ryujinx
[![Latest release](https://img.shields.io/gitlab/v/release/ryubing%2Fryujinx?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=stable&color=32cd32)](https://update.ryujinx.app/latest/stable)
[![Latest canary release](https://img.shields.io/gitlab/v/release/ryubing%2Fcanary?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=canary&color=FF4500)](https://update.ryujinx.app/latest/canary)
[![Latest release](https://img.shields.io/gitlab/v/release/ryubing%2Fryujinx?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=stable&color=32cd32)](https://git.ryujinx.app/ryubing/ryujinx/-/releases)
[![Latest canary release](https://img.shields.io/gitlab/v/release/ryubing%2Fcanary?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=canary&color=FF4500)](https://git.ryujinx.app/ryubing/canary/-/releases)
<br>
<a href="https://discord.gg/PEuzjrFXUA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">

View file

@ -77,8 +77,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@ -86,9 +84,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\canary.yml = .github\workflows\canary.yml
Directory.Packages.props = Directory.Packages.props
.github\workflows\release.yml = .github\workflows\release.yml
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

File diff suppressed because it is too large Load diff

View file

@ -2436,7 +2436,6 @@
0100E9C010EA8000,"Rise of Insanity",,playable,2020-08-30 15:42:14
01006BA00E652000,"Rise: Race The Future",,playable,2021-02-27 13:29:06
010020C012F48000,"Rising Hell",,playable,2022-10-31 13:54:02
0100D1801A0F4000,"Risk of Rain Returns",,playable,2025-06-28 04:24:04
010076D00E4BA000,"Risk of Rain 2",online-broken,playable,2024-03-04 17:01:05
0100E8300A67A000,"RISK® Global Domination",nvdec;online-broken,playable,2022-08-01 18:53:28
010042500FABA000,"Ritual: Crown of Horns",,playable,2021-01-26 16:01:47
@ -2747,7 +2746,6 @@
01005D701264A000,"SpyHack",,playable,2021-04-15 10:53:51
010077B00E046000,"Spyro™ Reignited Trilogy",nvdec;UE4,playable,2022-09-11 18:38:33
0100085012A0E000,"Squeakers",,playable,2020-12-13 12:13:05
0100E1D01EB2E000,"Squeakross: Home Squeak Home",,playable,2025-06-16 02:02:00
010009300D31C000,"Squidgies Takeover",,playable,2020-07-20 22:28:08
0100FCD0102EC000,"Squidlit",,playable,2020-08-06 12:38:32
0100EBF00E702000,"STAR OCEAN First Departure R",nvdec,playable,2021-07-05 19:29:16
@ -3018,7 +3016,6 @@
01009B101044C000,"The Legend of Heroes: Trails of Cold Steel III Demo",demo;nvdec,playable,2021-04-23 01:07:32
0100D3C010DE8000,"The Legend of Heroes: Trails of Cold Steel IV",nvdec,playable,2021-04-23 14:01:05
01005E5013862000,"THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改]",crash,nothing,2021-09-30 14:41:07
01009C901ACEE000,"The Legend of Nayuta: Boundless Trails",,ingame,2025-06-12 15:47
01008CF01BAAC000,"The Legend of Zelda Echoes of Wisdom",nvdec;ASTC;intel-vendor-bug,playable,2024-10-01 14:11:01
0100509005AF2000,"The Legend of Zelda: Breath of the Wild Demo",demo,ingame,2022-12-24 05:02:58
01007EF00011E000,"The Legend of Zelda™: Breath of the Wild",gpu;amd-vendor-bug;mac-bug,ingame,2024-09-23 19:35:46

1 title_id game_name labels status last_updated
2436 0100E9C010EA8000 Rise of Insanity playable 2020-08-30 15:42:14
2437 01006BA00E652000 Rise: Race The Future playable 2021-02-27 13:29:06
2438 010020C012F48000 Rising Hell playable 2022-10-31 13:54:02
0100D1801A0F4000 Risk of Rain Returns playable 2025-06-28 04:24:04
2439 010076D00E4BA000 Risk of Rain 2 online-broken playable 2024-03-04 17:01:05
2440 0100E8300A67A000 RISK® Global Domination nvdec;online-broken playable 2022-08-01 18:53:28
2441 010042500FABA000 Ritual: Crown of Horns playable 2021-01-26 16:01:47
2746 01005D701264A000 SpyHack playable 2021-04-15 10:53:51
2747 010077B00E046000 Spyro™ Reignited Trilogy nvdec;UE4 playable 2022-09-11 18:38:33
2748 0100085012A0E000 Squeakers playable 2020-12-13 12:13:05
0100E1D01EB2E000 Squeakross: Home Squeak Home playable 2025-06-16 02:02:00
2749 010009300D31C000 Squidgies Takeover playable 2020-07-20 22:28:08
2750 0100FCD0102EC000 Squidlit playable 2020-08-06 12:38:32
2751 0100EBF00E702000 STAR OCEAN First Departure R nvdec playable 2021-07-05 19:29:16
3016 01009B101044C000 The Legend of Heroes: Trails of Cold Steel III Demo demo;nvdec playable 2021-04-23 01:07:32
3017 0100D3C010DE8000 The Legend of Heroes: Trails of Cold Steel IV nvdec playable 2021-04-23 14:01:05
3018 01005E5013862000 THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改] crash nothing 2021-09-30 14:41:07
01009C901ACEE000 The Legend of Nayuta: Boundless Trails ingame 2025-06-12 15:47
3019 01008CF01BAAC000 The Legend of Zelda Echoes of Wisdom nvdec;ASTC;intel-vendor-bug playable 2024-10-01 14:11:01
3020 0100509005AF2000 The Legend of Zelda: Breath of the Wild Demo demo ingame 2022-12-24 05:02:58
3021 01007EF00011E000 The Legend of Zelda™: Breath of the Wild gpu;amd-vendor-bug;mac-bug ingame 2024-09-23 19:35:46

View file

@ -6,20 +6,5 @@
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
<!--<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />-->
<add key="Ryujinx.UpdateClient" value="https://git.ryujinx.app/api/v4/projects/71/packages/nuget/index.json" />
</packageSources>
<packageSourceMapping>
<!-- key value for <packageSource> should match key values from <packageSources> element -->
<!-- These are defined and .NET still yells about multiple package sources with no mappings. Not sure what to do, this is in the docs lol -->
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="Ryujinx.UpdateClient">
<package pattern="Ryujinx.UpdateClient" />
<package pattern="Ryujinx.Systems.Update.Common" />
</packageSource>
<!--<packageSource key="LibHacAlpha">
<package pattern="Ryujinx.LibHac" />
</packageSource>-->
</packageSourceMapping>
</configuration>

View file

@ -195,7 +195,6 @@ namespace Ryujinx.Common
"01008d100d43e000", // Saints Row IV
"0100de600beee000", // Saints Row: The Third - The Full Package
"01001180021fa000", // Shovel Knight: Specter of Torment
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
"0100e65002bb8000", // Stardew Valley
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
"0100800015926000", // Suika Game

View file

@ -21,21 +21,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandCmif(3)] // 20.0.0+
// CreateLibraryAppletEx(u32, u32, u64) -> object<nn::am::service::ILibraryAppletAccessor>
public ResultCode CreateLibraryAppletEx(ServiceCtx context)
{
AppletId appletId = (AppletId)context.RequestData.ReadInt32();
_ = context.RequestData.ReadInt32(); // libraryAppletMode
_ = context.RequestData.ReadUInt64(); // threadId
MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System));
return ResultCode.Success;
}
[CommandCmif(10)]
// CreateStorage(u64) -> object<nn::am::service::IStorage>
public ResultCode CreateStorage(ServiceCtx context)

View file

@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
// F_SETFL
else if (cmd == 0x4)
{
socket.Blocking = (arg & 0x800) == 0;
socket.Blocking = (arg & 0x800) != 0;
result = 0;
}
else

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Ava.Common.Models.Github
{
public class GithubReleaseAssetJsonResponse
{
public string Name { get; set; }
public string State { get; set; }
public string BrowserDownloadUrl { get; set; }
}
}

View file

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Ryujinx.Ava.Common.Models.Github
{
public class GithubReleasesJsonResponse
{
public string Name { get; set; }
public string TagName { get; set; }
public List<GithubReleaseAssetJsonResponse> Assets { get; set; }
}
}

View file

@ -0,0 +1,7 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Common.Models.Github
{
[JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext;
}

View file

@ -65,8 +65,6 @@
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
<PackageReference Include="Ryujinx.UpdateClient" />
<PackageReference Include="Ryujinx.Systems.Update.Common" />
<PackageReference Include="securifybv.ShellLink" />
<PackageReference Include="Sep" />
<PackageReference Include="Silk.NET.Vulkan" />

View file

@ -75,7 +75,6 @@ namespace Ryujinx.Ava.Systems
private readonly long _ticksPerFrame;
private readonly Stopwatch _chrono;
private readonly Stopwatch _playTimer;
private long _ticks;
private readonly AccountManager _accountManager;
@ -176,7 +175,6 @@ namespace Ryujinx.Ava.Systems
_chrono = new Stopwatch();
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
_playTimer = new Stopwatch();
if (ApplicationPath.StartsWith("@SystemContent"))
{
@ -567,7 +565,6 @@ namespace Ryujinx.Ava.Systems
public void Stop()
{
_isActive = false;
_playTimer.Stop();
}
private void Exit()
@ -619,7 +616,7 @@ namespace Ryujinx.Ava.Systems
private void Dispose()
{
if (Device.Processes != null)
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText, _playTimer.Elapsed);
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
@ -638,7 +635,6 @@ namespace Ryujinx.Ava.Systems
_gpuCancellationTokenSource.Dispose();
_chrono.Stop();
_playTimer.Stop();
}
public void DisposeGpu()
@ -872,7 +868,6 @@ namespace Ryujinx.Ava.Systems
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
appMetadata => appMetadata.UpdatePreGame()
);
_playTimer.Start();
return true;
}
@ -882,7 +877,6 @@ namespace Ryujinx.Ava.Systems
Device?.System.TogglePauseEmulation(false);
_viewModel.IsPaused = false;
_playTimer.Start();
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed");
}
@ -892,7 +886,6 @@ namespace Ryujinx.Ava.Systems
Device?.System.TogglePauseEmulation(true);
_viewModel.IsPaused = true;
_playTimer.Stop();
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]);
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
}
@ -1159,24 +1152,6 @@ namespace Ryujinx.Ava.Systems
_dialogShown = true;
// The hard-coded hotkey mapped to exit is Escape, but it's also the same key
// that causes the dialog we launch to close (without doing anything). In release
// mode, a race is observed that between ShowExitPrompt() appearing on KeyDown
// and the ContentDialog we create seeing the key state before KeyUp. Merely waiting
// for the key to no longer be pressed appears to be insufficient.
// NB: Using _keyboardInterface.IsPressed(Key.Escape) does not currently work.
if (OperatingSystem.IsWindows())
{
while (GetAsyncKeyState(0x1B) != 0)
{
await Task.Delay(100);
}
}
else
{
await Task.Delay(250);
}
shouldExit = await ContentDialogHelper.CreateStopEmulationDialog();
_dialogShown = false;

View file

@ -33,11 +33,19 @@ namespace Ryujinx.Ava.Systems.AppLibrary
/// <summary>
/// Updates <see cref="LastPlayed"/> and <see cref="TimePlayed"/>. Call this after a game ends.
/// </summary>
/// <param name="playTime">The active gameplay time this past session.</param>
public void UpdatePostGame(TimeSpan playTime)
public void UpdatePostGame()
{
DateTime? prevLastPlayed = LastPlayed;
UpdatePreGame();
TimePlayed += playTime;
if (!prevLastPlayed.HasValue)
{
return;
}
TimeSpan diff = DateTime.UtcNow - prevLastPlayed.Value;
double newTotalSeconds = TimePlayed.Add(diff).TotalSeconds;
TimePlayed = TimeSpan.FromSeconds(Math.Round(newTotalSeconds, MidpointRounding.AwayFromZero));
}
}
}

View file

@ -4,26 +4,42 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.Systems.Update.Client;
using Ryujinx.Systems.Update.Common;
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Systems
{
internal static partial class Updater
{
private static VersionResponse _versionResponse;
private static string CreateUpdateQueryUrl()
{
#pragma warning disable CS8524
var os = RunningPlatform.CurrentOS switch
#pragma warning restore CS8524
{
OperatingSystemType.MacOS => "mac",
OperatingSystemType.Linux => "linux",
OperatingSystemType.Windows => "win"
};
private static UpdateClient CreateUpdateClient()
=> UpdateClient.Builder()
.WithServerEndpoint("https://update.ryujinx.app") // This is the default, and doesn't need to be provided; it's here for transparency.
.WithLogger((format, args, caller) =>
Logger.Info?.Print(
LogClass.Application,
args.Length is 0 ? format : format.Format(args),
caller: caller)
);
var arch = RunningPlatform.Architecture switch
{
Architecture.Arm64 => "arm",
Architecture.X64 => "amd64",
_ => null
};
if (arch is null)
return null;
var rc = ReleaseInformation.IsCanaryBuild ? "canary" : "stable";
return $"https://update.ryujinx.app/latest/query?os={os}&arch={arch}&rc={rc}";
}
public static async Task<Optional<(Version Current, Version Incoming)>> CheckVersionAsync(bool showVersionUpToDate = false)
{
@ -41,31 +57,39 @@ namespace Ryujinx.Ava.Systems
return default;
}
using UpdateClient updateClient = CreateUpdateClient();
try
if (CreateUpdateQueryUrl() is not {} updateUrl)
{
_versionResponse = await updateClient.QueryLatestAsync(ReleaseInformation.IsCanaryBuild
? ReleaseChannel.Canary
: ReleaseChannel.Stable);
}
catch (Exception e)
{
Logger.Error?.Print(LogClass.Application, $"An error occurred when requesting for updates ({e.GetType().AsFullNamePrettyString()}): {e.Message}");
Logger.Error?.Print(LogClass.Application, "Could not determine URL for updates.");
_running = false;
return default;
}
if (_versionResponse == null)
Logger.Info?.Print(LogClass.Application, $"Checking for updates from {updateUrl}.");
// Get latest version number from update.ryujinx.app API
using HttpClient jsonClient = ConstructHttpClient();
try
{
// logging is done via the UpdateClient library
UpdaterResponse response =
await jsonClient.GetFromJsonAsync(updateUrl, UpdaterResponseJsonContext.Default.UpdaterResponse);
_buildVer = response.Tag;
_buildUrl = response.DownloadUrl;
_changelogUrlFormat = response.ReleaseUrlFormat;
}
catch (Exception e)
{
Logger.Error?.Print(LogClass.Application, $"An error occurred when parsing JSON response from API ({e.GetType().AsFullNamePrettyString()}): {e.Message}");
_running = false;
return default;
}
// If build URL not found, assume no new update is available.
if (_versionResponse.ArtifactUrl is null or "")
if (_buildUrl is null or "")
{
if (showVersionUpToDate)
{
@ -75,7 +99,7 @@ namespace Ryujinx.Ava.Systems
if (userResult is UserResult.Ok)
{
OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion));
OpenHelper.OpenUrl(_changelogUrlFormat.Format(currentVersion));
}
}
@ -87,7 +111,7 @@ namespace Ryujinx.Ava.Systems
}
if (!Version.TryParse(_versionResponse.Version, out Version newVersion))
if (!Version.TryParse(_buildVer, out Version newVersion))
{
Logger.Error?.Print(LogClass.Application,
$"Failed to convert the received {RyujinxApp.FullAppName} version from the update server!");
@ -103,5 +127,17 @@ namespace Ryujinx.Ava.Systems
return (currentVersion, newVersion);
}
[JsonSerializable(typeof(UpdaterResponse))]
partial class UpdaterResponseJsonContext : JsonSerializerContext;
public class UpdaterResponse
{
[JsonPropertyName("tag")] public string Tag { get; set; }
[JsonPropertyName("download_url")] public string DownloadUrl { get; set; }
[JsonPropertyName("web_url")] public string ReleaseUrl { get; set; }
[JsonIgnore] public string ReleaseUrlFormat => ReleaseUrl.Replace(Tag, "{0}");
}
}
}

View file

@ -5,11 +5,13 @@ using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models.Github;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities;
using Ryujinx.Common;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -34,12 +36,16 @@ namespace Ryujinx.Ava.Systems
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
private const int ConnectionCount = 4;
private static string _buildVer;
private static string _buildUrl;
private static long _buildSize;
private static bool _updateSuccessful;
private static bool _running;
private static readonly string[] _windowsDependencyDirs = [];
private static string _changelogUrlFormat = null;
public static async Task BeginUpdateAsync(bool showVersionUpToDate = false)
{
if (_running)
@ -66,7 +72,7 @@ namespace Ryujinx.Ava.Systems
if (userResult is UserResult.Ok)
{
OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion));
OpenHelper.OpenUrl(_changelogUrlFormat.Format(currentVersion));
}
}
@ -86,7 +92,7 @@ namespace Ryujinx.Ava.Systems
// GitLab instance is located in Ukraine. Connection times will vary across the world.
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_versionResponse.ArtifactUrl), HttpCompletionOption.ResponseHeadersRead);
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
_buildSize = message.Content.Headers.ContentRange.Length.Value;
}
@ -116,7 +122,7 @@ namespace Ryujinx.Ava.Systems
switch (shouldUpdate)
{
case UserResult.Yes:
await UpdateRyujinx(_versionResponse.ArtifactUrl);
await UpdateRyujinx(_buildUrl);
break;
// Secondary button maps to no, which in this case is the show changelog button.
case UserResult.No:

29
src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml Executable file → Normal file
View file

@ -41,34 +41,32 @@
Command="{Binding OpenApplicationCompatibility}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuShowCompatEntry}"
Icon="{ext:Icon fa-solid fa-database}"
Icon="{ext:Icon mdi-gamepad}"
ToolTip.Tip="{ext:Locale GameListContextMenuShowCompatEntryToolTip}"/>
<MenuItem
Command="{Binding OpenApplicationData}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuShowGameData}"
Icon="{ext:Icon fa-solid fa-chart-line}"
Icon="{ext:Icon mdi-chart-line}"
ToolTip.Tip="{ext:Locale GameListContextMenuShowGameDataToolTip}"/>
<Separator />
<MenuItem
Command="{Binding OpenUserSaveDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuOpenUserSaveDirectory}"
Icon="{ext:Icon fa-solid fa-sd-card}"
Icon="{ext:Icon mdi-folder-account}"
IsEnabled="{Binding OpenUserSaveDirectoryEnabled}"
ToolTip.Tip="{ext:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenDeviceSaveDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuOpenDeviceSaveDirectory}"
Icon="{ext:Icon fa-solid fa-hard-drive}"
IsEnabled="{Binding OpenDeviceSaveDirectoryEnabled}"
ToolTip.Tip="{ext:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenBcatSaveDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuOpenBcatSaveDirectory}"
Icon="{ext:Icon fa-solid fa-box-archive}"
IsEnabled="{Binding OpenBcatSaveDirectoryEnabled}"
ToolTip.Tip="{ext:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator />
@ -94,20 +92,20 @@
Command="{Binding OpenModManager}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuManageMod}"
Icon="{ext:Icon fa-solid fa-sliders}"
Icon="{ext:Icon mdi-view-module}"
ToolTip.Tip="{ext:Locale GameListContextMenuManageModToolTip}" />
<Separator />
<MenuItem
Command="{Binding OpenModsDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuOpenModsDirectory}"
Icon="{ext:Icon fa-solid fa-folder}"
Icon="{ext:Icon mdi-folder-file}"
ToolTip.Tip="{ext:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenSdModsDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuOpenSdModsDirectory}"
Icon="{ext:Icon fa-solid fa-folder}"
Icon="{ext:Icon mdi-folder-file}"
ToolTip.Tip="{ext:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
<Separator />
<MenuItem
@ -115,41 +113,40 @@
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuTrimXCI}"
IsEnabled="{Binding TrimXCIEnabled}"
Icon="{ext:Icon fa-solid fa-scissors}"
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon fa-solid fa-memory}">
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
<MenuItem
Command="{Binding PurgePtcCache}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}"
Icon="{ext:Icon fa-solid fa-arrow-rotate-right}"
Icon="{ext:Icon mdi-refresh}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
<MenuItem
Command="{Binding NukePtcCache}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuCacheManagementNukePptc}"
Icon="{ext:Icon fa-solid fa-trash-can}"
Icon="{ext:Icon mdi-delete-alert}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementNukePptcToolTip}" />
<MenuItem
Command="{Binding PurgeShaderCache}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
Icon="{ext:Icon fa-solid fa-trash-can}"
Icon="{ext:Icon mdi-delete-alert}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
<MenuItem
Command="{Binding OpenPtcDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
Icon="{ext:Icon fa-solid fa-folder}"
Icon="{ext:Icon mdi-folder-arrow-up-down}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenShaderCacheDirectory}"
CommandParameter="{Binding}"
Header="{ext:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
Icon="{ext:Icon fa-solid fa-folder}"
Icon="{ext:Icon mdi-folder-arrow-up-down}"
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
</MenuItem>
<MenuItem Header="{ext:Locale GameListContextMenuExtractData}" Icon="{ext:Icon fa-solid fa-file-export}">
<MenuItem Header="{ext:Locale GameListContextMenuExtractData}">
<MenuItem
Command="{Binding ExtractApplicationExeFs}"
CommandParameter="{Binding}"

View file

@ -110,8 +110,5 @@ namespace Ryujinx.Ava.UI.Helpers
[LibraryImport("user32.dll", SetLastError = true)]
public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value);
[LibraryImport("user32.dll", SetLastError = true)]
public static partial ushort GetAsyncKeyState(int nVirtKey);
}
}

View file

@ -310,15 +310,10 @@ namespace Ryujinx.Ava.UI.ViewModels
private void TotalTimePlayed_Recalculated(Optional<TimeSpan> ts)
{
if (ts.HasValue)
{
var formattedPlayTime = ValueFormatUtils.FormatTimeSpan(ts.Value);
LocaleManager.Instance.SetDynamicValues(LocaleKeys.GameListLabelTotalTimePlayed, formattedPlayTime);
ShowTotalTimePlayed = formattedPlayTime != string.Empty;
return;
}
ShowTotalTimePlayed = ts.HasValue;
if (ts.HasValue)
LocaleManager.Instance.SetDynamicValues(LocaleKeys.GameListLabelTotalTimePlayed, ValueFormatUtils.FormatTimeSpan(ts.Value));
}
public bool ShowTotalTimePlayed
@ -339,6 +334,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_listSelectedApplication = value;
if (_listSelectedApplication != null && ListAppContextMenu == null)
ListAppContextMenu = new ApplicationContextMenu();
else if (_listSelectedApplication == null && ListAppContextMenu != null)
ListAppContextMenu = null!;
@ -1692,8 +1688,8 @@ namespace Ryujinx.Ava.UI.ViewModels
RendererHostControl.Focus();
});
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
public static void UpdateGameMetadata(string titleId)
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame());
public void RefreshFirmwareStatus()
{

43
src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml Executable file → Normal file
View file

@ -37,7 +37,7 @@
<MenuItem
Command="{Binding OpenFolder}"
Header="{ext:Locale MenuBarFileOpenUnpacked}"
Icon="{ext:Icon fa-solid fa-folder-open}"
Icon="{ext:Icon fa-solid fa-folder}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{ext:Locale LoadApplicationFolderTooltip}" />
<MenuItem
@ -52,7 +52,7 @@
Icon="{ext:Icon fa-solid fa-code-compare}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{ext:Locale LoadTitleUpdatesFromFolderTooltip}" />
<MenuItem Header="{ext:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{ext:Icon fa-solid fa-microchip}">
<MenuItem Header="{ext:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{ext:Icon mdi-launch}">
<MenuItem
Name="MiiAppletMenuItem"
Header="{ext:Locale MenuBarFileOpenAppletOpenMiiApplet}"
@ -63,23 +63,20 @@
<MenuItem
Command="{Binding OpenRyujinxFolder}"
Header="{ext:Locale MenuBarFileOpenEmuFolder}"
Icon="{ext:Icon fa-solid fa-folder-closed}"
ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{Binding OpenScreenshotsFolder}"
Header="{ext:Locale MenuBarFileOpenScreenshotsFolder}"
Icon="{ext:Icon fa-solid fa-desktop}"
ToolTip.Tip="{ext:Locale OpenScreenshotFolderTooltip}"/>
<MenuItem
Command="{Binding OpenLogsFolder}"
Header="{ext:Locale MenuBarFileOpenLogsFolder}"
Icon="{ext:Icon fa-solid fa-file-lines}"
ToolTip.Tip="{ext:Locale OpenRyujinxLogsTooltip}" />
<Separator />
<MenuItem
Name="CloseRyujinxMenuItem"
Header="{ext:Locale MenuBarFileExit}"
Icon="{ext:Icon fa-solid fa-power-off}"
Icon="{ext:Icon fa-solid fa-xmark}"
ToolTip.Tip="{ext:Locale ExitTooltip}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarOptions}">
@ -136,7 +133,7 @@
Name="ChangeLanguageMenuItem"
Padding="0"
Header="{ext:Locale MenuBarOptionsChangeLanguage}"
Icon="{ext:Icon fa-solid fa-globe}"
Icon="{ext:Icon fa-solid fa-language}"
Classes="withCheckbox">
</MenuItem>
<MenuItem
@ -156,7 +153,7 @@
Command="{Binding ManageProfiles}"
Padding="0"
Header="{ext:Locale MenuBarOptionsManageUserProfiles}"
Icon="{ext:Icon fa-solid fa-user}"
Icon="{ext:Icon mdi-account}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{ext:Locale OpenProfileManagerTooltip}"
Classes="withCheckbox">
@ -188,33 +185,33 @@
InputGesture="Escape"
IsEnabled="{Binding IsGameRunning}"
ToolTip.Tip="{ext:Locale StopEmulationTooltip}" />
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" Icon="{ext:Icon fa-solid fa-sun}" />
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" />
<Separator />
<MenuItem
Command="{Binding OpenAmiiboWindow}"
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
Header="{ext:Locale MenuBarActionsScanAmiibo}"
Icon="{ext:Icon fa-solid fa-cube}"
Icon="{ext:Icon mdi-cube-scan}"
InputGesture="Ctrl + A"
IsEnabled="{Binding IsAmiiboRequested}" />
<MenuItem
Command="{Binding OpenBinFile}"
AttachedToVisualTree="ScanBinAmiiboMenuItem_AttachedToVisualTree"
Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
Icon="{ext:Icon fa-solid fa-cube}"
Icon="{ext:Icon mdi-cube-scan}"
IsVisible="{Binding CanScanAmiiboBinaries}"
InputGesture="Ctrl + B"
IsEnabled="{Binding IsAmiiboBinRequested}" />
<MenuItem
Command="{Binding TakeScreenshot}"
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
Icon="{ext:Icon fa-solid fa-camera}"
Icon="{ext:Icon mdi-monitor-screenshot}"
InputGesture="{Binding ScreenshotKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{Binding HideUi}"
Header="{ext:Locale MenuBarFileToolsHideUi}"
Icon="{ext:Icon fa-solid fa-eye-slash}"
Icon="{ext:Icon mdi-eye-off}"
InputGesture="{Binding ShowUiKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
@ -225,12 +222,12 @@
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarActions}" IsVisible="{Binding EnableNonGameRunningControls}">
<MenuItem Header="{ext:Locale MenuBarActionsInstallKeys}" Icon="{ext:Icon fa-solid fa-key}">
<MenuItem Command="{Binding InstallKeysFromFile}" Header="{ext:Locale MenuBarFileActionsInstallKeysFromFile}" Icon="{ext:Icon fa-solid fa-file-code}" />
<MenuItem Command="{Binding InstallKeysFromFolder}" Header="{ext:Locale MenuBarFileActionsInstallKeysFromFolder}" Icon="{ext:Icon fa-solid fa-folder-closed}" />
<MenuItem Command="{Binding InstallKeysFromFile}" Header="{ext:Locale MenuBarFileActionsInstallKeysFromFile}" Icon="{ext:Icon mdi-file-cog}" />
<MenuItem Command="{Binding InstallKeysFromFolder}" Header="{ext:Locale MenuBarFileActionsInstallKeysFromFolder}" Icon="{ext:Icon mdi-folder-cog}" />
</MenuItem>
<MenuItem Header="{ext:Locale MenuBarActionsInstallFirmware}" Icon="{ext:Icon fa-solid fa-floppy-disk}">
<MenuItem Command="{Binding InstallFirmwareFromFile}" Header="{ext:Locale MenuBarActionsInstallFirmwareFromFile}" Icon="{ext:Icon fa-solid fa-file-code}" />
<MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{ext:Locale MenuBarActionsInstallFirmwareFromDirectory}" Icon="{ext:Icon fa-solid fa-folder-closed}" />
<MenuItem Header="{ext:Locale MenuBarActionsInstallFirmware}" Icon="{ext:Icon fa-solid fa-download}">
<MenuItem Command="{Binding InstallFirmwareFromFile}" Header="{ext:Locale MenuBarActionsInstallFirmwareFromFile}" Icon="{ext:Icon mdi-file-cog}" />
<MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{ext:Locale MenuBarActionsInstallFirmwareFromDirectory}" Icon="{ext:Icon mdi-folder-cog}" />
</MenuItem>
<MenuItem Header="{ext:Locale MenuBarActionsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
<MenuItem Name="InstallFileTypesMenuItem" Header="{ext:Locale MenuBarActionsInstallFileTypes}" IsEnabled="{Binding AreMimeTypesRegistered, Converter={x:Static BoolConverters.Not}}" />
@ -257,30 +254,30 @@
Name="UpdateMenuItem"
IsEnabled="{Binding CanUpdate}"
Header="{ext:Locale MenuBarHelpCheckForUpdates}"
Icon="{ext:Icon fa-solid fa-rotate}"
Icon="{ext:Icon mdi-update}"
ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" />
<MenuItem
Name="CompatibilityListMenuItem"
Header="{ext:Locale CompatibilityListOpen}"
Icon="{ext:Icon fa-solid fa-database}"/>
Icon="{ext:Icon mdi-gamepad}"/>
<Separator />
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelpFaqAndGuides}" Icon="{ext:Icon fa-solid fa-question}" >
<MenuItem
Name="FaqMenuItem"
Header="{ext:Locale MenuBarHelpFaq}"
Icon="{ext:Icon fa-brands fa-gitlab}"
Icon="{ext:Icon fa-github}"
CommandParameter="https://git.ryujinx.app/ryubing/ryujinx/-/wikis/FAQ-&amp;-Troubleshooting"
ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" />
<MenuItem
Name="SetupGuideMenuItem"
Header="{ext:Locale MenuBarHelpSetup}"
Icon="{ext:Icon fa-brands fa-gitlab}"
Icon="{ext:Icon fa-github}"
CommandParameter="https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Setup-&amp;-Configuration-Guide"
ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" />
<MenuItem
Name="LdnGuideMenuItem"
Header="{ext:Locale MenuBarHelpMultiplayer}"
Icon="{ext:Icon fa-brands fa-gitlab}"
Icon="{ext:Icon fa-github}"
CommandParameter="https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Multiplayer-(LDN-Local-Wireless)-Guide"
ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" />
</MenuItem>

View file

@ -64,7 +64,6 @@
MinWidth="200"
Height="6"
VerticalAlignment="Center"
Margin="0, 0, 5, 0"
Foreground="{DynamicResource SystemAccentColorLight2}"
IsVisible="{Binding StatusBarVisible}"
Maximum="{Binding StatusBarProgressMaximum}"

View file

@ -213,13 +213,13 @@ namespace Ryujinx.Ava.UI.Windows
}
}
public async void Application_Opened(object sender, ApplicationOpenedEventArgs args)
public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
{
if (args.Application != null)
{
ViewModel.SelectedIcon = args.Application.Icon;
await ViewModel.LoadApplication(args.Application);
ViewModel.LoadApplication(args.Application).Wait();
}
args.Handled = true;