Compare commits
34 Commits
Canary-1.2
...
3b2a0f7ff6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b2a0f7ff6 | ||
|
|
9eb273a0f7 | ||
|
|
ccddaa77d1 | ||
|
|
01c2e67334 | ||
|
|
4c646721d6 | ||
|
|
d3bc3a1081 | ||
|
|
c69881a0a2 | ||
|
|
1bc0159139 | ||
|
|
0733b7d0a1 | ||
|
|
9df1366fa1 | ||
|
|
c73b5bdf46 | ||
|
|
9754d247b5 | ||
|
|
267e9f6350 | ||
|
|
d7b3dd12d1 | ||
|
|
c9b2a6b1f1 | ||
|
|
d1824c5cb6 | ||
|
|
17233d30da | ||
|
|
2bf48f57d2 | ||
|
|
412d4065b8 | ||
|
|
e6644626fc | ||
|
|
0bacdb8765 | ||
|
|
0ca4d6e921 | ||
|
|
f0aa7eedf6 | ||
|
|
41acc4b1f3 | ||
|
|
7aede70ba9 | ||
|
|
a0a4f78cff | ||
|
|
0502da96cd | ||
|
|
16a60fdf12 | ||
|
|
7df0fb587e | ||
|
|
50847f0687 | ||
|
|
125f6d2777 | ||
|
|
dfc4831828 | ||
|
|
45feeae9db | ||
|
|
e67df5219c |
4
.github/workflows/canary.yml
vendored
4
.github/workflows/canary.yml
vendored
@@ -21,9 +21,9 @@ env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.2"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Canary-Releases"
|
||||
RELEASE: 1
|
||||
|
||||
jobs:
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -21,7 +21,7 @@ env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.2"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
||||
RELEASE: 1
|
||||
|
||||
|
||||
11
Ryujinx.sln
11
Ryujinx.sln
@@ -80,11 +80,16 @@ EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||
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.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
@@ -94,8 +99,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
.github\workflows\release.yml = .github\workflows\release.yml
|
||||
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
|
||||
@@ -265,6 +268,10 @@ Global
|
||||
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ARMeilleure.State;
|
||||
using Humanizer;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
@@ -58,8 +59,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
_ptc = ptc;
|
||||
|
||||
_timer = new Timer(SaveInterval * 1000d);
|
||||
_timer.Elapsed += PreSave;
|
||||
_timer = new Timer(SaveInterval.Seconds());
|
||||
_timer.Elapsed += TimerElapsed;
|
||||
|
||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||
|
||||
@@ -72,6 +73,9 @@ namespace ARMeilleure.Translation.PTC
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
private void TimerElapsed(object _, ElapsedEventArgs __)
|
||||
=> new Thread(PreSave) { Name = "Ptc.DiskWriter" }.Start();
|
||||
|
||||
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
@@ -262,7 +266,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
compressedStream.SetLength(0L);
|
||||
}
|
||||
|
||||
private void PreSave(object source, ElapsedEventArgs e)
|
||||
private void PreSave()
|
||||
{
|
||||
_waitEvent.Reset();
|
||||
|
||||
@@ -428,7 +432,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
_timer.Elapsed -= PreSave;
|
||||
_timer.Elapsed -= TimerElapsed;
|
||||
_timer.Dispose();
|
||||
|
||||
Wait();
|
||||
|
||||
190
src/Ryujinx.Common/TitleIDs.cs
Normal file
190
src/Ryujinx.Common/TitleIDs.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
public static class TitleIDs
|
||||
{
|
||||
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
|
||||
{
|
||||
switch (currentBackend)
|
||||
{
|
||||
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS():
|
||||
return GraphicsBackend.Vulkan;
|
||||
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal:
|
||||
return currentBackend;
|
||||
}
|
||||
|
||||
if (!(OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture is Architecture.Arm64))
|
||||
return GraphicsBackend.Vulkan;
|
||||
|
||||
return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan;
|
||||
}
|
||||
|
||||
public static readonly string[] GreatMetalTitles =
|
||||
[
|
||||
"01006f8002326000", // Animal Crossings: New Horizons
|
||||
"01009bf0072d4000", // Captain Toad: Treasure Tracker
|
||||
"0100a5c00d162000", // Cuphead
|
||||
"010023800d64a000", // Deltarune
|
||||
"010028600EBDA000", // Mario 3D World
|
||||
"0100152000022000", // Mario Kart 8 Deluxe
|
||||
"01005CA01580E000", // Persona 5
|
||||
"01008C0016544000", // Sea of Stars
|
||||
"01006A800016E000", // Smash Ultimate
|
||||
"0100000000010000", // Super Mario Odyessy
|
||||
];
|
||||
|
||||
public static string GetDiscordGameAsset(string titleId)
|
||||
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
|
||||
|
||||
public static readonly string[] DiscordGameAssetKeys =
|
||||
[
|
||||
"010055d009f78000", // Fire Emblem: Three Houses
|
||||
"0100a12011cc8000", // Fire Emblem: Shadow Dragon
|
||||
"0100a6301214e000", // Fire Emblem Engage
|
||||
"0100f15003e64000", // Fire Emblem Warriors
|
||||
"010071f0143ea000", // Fire Emblem Warriors: Three Hopes
|
||||
|
||||
"01007e3006dda000", // Kirby Star Allies
|
||||
"01004d300c5ae000", // Kirby and the Forgotten Land
|
||||
"01006b601380e000", // Kirby's Return to Dream Land Deluxe
|
||||
"01003fb00c5a8000", // Super Kirby Clash
|
||||
"0100227010460000", // Kirby Fighters 2
|
||||
"0100a8e016236000", // Kirby's Dream Buffet
|
||||
|
||||
"01007ef00011e000", // The Legend of Zelda: Breath of the Wild
|
||||
"01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
|
||||
"01002da013484000", // The Legend of Zelda: Skyward Sword HD
|
||||
"0100f2c0115b6000", // The Legend of Zelda: Tears of the Kingdom
|
||||
"01008cf01baac000", // The Legend of Zelda: Echoes of Wisdom
|
||||
"01000b900d8b0000", // Cadence of Hyrule
|
||||
"0100ae00096ea000", // Hyrule Warriors: Definitive Edition
|
||||
"01002b00111a2000", // Hyrule Warriors: Age of Calamity
|
||||
|
||||
"010048701995e000", // Luigi's Mansion 2 HD
|
||||
"0100dca0064a6000", // Luigi's Mansion 3
|
||||
|
||||
"010093801237c000", // Metroid Dread
|
||||
"010012101468c000", // Metroid Prime Remastered
|
||||
|
||||
"0100000000010000", // SUPER MARIO ODYSSEY
|
||||
"0100ea80032ea000", // Super Mario Bros. U Deluxe
|
||||
"01009b90006dc000", // Super Mario Maker 2
|
||||
"010049900f546000", // Super Mario 3D All-Stars
|
||||
"010049900F546001", // ^ 64
|
||||
"010049900F546002", // ^ Sunshine
|
||||
"010049900F546003", // ^ Galaxy
|
||||
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
|
||||
"010015100b514000", // Super Mario Bros. Wonder
|
||||
"0100152000022000", // Mario Kart 8 Deluxe
|
||||
"010036b0034e4000", // Super Mario Party
|
||||
"01006fe013472000", // Mario Party Superstars
|
||||
"0100965017338000", // Super Mario Party Jamboree
|
||||
"01006d0017f7a000", // Mario & Luigi: Brothership
|
||||
"010067300059a000", // Mario + Rabbids: Kingdom Battle
|
||||
"0100317013770000", // Mario + Rabbids: Sparks of Hope
|
||||
"0100a3900c3e2000", // Paper Mario: The Origami King
|
||||
"0100ecd018ebe000", // Paper Mario: The Thousand-Year Door
|
||||
"0100bc0018138000", // Super Mario RPG
|
||||
"0100bde00862a000", // Mario Tennis Aces
|
||||
"0100c9c00e25c000", // Mario Golf: Super Rush
|
||||
"010019401051c000", // Mario Strikers: Battle League
|
||||
"010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
|
||||
"0100b99019412000", // Mario vs. Donkey Kong
|
||||
|
||||
"0100aa80194b0000", // Pikmin 1
|
||||
"0100d680194b2000", // Pikmin 2
|
||||
"0100f4c009322000", // Pikmin 3 Deluxe
|
||||
"0100b7c00933a000", // Pikmin 4
|
||||
|
||||
"010003f003a34000", // Pokémon: Let's Go Pikachu!
|
||||
"0100187003a36000", // Pokémon: Let's Go Eevee!
|
||||
"0100abf008968000", // Pokémon Sword
|
||||
"01008db008c2c000", // Pokémon Shield
|
||||
"0100000011d90000", // Pokémon Brilliant Diamond
|
||||
"010018e011d92000", // Pokémon Shining Pearl
|
||||
"01001f5010dfa000", // Pokémon Legends: Arceus
|
||||
"0100a3d008c5c000", // Pokémon Scarlet
|
||||
"01008f6008c5e000", // Pokémon Violet
|
||||
"0100b3f000be2000", // Pokkén Tournament DX
|
||||
"0100f4300bf2c000", // New Pokémon Snap
|
||||
|
||||
"01003bc0000a0000", // Splatoon 2 (US)
|
||||
"0100f8f0000a2000", // Splatoon 2 (EU)
|
||||
"01003c700009c000", // Splatoon 2 (JP)
|
||||
"0100c2500fc20000", // Splatoon 3
|
||||
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
||||
|
||||
"010040600c5ce000", // Tetris 99
|
||||
"0100277011f1a000", // Super Mario Bros. 35
|
||||
"0100ad9012510000", // PAC-MAN 99
|
||||
"0100ccf019c8c000", // F-ZERO 99
|
||||
"0100d870045b6000", // NES - Nintendo Switch Online
|
||||
"01008d300c50c000", // SNES - Nintendo Switch Online
|
||||
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
||||
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
||||
"0100c62011050000", // GB - Nintendo Switch Online
|
||||
"010012f017576000", // GBA - Nintendo Switch Online
|
||||
|
||||
"01000320000cc000", // 1-2 Switch
|
||||
"0100300012f2a000", // Advance Wars 1+2: Re-Boot Camp
|
||||
"01006f8002326000", // Animal Crossing: New Horizons
|
||||
"0100620012d6e000", // Big Brain Academy: Brain vs. Brain
|
||||
"010018300d006000", // BOXBOY! + BOXGIRL!
|
||||
"0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze
|
||||
"0100ed000d390000", // Dr. Kawashima's Brain Training
|
||||
"010067b017588000", // Endless Ocean Luminous
|
||||
"0100d2f00d5c0000", // Nintendo Switch Sports
|
||||
"01006b5012b32000", // Part Time UFO
|
||||
"0100704000B3A000", // Snipperclips
|
||||
"01006a800016e000", // Super Smash Bros. Ultimate
|
||||
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
|
||||
|
||||
"010076f0049a2000", // Bayonetta
|
||||
"01007960049a0000", // Bayonetta 2
|
||||
"01004a4010fea000", // Bayonetta 3
|
||||
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
||||
|
||||
"0100dcd01525a000", // Persona 3 Portable
|
||||
"010062b01525c000", // Persona 4 Golden
|
||||
"010075a016a3a000", // Persona 4 Arena Ultimax
|
||||
"01005ca01580e000", // Persona 5 Royal
|
||||
"0100801011c3e000", // Persona 5 Strikers
|
||||
"010087701b092000", // Persona 5 Tactica
|
||||
|
||||
"01009aa000faa000", // Sonic Mania
|
||||
"01004ad014bf0000", // Sonic Frontiers
|
||||
"01005ea01c0fc000", // SONIC X SHADOW GENERATIONS
|
||||
"01005ea01c0fc001", // ^
|
||||
|
||||
"010056e00853a000", // A Hat in Time
|
||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||
"0100744001588000", // Cars 3: Driven to Win
|
||||
"0100b41013c82000", // Cruis'n Blast
|
||||
"01001b300b9be000", // Diablo III: Eternal Collection
|
||||
"01008c8012920000", // Dying Light Platinum Edition
|
||||
"010073c01af34000", // LEGO Horizon Adventures
|
||||
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
||||
"0100b04011742000", // Monster Hunter Rise
|
||||
"0100853015e86000", // No Man's Sky
|
||||
"01007bb017812000", // Portal
|
||||
"0100abd01785c000", // Portal 2
|
||||
"01008e200c5c2000", // Muse Dash
|
||||
"01007820196a6000", // Red Dead Redemption
|
||||
"01002f7013224000", // Rune Factory 5
|
||||
"01008d100d43e000", // Saints Row IV
|
||||
"0100de600beee000", // Saints Row: The Third - The Full Package
|
||||
"01001180021fa000", // Shovel Knight: Specter of Torment
|
||||
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
|
||||
"0100800015926000", // Suika Game
|
||||
"0100e46006708000", // Terraria
|
||||
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
||||
"010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon-
|
||||
"010080b00ad66000", // Undertale
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Window;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum VSyncMode
|
||||
{
|
||||
Switch,
|
||||
Unbounded,
|
||||
Custom
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using SharpMetal;
|
||||
using SharpMetal.ObjectiveCCore;
|
||||
using SharpMetal.QuartzCore;
|
||||
using System.Runtime.Versioning;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Ryujinx.Graphics.Metal.SharpMetalExtensions
|
||||
{
|
||||
[SupportedOSPlatform("macOS")]
|
||||
public static class CAMetalLayerExtensions
|
||||
{
|
||||
private static readonly Selector sel_displaySyncEnabled = "displaySyncEnabled";
|
||||
private static readonly Selector sel_setDisplaySyncEnabled = "setDisplaySyncEnabled:";
|
||||
|
||||
private static readonly Selector sel_developerHUDProperties = "developerHUDProperties";
|
||||
private static readonly Selector sel_setDeveloperHUDProperties = "setDeveloperHUDProperties:";
|
||||
|
||||
public static bool IsDisplaySyncEnabled(this CAMetalLayer metalLayer)
|
||||
=> ObjectiveCRuntime.bool_objc_msgSend(metalLayer.NativePtr, sel_displaySyncEnabled);
|
||||
|
||||
public static void SetDisplaySyncEnabled(this CAMetalLayer metalLayer, bool enabled)
|
||||
=> ObjectiveCRuntime.objc_msgSend(metalLayer.NativePtr, sel_setDisplaySyncEnabled, enabled);
|
||||
|
||||
public static nint GetDeveloperHudProperties(this CAMetalLayer metalLayer)
|
||||
=> ObjectiveCRuntime.IntPtr_objc_msgSend(metalLayer.NativePtr, sel_developerHUDProperties);
|
||||
|
||||
public static void SetDeveloperHudProperties(this CAMetalLayer metalLayer, nint dictionaryPointer)
|
||||
=> ObjectiveCRuntime.objc_msgSend(metalLayer.NativePtr, sel_setDeveloperHUDProperties, dictionaryPointer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpMetal" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -5,12 +5,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpMetal" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Metal.Effects;
|
||||
using Ryujinx.Graphics.Metal.SharpMetalExtensions;
|
||||
using SharpMetal.ObjectiveCCore;
|
||||
using SharpMetal.QuartzCore;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
@@ -140,7 +144,15 @@ namespace Ryujinx.Graphics.Metal
|
||||
|
||||
public void ChangeVSyncMode(VSyncMode vSyncMode)
|
||||
{
|
||||
//_vSyncMode = vSyncMode;
|
||||
switch (vSyncMode)
|
||||
{
|
||||
case VSyncMode.Unbounded:
|
||||
_metalLayer.SetDisplaySyncEnabled(false);
|
||||
break;
|
||||
case VSyncMode.Switch:
|
||||
_metalLayer.SetDisplaySyncEnabled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAntiAliasing(AntiAliasing effect)
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||
|
||||
_current = new CounterQueueEvent(this, glType, 0);
|
||||
|
||||
_consumerThread = new Thread(EventConsumer);
|
||||
_consumerThread = new Thread(EventConsumer) { Name = "CPU.CounterQueue." + (int)type };
|
||||
_consumerThread.Start();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.OpenGL.Effects;
|
||||
using Ryujinx.Graphics.OpenGL.Effects.Smaa;
|
||||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||
|
||||
_current = new CounterQueueEvent(this, type, 0);
|
||||
|
||||
_consumerThread = new Thread(EventConsumer);
|
||||
_consumerThread = new Thread(EventConsumer) { Name = "CPU.CounterQueue." + (int)type };
|
||||
_consumerThread.Start();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Vulkan.Effects;
|
||||
using Silk.NET.Vulkan;
|
||||
using Silk.NET.Vulkan.Extensions.KHR;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
|
||||
@@ -1034,16 +1034,16 @@ namespace Ryujinx.HLE.FileSystem
|
||||
switch (fileName)
|
||||
{
|
||||
case "prod.keys":
|
||||
verified = verifyKeys(lines, genericPattern);
|
||||
verified = VerifyKeys(lines, genericPattern);
|
||||
break;
|
||||
case "title.keys":
|
||||
verified = verifyKeys(lines, titlePattern);
|
||||
verified = VerifyKeys(lines, titlePattern);
|
||||
break;
|
||||
case "console.keys":
|
||||
verified = verifyKeys(lines, genericPattern);
|
||||
verified = VerifyKeys(lines, genericPattern);
|
||||
break;
|
||||
case "dev.keys":
|
||||
verified = verifyKeys(lines, genericPattern);
|
||||
verified = VerifyKeys(lines, genericPattern);
|
||||
break;
|
||||
default:
|
||||
throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.");
|
||||
@@ -1056,20 +1056,22 @@ namespace Ryujinx.HLE.FileSystem
|
||||
{
|
||||
throw new FileNotFoundException($"Keys file not found at \"{filePath}\".");
|
||||
}
|
||||
}
|
||||
|
||||
private bool verifyKeys(string[] lines, string regex)
|
||||
{
|
||||
foreach (string line in lines)
|
||||
return;
|
||||
|
||||
bool VerifyKeys(string[] lines, string regex)
|
||||
{
|
||||
if (!Regex.IsMatch(line, regex))
|
||||
foreach (string line in lines)
|
||||
{
|
||||
return false;
|
||||
if (!Regex.IsMatch(line, regex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool AreKeysAlredyPresent(string pathToCheck)
|
||||
{
|
||||
string[] fileNames = { "prod.keys", "title.keys", "console.keys", "dev.keys" };
|
||||
|
||||
@@ -9,7 +9,6 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
is64Bits = true;
|
||||
}
|
||||
|
||||
HostThread = new Thread(ThreadStart);
|
||||
HostThread = new Thread(ThreadStart) { Name = "HLE.KThread" };
|
||||
|
||||
Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext();
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
{
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace Ryujinx.UI.App.Common
|
||||
public string Path { get; set; }
|
||||
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
|
||||
|
||||
public bool HasControlHolder => ControlHolder.ByteSpan.Length > 0;
|
||||
|
||||
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);
|
||||
|
||||
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever();
|
||||
|
||||
@@ -789,16 +789,15 @@ namespace Ryujinx.UI.App.Common
|
||||
using HttpClient httpClient = new HttpClient();
|
||||
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
|
||||
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
|
||||
var evt = new LdnGameDataReceivedEventArgs
|
||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
|
||||
{
|
||||
LdnData = ldnGameDataArray
|
||||
};
|
||||
LdnGameDataReceived?.Invoke(null, evt);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
|
||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
|
||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
|
||||
{
|
||||
LdnData = Array.Empty<LdnGameData>()
|
||||
});
|
||||
@@ -806,7 +805,7 @@ namespace Ryujinx.UI.App.Common
|
||||
}
|
||||
else
|
||||
{
|
||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs()
|
||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
|
||||
{
|
||||
LdnData = Array.Empty<LdnGameData>()
|
||||
});
|
||||
|
||||
24
src/Ryujinx.UI.Common/App/LdnGameDataList.cs
Normal file
24
src/Ryujinx.UI.Common/App/LdnGameDataList.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using LibHac.Ns;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
public class LdnGameDataArray
|
||||
{
|
||||
private readonly LdnGameData[] _ldnDatas;
|
||||
|
||||
public LdnGameDataArray(IEnumerable<LdnGameData> receivedData, ref ApplicationControlProperty acp)
|
||||
{
|
||||
LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId;
|
||||
|
||||
_ldnDatas = receivedData.Where(game =>
|
||||
communicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16))
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
public int PlayerCount => _ldnDatas.Sum(it => it.PlayerCount);
|
||||
public int GameCount => _ldnDatas.Length;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace Ryujinx.UI.Common
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = _discordGameAssetKeys.Contains(procRes.ProgramIdText) ? procRes.ProgramIdText : "game",
|
||||
LargeImageKey = TitleIDs.GetDiscordGameAsset(procRes.ProgramIdText),
|
||||
LargeImageText = TruncateToByteLength($"{appMeta.Title} (v{procRes.DisplayVersion})"),
|
||||
SmallImageKey = "ryujinx",
|
||||
SmallImageText = TruncateToByteLength(_description)
|
||||
@@ -122,151 +122,5 @@ namespace Ryujinx.UI.Common
|
||||
{
|
||||
_discordClient?.Dispose();
|
||||
}
|
||||
|
||||
private static readonly string[] _discordGameAssetKeys =
|
||||
[
|
||||
"010055d009f78000", // Fire Emblem: Three Houses
|
||||
"0100a12011cc8000", // Fire Emblem: Shadow Dragon
|
||||
"0100a6301214e000", // Fire Emblem Engage
|
||||
"0100f15003e64000", // Fire Emblem Warriors
|
||||
"010071f0143ea000", // Fire Emblem Warriors: Three Hopes
|
||||
|
||||
"01007e3006dda000", // Kirby Star Allies
|
||||
"01004d300c5ae000", // Kirby and the Forgotten Land
|
||||
"01006b601380e000", // Kirby's Return to Dream Land Deluxe
|
||||
"01003fb00c5a8000", // Super Kirby Clash
|
||||
"0100227010460000", // Kirby Fighters 2
|
||||
"0100a8e016236000", // Kirby's Dream Buffet
|
||||
|
||||
"01007ef00011e000", // The Legend of Zelda: Breath of the Wild
|
||||
"01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
|
||||
"01002da013484000", // The Legend of Zelda: Skyward Sword HD
|
||||
"0100f2c0115b6000", // The Legend of Zelda: Tears of the Kingdom
|
||||
"01008cf01baac000", // The Legend of Zelda: Echoes of Wisdom
|
||||
"01000b900d8b0000", // Cadence of Hyrule
|
||||
"0100ae00096ea000", // Hyrule Warriors: Definitive Edition
|
||||
"01002b00111a2000", // Hyrule Warriors: Age of Calamity
|
||||
|
||||
"010048701995e000", // Luigi's Mansion 2 HD
|
||||
"0100dca0064a6000", // Luigi's Mansion 3
|
||||
|
||||
"010093801237c000", // Metroid Dread
|
||||
"010012101468c000", // Metroid Prime Remastered
|
||||
|
||||
"0100000000010000", // SUPER MARIO ODYSSEY
|
||||
"0100ea80032ea000", // Super Mario Bros. U Deluxe
|
||||
"01009b90006dc000", // Super Mario Maker 2
|
||||
"010049900f546000", // Super Mario 3D All-Stars
|
||||
"010049900F546001", // ^ 64
|
||||
"010049900F546002", // ^ Sunshine
|
||||
"010049900F546003", // ^ Galaxy
|
||||
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
|
||||
"010015100b514000", // Super Mario Bros. Wonder
|
||||
"0100152000022000", // Mario Kart 8 Deluxe
|
||||
"010036b0034e4000", // Super Mario Party
|
||||
"01006fe013472000", // Mario Party Superstars
|
||||
"0100965017338000", // Super Mario Party Jamboree
|
||||
"01006d0017f7a000", // Mario & Luigi: Brothership
|
||||
"010067300059a000", // Mario + Rabbids: Kingdom Battle
|
||||
"0100317013770000", // Mario + Rabbids: Sparks of Hope
|
||||
"0100a3900c3e2000", // Paper Mario: The Origami King
|
||||
"0100ecd018ebe000", // Paper Mario: The Thousand-Year Door
|
||||
"0100bc0018138000", // Super Mario RPG
|
||||
"0100bde00862a000", // Mario Tennis Aces
|
||||
"0100c9c00e25c000", // Mario Golf: Super Rush
|
||||
"010019401051c000", // Mario Strikers: Battle League
|
||||
"010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
|
||||
"0100b99019412000", // Mario vs. Donkey Kong
|
||||
|
||||
"0100aa80194b0000", // Pikmin 1
|
||||
"0100d680194b2000", // Pikmin 2
|
||||
"0100f4c009322000", // Pikmin 3 Deluxe
|
||||
"0100b7c00933a000", // Pikmin 4
|
||||
|
||||
"010003f003a34000", // Pokémon: Let's Go Pikachu!
|
||||
"0100187003a36000", // Pokémon: Let's Go Eevee!
|
||||
"0100abf008968000", // Pokémon Sword
|
||||
"01008db008c2c000", // Pokémon Shield
|
||||
"0100000011d90000", // Pokémon Brilliant Diamond
|
||||
"010018e011d92000", // Pokémon Shining Pearl
|
||||
"01001f5010dfa000", // Pokémon Legends: Arceus
|
||||
"0100a3d008c5c000", // Pokémon Scarlet
|
||||
"01008f6008c5e000", // Pokémon Violet
|
||||
"0100b3f000be2000", // Pokkén Tournament DX
|
||||
"0100f4300bf2c000", // New Pokémon Snap
|
||||
|
||||
"01003bc0000a0000", // Splatoon 2 (US)
|
||||
"0100f8f0000a2000", // Splatoon 2 (EU)
|
||||
"01003c700009c000", // Splatoon 2 (JP)
|
||||
"0100c2500fc20000", // Splatoon 3
|
||||
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
||||
|
||||
"010040600c5ce000", // Tetris 99
|
||||
"0100277011f1a000", // Super Mario Bros. 35
|
||||
"0100ad9012510000", // PAC-MAN 99
|
||||
"0100ccf019c8c000", // F-ZERO 99
|
||||
"0100d870045b6000", // NES - Nintendo Switch Online
|
||||
"01008d300c50c000", // SNES - Nintendo Switch Online
|
||||
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
||||
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
||||
"0100c62011050000", // GB - Nintendo Switch Online
|
||||
"010012f017576000", // GBA - Nintendo Switch Online
|
||||
|
||||
"01000320000cc000", // 1-2 Switch
|
||||
"0100300012f2a000", // Advance Wars 1+2: Re-Boot Camp
|
||||
"01006f8002326000", // Animal Crossing: New Horizons
|
||||
"0100620012d6e000", // Big Brain Academy: Brain vs. Brain
|
||||
"010018300d006000", // BOXBOY! + BOXGIRL!
|
||||
"0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze
|
||||
"0100ed000d390000", // Dr. Kawashima's Brain Training
|
||||
"010067b017588000", // Endless Ocean Luminous
|
||||
"0100d2f00d5c0000", // Nintendo Switch Sports
|
||||
"01006b5012b32000", // Part Time UFO
|
||||
"0100704000B3A000", // Snipperclips
|
||||
"01006a800016e000", // Super Smash Bros. Ultimate
|
||||
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
|
||||
|
||||
"010076f0049a2000", // Bayonetta
|
||||
"01007960049a0000", // Bayonetta 2
|
||||
"01004a4010fea000", // Bayonetta 3
|
||||
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
||||
|
||||
"0100dcd01525a000", // Persona 3 Portable
|
||||
"010062b01525c000", // Persona 4 Golden
|
||||
"010075a016a3a000", // Persona 4 Arena Ultimax
|
||||
"01005ca01580e000", // Persona 5 Royal
|
||||
"0100801011c3e000", // Persona 5 Strikers
|
||||
"010087701b092000", // Persona 5 Tactica
|
||||
|
||||
"01009aa000faa000", // Sonic Mania
|
||||
"01004ad014bf0000", // Sonic Frontiers
|
||||
"01005ea01c0fc000", // SONIC X SHADOW GENERATIONS
|
||||
"01005ea01c0fc001", // ^
|
||||
|
||||
"010056e00853a000", // A Hat in Time
|
||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||
"0100744001588000", // Cars 3: Driven to Win
|
||||
"0100b41013c82000", // Cruis'n Blast
|
||||
"01001b300b9be000", // Diablo III: Eternal Collection
|
||||
"01008c8012920000", // Dying Light Platinum Edition
|
||||
"010073c01af34000", // LEGO Horizon Adventures
|
||||
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
||||
"0100b04011742000", // Monster Hunter Rise
|
||||
"0100853015e86000", // No Man's Sky
|
||||
"01007bb017812000", // Portal
|
||||
"0100abd01785c000", // Portal 2
|
||||
"01008e200c5c2000", // Muse Dash
|
||||
"01007820196a6000", // Red Dead Redemption
|
||||
"01002f7013224000", // Rune Factory 5
|
||||
"01008d100d43e000", // Saints Row IV
|
||||
"0100de600beee000", // Saints Row: The Third - The Full Package
|
||||
"01001180021fa000", // Shovel Knight: Specter of Torment
|
||||
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
|
||||
"0100800015926000", // Suika Game
|
||||
"0100e46006708000", // Terraria
|
||||
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
||||
"010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon-
|
||||
"010080b00ad66000", // Undertale
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
64
src/Ryujinx.UI.Common/Helper/AppletMetadata.cs
Normal file
64
src/Ryujinx.UI.Common/Helper/AppletMetadata.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.UI.App.Common;
|
||||
|
||||
namespace Ryujinx.UI.Common.Helper
|
||||
{
|
||||
public readonly struct AppletMetadata
|
||||
{
|
||||
private readonly ContentManager _contentManager;
|
||||
|
||||
public string Name { get; }
|
||||
public ulong ProgramId { get; }
|
||||
|
||||
public string Version { get; }
|
||||
|
||||
public AppletMetadata(ContentManager contentManager, string name, ulong programId, string version = "1.0.0")
|
||||
: this(name, programId, version)
|
||||
{
|
||||
_contentManager = contentManager;
|
||||
}
|
||||
|
||||
public AppletMetadata(string name, ulong programId, string version = "1.0.0")
|
||||
{
|
||||
Name = name;
|
||||
ProgramId = programId;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public string GetContentPath(ContentManager contentManager)
|
||||
=> (contentManager ?? _contentManager)
|
||||
.GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
public bool CanStart(ContentManager contentManager, out ApplicationData appData, out BlitStruct<ApplicationControlProperty> appControl)
|
||||
{
|
||||
contentManager ??= _contentManager;
|
||||
if (contentManager == null)
|
||||
{
|
||||
appData = null;
|
||||
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
appData = new()
|
||||
{
|
||||
Name = Name,
|
||||
Id = ProgramId,
|
||||
Path = GetContentPath(contentManager)
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(appData.Path))
|
||||
{
|
||||
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
appControl = StructHelpers.CreateCustomNacpData(Name, Version);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/Ryujinx.UI.Common/Resources/Logo_Ryujinx_AntiAlias.png
Normal file
BIN
src/Ryujinx.UI.Common/Resources/Logo_Ryujinx_AntiAlias.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 609 KiB |
@@ -33,7 +33,7 @@
|
||||
<EmbeddedResource Include="Resources\Icon_XCI.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_Amiibo.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_Ryujinx.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_Thiccjinx.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_Ryujinx_AntiAlias.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_Discord_Dark.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_Discord_Light.png" />
|
||||
<EmbeddedResource Include="Resources\Logo_GitHub_Dark.png" />
|
||||
|
||||
@@ -143,23 +143,6 @@ namespace Ryujinx.Ava
|
||||
public ulong ApplicationId { get; private set; }
|
||||
public bool ScreenshotRequested { get; set; }
|
||||
|
||||
public bool ShouldInitMetal
|
||||
{
|
||||
get
|
||||
{
|
||||
return OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 &&
|
||||
(
|
||||
(
|
||||
(
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Auto &&
|
||||
RendererHost.KnownGreatMetalTitles.ContainsIgnoreCase(ApplicationId.ToString("X16"))
|
||||
) ||
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public AppHost(
|
||||
RendererHost renderer,
|
||||
InputManager inputManager,
|
||||
@@ -328,7 +311,7 @@ namespace Ryujinx.Ava
|
||||
Device.VSyncMode = e.NewValue;
|
||||
Device.UpdateVSyncInterval();
|
||||
}
|
||||
_renderer.Window?.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)e.NewValue);
|
||||
_renderer.Window?.ChangeVSyncMode(e.NewValue);
|
||||
|
||||
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
|
||||
}
|
||||
@@ -912,27 +895,20 @@ namespace Ryujinx.Ava
|
||||
VirtualFileSystem.ReloadKeySet();
|
||||
|
||||
// Initialize Renderer.
|
||||
IRenderer renderer;
|
||||
GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend;
|
||||
GraphicsBackend backend = TitleIDs.SelectGraphicsBackend(ApplicationId.ToString("X16"), ConfigurationState.Instance.Graphics.GraphicsBackend);
|
||||
|
||||
if (ShouldInitMetal)
|
||||
IRenderer renderer = backend switch
|
||||
{
|
||||
#pragma warning disable CA1416 // This call site is reachable on all platforms
|
||||
// The condition does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem.
|
||||
renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface);
|
||||
// SelectGraphicsBackend does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem.
|
||||
GraphicsBackend.Metal => new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface),
|
||||
#pragma warning restore CA1416
|
||||
}
|
||||
else if (backend == GraphicsBackend.Vulkan || (backend == GraphicsBackend.Auto && !ShouldInitMetal))
|
||||
{
|
||||
renderer = VulkanRenderer.Create(
|
||||
GraphicsBackend.Vulkan => VulkanRenderer.Create(
|
||||
ConfigurationState.Instance.Graphics.PreferredGpu,
|
||||
(RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface,
|
||||
VulkanHelper.GetRequiredInstanceExtensions);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer = new OpenGLRenderer();
|
||||
}
|
||||
VulkanHelper.GetRequiredInstanceExtensions),
|
||||
_ => new OpenGLRenderer()
|
||||
};
|
||||
|
||||
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
|
||||
|
||||
@@ -1098,7 +1074,7 @@ namespace Ryujinx.Ava
|
||||
Device.Gpu.SetGpuThread();
|
||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||
|
||||
_renderer.Window.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)Device.VSyncMode);
|
||||
_renderer.Window.ChangeVSyncMode(Device.VSyncMode);
|
||||
|
||||
while (_isActive)
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Аплет для редагування Mii",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "Mii 小程序",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -713,7 +713,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "从bin文件扫描 Amiibo",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -833,7 +833,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "安装密匙",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -857,7 +857,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "从.KEYS文件或ZIP压缩包安装密匙",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -881,7 +881,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "从一个文件夹安装密匙",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -977,7 +977,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Обрізати XCI файли",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件瘦身",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -1217,7 +1217,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "常见问题和问题排除页面",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -1241,7 +1241,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "打开Ryujinx官方wiki的常见问题和问题排除页面",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -1265,7 +1265,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "安装与配置指南",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -1289,7 +1289,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "打开Ryujinx官方wiki的安装与配置指南",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -1313,7 +1313,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "多人游戏(LDN/LAN)指南",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -1337,7 +1337,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "打开Ryujinx官方wiki的多人游戏指南",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -2441,7 +2441,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Перевірка та Нарізка XCI Файлів",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "检查并瘦身XCI文件",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -2465,7 +2465,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Перевірка та Нарізка XCI Файлів для збереження місця на диску",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "检查并瘦身XCI文件以节约磁盘空间",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -2537,7 +2537,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Обрізано XCI Файлів '{0}'",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件瘦身中'{0}'",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -10097,7 +10097,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Скасування",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "正在取消",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -10121,7 +10121,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Закрити",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "关闭",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -11369,7 +11369,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Показати список змін",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "显示更新日志",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -11825,7 +11825,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件瘦身窗口",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12521,7 +12521,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "在{0}发现了一个无效的密匙文件",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12545,7 +12545,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Встановлення Ключів",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "安装密匙",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12569,7 +12569,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Новий файл Ключів буде встановлено",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "将会安装新密匙文件",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12593,7 +12593,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "\n\nЦе замінить собою поточні файли Ключів.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "\n\n这也许会替换掉一些当前已安装的密匙",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12617,7 +12617,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "\n\nВи хочете продовжити?",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "\n\n你想要继续吗?",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12641,7 +12641,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Встановлення Ключів...",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "安装密匙中。。。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -12665,7 +12665,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Нові ключі встановлено.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "已成功安装新密匙文件",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -13673,7 +13673,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Минулі розробники:",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "曾经的维护者:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -14657,7 +14657,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "重新同步系统时间以匹配您电脑的当前日期和时间。\n\n这个操作不会实时同步系统时间与电脑时间,时间仍然可能不同步;在这种情况下,只需再次单击此按钮即可。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -16817,7 +16817,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Часткові",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "分区",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -16841,7 +16841,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Необрізані",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "没有瘦身的",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -16865,7 +16865,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Обрізані",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "经过瘦身的",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -16889,7 +16889,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "(Невдача)",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "(失败)",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -16913,7 +16913,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Зберегти {0:n0} Мб",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "能节约 {0:n0} Mb",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -16937,7 +16937,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Збережено {0:n0} Мб",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "节约了 {0:n0} Mb",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -17129,7 +17129,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вкажіть Ваше нове ім'я Amiibo",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "输入你的 Amiibo 的新名字",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -17153,7 +17153,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Будь ласка, проскануйте Ваш Amiibo.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "请现在扫描你的 Amiibo",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18233,7 +18233,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Перевірити та Обрізати XCI файл",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "检查并瘦身XCI文件",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18257,7 +18257,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Ця функція спочатку перевірить вільний простір, а потім обрізатиме файл XCI для економії місця на диску.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "这个功能将会先检查XCI文件,再对其执行瘦身操作以节约磁盘空间。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18281,7 +18281,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Поточний розмір файла: {0:n} MB\nРозмір файлів гри: {1:n} MB\nЕкономія місця: {2:n} MB",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "当前文件大小: {0:n} MB\n游戏数据大小: {1:n} MB\n节约的磁盘空间: {2:n} MB",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18305,7 +18305,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "XCI файл не потребує обрізання. Перевірте журнали для додаткової інформації",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件不需要被瘦身。查看日志以获得更多细节。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18329,7 +18329,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "XCI файл не може бути обрізаний. Перевірте журнали для додаткової інформації",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件不能被瘦身。查看日志以获得更多细节。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18353,7 +18353,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "XCI файл Тільки для Читання і не може бути прочитаним. Перевірте журнали додаткової інформації",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件是只读的,且不可以被标记为可读取的。查看日志以获得更多细节。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18377,7 +18377,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Розмір файлу XCI змінився з моменту сканування. Перевірте, чи не записується файл, та спробуйте знову",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件在扫描后大小发生了变化。请检查文件是否未被写入,然后重试。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18401,7 +18401,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Файл XCI містить дані в зоні вільного простору, тому обрізка небезпечна",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件的空闲区域内有数据,不能安全瘦身。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18425,7 +18425,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "XCI Файл містить недійсні дані. Перевірте журнали для додаткової інформації",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件含有无效数据。查看日志以获得更多细节。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18449,7 +18449,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "XCI Файл файл не вдалося відкрити для запису. Перевірте журнали для додаткової інформації",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件不能被读写。查看日志以获得更多细节。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18473,7 +18473,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Не вдалося обрізати файл XCI",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件瘦身失败",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18497,7 +18497,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Операція перервана",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "操作已取消",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18521,7 +18521,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Операція не проводилася",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "未执行操作",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18665,7 +18665,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Обрізка XCI Файлів",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "XCI文件瘦身器",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18689,7 +18689,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "{0} з {1} тайтл(ів) обрано",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "在 {1} 中选中了 {0} 个游戏 ",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18713,7 +18713,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "{0} з {1} тайтл(ів) обрано ({2} відображається)",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "在 {1} 中选中了 {0} 个游戏 (显示了 {2} 个)",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18737,7 +18737,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Обрізка {0} тайтл(ів)...",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "{0} 个游戏瘦身中。。。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18785,7 +18785,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Невдача",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "失败",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18809,7 +18809,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Потенційна економія",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "潜在的储存空间节省",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18833,7 +18833,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Зекономлено",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "实际的储存空间节省",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18881,7 +18881,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вибрати показане",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "选定显示的",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18905,7 +18905,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Скасувати вибір показаного",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "反选显示的",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -18977,7 +18977,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Обрізка",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "瘦身",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -19337,7 +19337,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Продовжити",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "继续",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -19697,7 +19697,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "自动",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -19721,7 +19721,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "使用Vulkan。\n在ARM Mac上,当玩在其下运行良好的游戏时,使用Metal后端。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21065,7 +21065,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вимкнути хостинг P2P мережі (може збільшити затримку)",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "禁用P2P网络连接 (也许会增加延迟)",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21089,7 +21089,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вимкнути хостинг P2P мережі, піри будуть підключатися через майстер-сервер замість прямого з'єднання з вами.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "禁用P2P网络连接,对方将通过主服务器进行连接,而不是直接连接到您。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21113,7 +21113,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Мережевий пароль:",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "网络密码:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21137,7 +21137,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Ви зможете бачити лише ті ігри, які мають такий самий пароль, як і у вас.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "您只能看到与您使用相同密码的游戏房间。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21161,7 +21161,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Введіть пароль у форматі Ryujinx-<8 символів>. Ви зможете бачити лише ті ігри, які мають такий самий пароль, як і у вас.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "以Ryujinx-<8个十六进制字符>的格式输入密码。您只能看到与您使用相同密码的游戏房间。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21185,7 +21185,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "(публічний)",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "(公开的)",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21209,7 +21209,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Згенерувати випадкову",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "随机生成",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21233,7 +21233,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Генерує новий пароль, яким можна поділитися з іншими гравцями.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "生成一个新的密码,可以与其他玩家共享。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21257,7 +21257,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Очистити",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "清除",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21281,7 +21281,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Очищає поточну пароль, повертаючись до публічної мережі.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "清除当前密码,返回公共网络。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21305,7 +21305,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Невірний пароль! Має бути в форматі \"Ryujinx-<8 символів>\"",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "无效密码!密码的格式必须是\"Ryujinx-<8个十六进制字符>\"",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21329,7 +21329,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Вертикальна синхронізація (VSync):",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "垂直同步(VSync)",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21353,7 +21353,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Увімкнути користувацьку частоту оновлення (Експериментально)",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "启动自定义刷新率(实验性功能)",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21401,7 +21401,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Безмежна",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "无限制",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21425,7 +21425,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Користувацька",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "自定义刷新率",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21449,7 +21449,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Емульована вертикальна синхронізація. 'Switch' емулює частоту оновлення Switch 60 Гц. 'Безмежна' — частота оновлення не матиме обмежень.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "模拟垂直同步。“Switch”模拟了Switch的60Hz刷新率。“无限制”没有刷新率限制。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21473,7 +21473,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Емульована вертикальна синхронізація. 'Switch' емулює частоту оновлення Switch 60 Гц. 'Безмежна' — частота оновлення не матиме обмежень. 'Користувацька' емулює вказану користувацьку частоту оновлення.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "模拟垂直同步。“Switch”模拟了Switch的60Hz刷新率。“无限制”没有刷新率限制。“自定义刷新率”模拟指定的自定义刷新率。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21497,7 +21497,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Дозволяє користувачу вказати емульовану частоту оновлення. У деяких іграх це може прискорити або сповільнити логіку гри. У інших іграх це може дозволити обмежити FPS на певні кратні частоти оновлення або призвести до непередбачуваної поведінки. Це експериментальна функція, без гарантій того, як це вплине на ігровий процес. \n\nЗалиште ВИМКНЕНИМ, якщо не впевнені.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "允许用户指定模拟刷新率。在某些游戏中,这可能会加快或减慢游戏逻辑的速度。在其他游戏中,它可能允许将FPS限制在刷新率的某个倍数,或者导致不可预测的行为。这是一个实验性功能,无法保证游戏会受到怎样的影响。\n\n如果不确定,请关闭。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21521,7 +21521,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Цільове значення користувацької частоти оновлення.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "目标自定义刷新率值。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21545,7 +21545,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Користувацька частота оновлення, як відсоток від стандартної частоти оновлення Switch.",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "自定义刷新率,占正常SWitch刷新率的百分比值。",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21569,7 +21569,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Користувацька частота оновлення %:",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "自定义刷新率值 %:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21593,7 +21593,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Значення користувацька частота оновлення:",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "自定义刷新率值:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21617,7 +21617,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "间隔",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21641,7 +21641,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Перемкнути VSync режим:",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "设置 VSync 模式:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21665,7 +21665,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Підвищити користувацьку частоту оновлення",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "提高自定义刷新率:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
@@ -21689,7 +21689,7 @@
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Понизити користувацьку частоту оновлення",
|
||||
"zh_CN": "",
|
||||
"zh_CN": "降低自定义刷新率:",
|
||||
"zh_TW": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Ryujinx.Ava.Common
|
||||
var cancellationToken = new CancellationTokenSource();
|
||||
|
||||
UpdateWaitWindow waitingDialog = new(
|
||||
App.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
|
||||
RyujinxApp.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
|
||||
cancellationToken);
|
||||
|
||||
@@ -268,10 +268,9 @@ namespace Ryujinx.Ava.Common
|
||||
{
|
||||
Dispatcher.UIThread.Post(waitingDialog.Close);
|
||||
|
||||
NotificationHelper.Show(
|
||||
App.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
|
||||
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
|
||||
NotificationType.Information);
|
||||
NotificationHelper.ShowInformation(
|
||||
RyujinxApp.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
|
||||
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
@@ -17,7 +16,6 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
private const string DefaultLanguageCode = "en_US";
|
||||
|
||||
private readonly Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
|
||||
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
|
||||
private string _localeLanguageCode;
|
||||
|
||||
@@ -27,7 +25,6 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
public LocaleManager()
|
||||
{
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
|
||||
Load();
|
||||
@@ -37,9 +34,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
var localeLanguageCode = !string.IsNullOrEmpty(ConfigurationState.Instance.UI.LanguageCode.Value) ?
|
||||
ConfigurationState.Instance.UI.LanguageCode.Value : CultureInfo.CurrentCulture.Name.Replace('-', '_');
|
||||
|
||||
// Load en_US as default, if the target language translation is missing or incomplete.
|
||||
LoadDefaultLanguage();
|
||||
|
||||
LoadLanguage(localeLanguageCode);
|
||||
|
||||
// Save whatever we ended up with.
|
||||
@@ -66,26 +61,14 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If formatting failed use the default text instead.
|
||||
if (_localeDefaultStrings.TryGetValue(key, out value))
|
||||
try
|
||||
{
|
||||
return string.Format(value, dynamicValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If formatting the default text failed return the key.
|
||||
return key.ToString();
|
||||
}
|
||||
// If formatting the text failed,
|
||||
// continue to the below line & return the text without formatting.
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// If the locale doesn't contain the key return the default one.
|
||||
return _localeDefaultStrings.TryGetValue(key, out string defaultValue)
|
||||
? defaultValue
|
||||
: key.ToString(); // If the locale text doesn't exist return the key.
|
||||
|
||||
return key.ToString(); // If the locale text doesn't exist return the key.
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -109,16 +92,11 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
_dynamicValues[key] = values;
|
||||
|
||||
OnPropertyChanged("Item");
|
||||
OnPropertyChanged("Translation");
|
||||
|
||||
return this[key];
|
||||
}
|
||||
|
||||
private void LoadDefaultLanguage()
|
||||
{
|
||||
_localeDefaultStrings = LoadJsonLanguage(DefaultLanguageCode);
|
||||
}
|
||||
|
||||
public void LoadLanguage(string languageCode)
|
||||
{
|
||||
var locale = LoadJsonLanguage(languageCode);
|
||||
@@ -126,7 +104,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
if (locale == null)
|
||||
{
|
||||
_localeLanguageCode = DefaultLanguageCode;
|
||||
locale = _localeDefaultStrings;
|
||||
locale = LoadJsonLanguage(_localeLanguageCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -138,16 +116,12 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
_localeStrings[key] = val;
|
||||
}
|
||||
|
||||
OnPropertyChanged("Item");
|
||||
OnPropertyChanged("Translation");
|
||||
|
||||
LocaleChanged?.Invoke();
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
|
||||
private static LocalesJson? _localeData;
|
||||
|
||||
#nullable disable
|
||||
|
||||
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode)
|
||||
{
|
||||
@@ -158,18 +132,29 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
foreach (LocalesEntry locale in _localeData.Value.Locales)
|
||||
{
|
||||
if (locale.Translations.Count != _localeData.Value.Languages.Count)
|
||||
if (locale.Translations.Count < _localeData.Value.Languages.Count)
|
||||
{
|
||||
throw new Exception($"Locale key {{{locale.ID}}} is missing languages! Has {locale.Translations.Count} translations, expected {_localeData.Value.Languages.Count}!");
|
||||
}
|
||||
|
||||
if (locale.Translations.Count > _localeData.Value.Languages.Count)
|
||||
{
|
||||
throw new Exception($"Locale key {{{locale.ID}}} has too many languages! Has {locale.Translations.Count} translations, expected {_localeData.Value.Languages.Count}!");
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<LocaleKeys>(locale.ID, out var localeKey))
|
||||
continue;
|
||||
|
||||
localeStrings[localeKey] =
|
||||
locale.Translations.TryGetValue(languageCode, out string val) && val != string.Empty
|
||||
? val
|
||||
: locale.Translations[DefaultLanguageCode];
|
||||
var str = locale.Translations.TryGetValue(languageCode, out string val) && !string.IsNullOrEmpty(val)
|
||||
? val
|
||||
: locale.Translations[DefaultLanguageCode];
|
||||
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
throw new Exception($"Locale key '{locale.ID}' has no valid translations for desired language {languageCode}! {DefaultLanguageCode} is an empty string or null");
|
||||
}
|
||||
|
||||
localeStrings[localeKey] = str;
|
||||
}
|
||||
|
||||
return localeStrings;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Ryujinx.Ava.Common.Markup
|
||||
{
|
||||
internal abstract class BasicMarkupExtension<T> : MarkupExtension
|
||||
{
|
||||
public virtual string Name => "Item";
|
||||
public abstract string Name { get; }
|
||||
public virtual Action<object, T?>? Setter => null;
|
||||
|
||||
protected abstract T? Value { get; }
|
||||
|
||||
@@ -6,16 +6,19 @@ namespace Ryujinx.Ava.Common.Markup
|
||||
{
|
||||
internal class IconExtension(string iconString) : BasicMarkupExtension<Icon>
|
||||
{
|
||||
public override string Name => "Icon";
|
||||
protected override Icon Value => new() { Value = iconString };
|
||||
}
|
||||
|
||||
internal class SpinningIconExtension(string iconString) : BasicMarkupExtension<Icon>
|
||||
{
|
||||
public override string Name => "SIcon";
|
||||
protected override Icon Value => new() { Value = iconString, Animation = IconAnimation.Spin };
|
||||
}
|
||||
|
||||
internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension<string>
|
||||
{
|
||||
public override string Name => "Translation";
|
||||
protected override string Value => LocaleManager.Instance[key];
|
||||
|
||||
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
|
||||
public static AppBuilder BuildAvaloniaApp() =>
|
||||
AppBuilder.Configure<App>()
|
||||
AppBuilder.Configure<RyujinxApp>()
|
||||
.UsePlatformDetect()
|
||||
.With(new X11PlatformOptions
|
||||
{
|
||||
@@ -100,7 +100,7 @@ namespace Ryujinx.Ava
|
||||
// Delete backup files after updating.
|
||||
Task.Run(Updater.CleanupUpdate);
|
||||
|
||||
Console.Title = $"{App.FullAppName} Console {Version}";
|
||||
Console.Title = $"{RyujinxApp.FullAppName} Console {Version}";
|
||||
|
||||
// Hook unhandled exception and process exit events.
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
||||
@@ -225,7 +225,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static void PrintSystemInfo()
|
||||
{
|
||||
Logger.Notice.Print(LogClass.Application, $"{App.FullAppName} Version: {Version}");
|
||||
Logger.Notice.Print(LogClass.Application, $"{RyujinxApp.FullAppName} Version: {Version}");
|
||||
SystemInfo.Gather().Print();
|
||||
|
||||
var enabledLogLevels = Logger.GetEnabledLevels().ToArray();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<Application
|
||||
x:Class="Ryujinx.Ava.App"
|
||||
x:Class="Ryujinx.Ava.RyujinxApp"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sty="using:FluentAvalonia.Styling">
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Styling;
|
||||
@@ -19,7 +20,7 @@ using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
{
|
||||
public class App : Application
|
||||
public class RyujinxApp : Application
|
||||
{
|
||||
internal static string FormatTitle(LocaleKeys? windowTitleKey = null)
|
||||
=> windowTitleKey is null
|
||||
@@ -32,8 +33,8 @@ namespace Ryujinx.Ava
|
||||
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()
|
||||
.MainWindow.Cast<MainWindow>();
|
||||
|
||||
public static IClassicDesktopStyleApplicationLifetime DesktopLifetime => Current!
|
||||
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>();
|
||||
public static bool IsClipboardAvailable(out IClipboard clipboard)
|
||||
=> (clipboard = MainWindow.Clipboard) != null;
|
||||
|
||||
public static void SetTaskbarProgress(TaskBarProgressBarState state) => MainWindow.PlatformFeatures.SetTaskBarProgressBarState(state);
|
||||
public static void SetTaskbarProgressValue(ulong current, ulong total) => MainWindow.PlatformFeatures.SetTaskBarProgressBarValue(current, total);
|
||||
@@ -135,7 +136,7 @@ namespace Ryujinx.Ava
|
||||
};
|
||||
|
||||
public static ThemeVariant DetectSystemTheme() =>
|
||||
Current is App { PlatformSettings: not null } app
|
||||
Current is RyujinxApp { PlatformSettings: not null } app
|
||||
? ConvertThemeVariant(app.PlatformSettings.GetColorValues().ThemeVariant)
|
||||
: ThemeVariant.Default;
|
||||
}
|
||||
@@ -43,17 +43,19 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
if (sender is not Button { Content: TextBlock idText })
|
||||
return;
|
||||
|
||||
if (!RyujinxApp.IsClipboardAvailable(out var clipboard))
|
||||
return;
|
||||
|
||||
if (App.MainWindow.Clipboard is { } clipboard)
|
||||
{
|
||||
var appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
|
||||
if (appData is null)
|
||||
return;
|
||||
var appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
|
||||
if (appData is null)
|
||||
return;
|
||||
|
||||
await clipboard.SetTextAsync(appData.IdString);
|
||||
|
||||
await clipboard.SetTextAsync(appData.IdString);
|
||||
|
||||
NotificationHelper.Show("Copied Title ID", $"{appData.Name} ({appData.IdString})", NotificationType.Information);
|
||||
}
|
||||
NotificationHelper.ShowInformation(
|
||||
"Copied Title ID",
|
||||
$"{appData.Name} ({appData.IdString})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/Ryujinx/UI/Controls/StatusBarSeparator.cs
Normal file
19
src/Ryujinx/UI/Controls/StatusBarSeparator.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public class MiniVerticalSeparator : Border
|
||||
{
|
||||
public MiniVerticalSeparator()
|
||||
{
|
||||
Width = 2;
|
||||
Height = 12;
|
||||
Margin = new Thickness();
|
||||
BorderBrush = Brushes.Gray;
|
||||
Background = Brushes.Gray;
|
||||
BorderThickness = new Thickness(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
AvaLogger.Sink = new LoggerAdapter();
|
||||
}
|
||||
|
||||
private static RyuLogger.Log? GetLog(AvaLogLevel level)
|
||||
private static RyuLogger.Log? GetLog(AvaLogLevel level, string area)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
AvaLogLevel.Debug => RyuLogger.Debug,
|
||||
AvaLogLevel.Information => RyuLogger.Debug,
|
||||
AvaLogLevel.Warning => RyuLogger.Debug,
|
||||
AvaLogLevel.Error => RyuLogger.Error,
|
||||
AvaLogLevel.Error => area is "IME" ? RyuLogger.Debug : RyuLogger.Error,
|
||||
AvaLogLevel.Fatal => RyuLogger.Error,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
|
||||
};
|
||||
@@ -35,17 +35,17 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
public bool IsEnabled(AvaLogLevel level, string area)
|
||||
{
|
||||
return GetLog(level) != null;
|
||||
return GetLog(level, area) != null;
|
||||
}
|
||||
|
||||
public void Log(AvaLogLevel level, string area, object source, string messageTemplate)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, null));
|
||||
GetLog(level, area)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, null));
|
||||
}
|
||||
|
||||
public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, propertyValues));
|
||||
GetLog(level, area)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, propertyValues));
|
||||
}
|
||||
|
||||
private static string Format(AvaLogLevel level, string area, string template, object source, object[] v)
|
||||
|
||||
@@ -62,9 +62,46 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
_notifications.Add(new Notification(title, text, type, delay, onClick, onClose));
|
||||
}
|
||||
|
||||
public static void ShowError(string message)
|
||||
{
|
||||
Show(LocaleManager.Instance[LocaleKeys.DialogErrorTitle], $"{LocaleManager.Instance[LocaleKeys.DialogErrorMessage]}\n\n{message}", NotificationType.Error);
|
||||
}
|
||||
public static void ShowError(string message) =>
|
||||
ShowError(
|
||||
LocaleManager.Instance[LocaleKeys.DialogErrorTitle],
|
||||
$"{LocaleManager.Instance[LocaleKeys.DialogErrorMessage]}\n\n{message}"
|
||||
);
|
||||
|
||||
public static void ShowInformation(string title, string text, bool waitingExit = false, Action onClick = null, Action onClose = null) =>
|
||||
Show(
|
||||
title,
|
||||
text,
|
||||
NotificationType.Information,
|
||||
waitingExit,
|
||||
onClick,
|
||||
onClose);
|
||||
|
||||
public static void ShowSuccess(string title, string text, bool waitingExit = false, Action onClick = null, Action onClose = null) =>
|
||||
Show(
|
||||
title,
|
||||
text,
|
||||
NotificationType.Success,
|
||||
waitingExit,
|
||||
onClick,
|
||||
onClose);
|
||||
|
||||
public static void ShowWarning(string title, string text, bool waitingExit = false, Action onClick = null, Action onClose = null) =>
|
||||
Show(
|
||||
title,
|
||||
text,
|
||||
NotificationType.Warning,
|
||||
waitingExit,
|
||||
onClick,
|
||||
onClose);
|
||||
|
||||
public static void ShowError(string title, string text, bool waitingExit = false, Action onClick = null, Action onClose = null) =>
|
||||
Show(
|
||||
title,
|
||||
text,
|
||||
NotificationType.Error,
|
||||
waitingExit,
|
||||
onClick,
|
||||
onClose);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.UI.Common.Models;
|
||||
using System;
|
||||
@@ -32,11 +33,11 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
if (app.CurrentSavingsB < app.PotentialSavingsB)
|
||||
{
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCICanSaveLabel, (app.PotentialSavingsB - app.CurrentSavingsB) / _bytesPerMB);
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCICanSaveLabel, ((app.PotentialSavingsB - app.CurrentSavingsB) / _bytesPerMB).CoerceAtLeast(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCISavingLabel, app.CurrentSavingsB / _bytesPerMB);
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCISavingLabel, (app.CurrentSavingsB / _bytesPerMB).CoerceAtLeast(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Gommon;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Ncm;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
@@ -47,7 +48,7 @@ namespace Ryujinx.Ava.UI.Models
|
||||
TitleId = info.ProgramId;
|
||||
UserId = info.UserId;
|
||||
|
||||
var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.IdString.Equals(TitleIdString, StringComparison.OrdinalIgnoreCase));
|
||||
var appData = RyujinxApp.MainWindow.ViewModel.Applications.FirstOrDefault(x => x.IdString.EqualsIgnoreCase(TitleIdString));
|
||||
|
||||
InGameList = appData != null;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Gommon;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
@@ -31,15 +32,6 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public static readonly string[] KnownGreatMetalTitles =
|
||||
[
|
||||
"01006A800016E000", // Smash Ultimate
|
||||
"0100000000010000", // Super Mario Odyessy
|
||||
"01008C0016544000", // Sea of Stars
|
||||
"01005CA01580E000", // Persona 5
|
||||
"010028600EBDA000", // Mario 3D World
|
||||
];
|
||||
|
||||
public GraphicsBackend Backend =>
|
||||
EmbeddedWindow switch
|
||||
{
|
||||
@@ -53,16 +45,8 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
switch (ConfigurationState.Instance.Graphics.GraphicsBackend.Value)
|
||||
switch (TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend))
|
||||
{
|
||||
case GraphicsBackend.Auto:
|
||||
EmbeddedWindow =
|
||||
OperatingSystem.IsMacOS() &&
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.Arm64 &&
|
||||
KnownGreatMetalTitles.ContainsIgnoreCase(titleId)
|
||||
? new EmbeddedWindowMetal()
|
||||
: new EmbeddedWindowVulkan();
|
||||
break;
|
||||
case GraphicsBackend.OpenGl:
|
||||
EmbeddedWindow = new EmbeddedWindowOpenGL();
|
||||
break;
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public AboutWindowViewModel()
|
||||
{
|
||||
Version = App.FullAppName + "\n" + Program.Version;
|
||||
Version = RyujinxApp.FullAppName + "\n" + Program.Version;
|
||||
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
|
||||
|
||||
ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
|
||||
@@ -64,7 +64,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void UpdateLogoTheme(string theme)
|
||||
{
|
||||
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && App.DetectSystemTheme() == ThemeVariant.Dark);
|
||||
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark);
|
||||
|
||||
string basePath = "resm:Ryujinx.UI.Common.Resources.";
|
||||
string themeSuffix = isDarkTheme ? "Dark.png" : "Light.png";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -11,5 +12,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected void OnPropertiesChanged(params ReadOnlySpan<string> propertyNames)
|
||||
{
|
||||
foreach (var propertyName in propertyNames)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Gommon;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Ava.Common;
|
||||
@@ -109,13 +110,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered;
|
||||
private bool _canUpdate = true;
|
||||
private Cursor _cursor;
|
||||
private string _title;
|
||||
private ApplicationData _currentApplicationData;
|
||||
private readonly AutoResetEvent _rendererWaitEvent;
|
||||
private WindowState _windowState;
|
||||
private double _windowWidth;
|
||||
private double _windowHeight;
|
||||
private int _customVSyncInterval;
|
||||
private int _customVSyncIntervalPercentageProxy;
|
||||
|
||||
@@ -124,14 +120,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public ApplicationData ListSelectedApplication;
|
||||
public ApplicationData GridSelectedApplication;
|
||||
|
||||
public IEnumerable<LdnGameData> LastLdnGameData;
|
||||
|
||||
// Key is Title ID
|
||||
public SafeDictionary<string, LdnGameDataArray> LdnData = [];
|
||||
|
||||
// The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions.
|
||||
// For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left.
|
||||
// The border gets reduced to colored pixels in the 4 corners.
|
||||
public static readonly Bitmap IconBitmap =
|
||||
new(Assembly.GetAssembly(typeof(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Thiccjinx.png")!);
|
||||
new(Assembly.GetAssembly(typeof(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Ryujinx_AntiAlias.png")!);
|
||||
|
||||
public MainWindow Window { get; init; }
|
||||
|
||||
@@ -216,7 +213,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool CanUpdate
|
||||
{
|
||||
get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false);
|
||||
get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate();
|
||||
set
|
||||
{
|
||||
_canUpdate = value;
|
||||
@@ -226,12 +223,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public Cursor Cursor
|
||||
{
|
||||
get => _cursor;
|
||||
set
|
||||
{
|
||||
_cursor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
get => Window.Cursor;
|
||||
set => Window.Cursor = value;
|
||||
}
|
||||
|
||||
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
|
||||
@@ -813,35 +806,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public WindowState WindowState
|
||||
{
|
||||
get => _windowState;
|
||||
get => Window.WindowState;
|
||||
internal set
|
||||
{
|
||||
_windowState = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
Window.WindowState = value;
|
||||
}
|
||||
}
|
||||
|
||||
public double WindowWidth
|
||||
{
|
||||
get => _windowWidth;
|
||||
set
|
||||
{
|
||||
_windowWidth = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
get => Window.Width;
|
||||
set => Window.Width = value;
|
||||
}
|
||||
|
||||
public double WindowHeight
|
||||
{
|
||||
get => _windowHeight;
|
||||
set
|
||||
{
|
||||
_windowHeight = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
get => Window.Height;
|
||||
set => Window.Height = value;
|
||||
}
|
||||
|
||||
public bool IsGrid => Glyph == Glyph.Grid;
|
||||
@@ -889,11 +870,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
get => Window.Title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
|
||||
Window.Title = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
@@ -2051,7 +2032,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Title = App.FormatTitle();
|
||||
Title = RyujinxApp.FormatTitle();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -71,8 +71,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
_resolutionScale = value;
|
||||
|
||||
OnPropertyChanged(nameof(CustomResolutionScale));
|
||||
OnPropertyChanged(nameof(IsCustomResolutionScaleActive));
|
||||
OnPropertiesChanged(nameof(CustomResolutionScale), nameof(IsCustomResolutionScaleActive));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +180,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
int newInterval = (int)((value / 100f) * 60);
|
||||
_customVSyncInterval = newInterval;
|
||||
_customVSyncIntervalPercentageProxy = value;
|
||||
OnPropertyChanged((nameof(CustomVSyncInterval)));
|
||||
OnPropertyChanged((nameof(CustomVSyncIntervalPercentageText)));
|
||||
OnPropertiesChanged(
|
||||
nameof(CustomVSyncInterval),
|
||||
nameof(CustomVSyncIntervalPercentageText));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
get
|
||||
{
|
||||
string text = CustomVSyncIntervalPercentageProxy.ToString() + "%";
|
||||
string text = CustomVSyncIntervalPercentageProxy + "%";
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -221,8 +221,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_customVSyncInterval = value;
|
||||
int newPercent = (int)((value / 60f) * 100);
|
||||
_customVSyncIntervalPercentageProxy = newPercent;
|
||||
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy));
|
||||
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText));
|
||||
OnPropertiesChanged(
|
||||
nameof(CustomVSyncIntervalPercentageProxy),
|
||||
nameof(CustomVSyncIntervalPercentageText));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
@@ -749,7 +750,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
MainWindow.UpdateGraphicsConfig();
|
||||
MainWindow.MainWindowViewModel.VSyncModeSettingChanged();
|
||||
RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
|
||||
|
||||
SaveSettingsEvent?.Invoke();
|
||||
|
||||
|
||||
@@ -91,39 +91,42 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void SortingChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(IsSortedByName));
|
||||
OnPropertyChanged(nameof(IsSortedBySaved));
|
||||
OnPropertyChanged(nameof(SortingAscending));
|
||||
OnPropertyChanged(nameof(SortingField));
|
||||
OnPropertyChanged(nameof(SortingFieldName));
|
||||
OnPropertiesChanged(
|
||||
nameof(IsSortedByName),
|
||||
nameof(IsSortedBySaved),
|
||||
nameof(SortingAscending),
|
||||
nameof(SortingField),
|
||||
nameof(SortingFieldName));
|
||||
|
||||
SortAndFilter();
|
||||
}
|
||||
|
||||
private void DisplayedChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(DisplayedXCIFiles));
|
||||
OnPropertyChanged(nameof(SelectedDisplayedXCIFiles));
|
||||
OnPropertiesChanged(nameof(Status), nameof(DisplayedXCIFiles), nameof(SelectedDisplayedXCIFiles));
|
||||
}
|
||||
|
||||
private void ApplicationsChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(AllXCIFiles));
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(PotentialSavings));
|
||||
OnPropertyChanged(nameof(ActualSavings));
|
||||
OnPropertyChanged(nameof(CanTrim));
|
||||
OnPropertyChanged(nameof(CanUntrim));
|
||||
OnPropertiesChanged(
|
||||
nameof(AllXCIFiles),
|
||||
nameof(Status),
|
||||
nameof(PotentialSavings),
|
||||
nameof(ActualSavings),
|
||||
nameof(CanTrim),
|
||||
nameof(CanUntrim));
|
||||
|
||||
DisplayedChanged();
|
||||
SortAndFilter();
|
||||
}
|
||||
|
||||
private void SelectionChanged(bool displayedChanged = true)
|
||||
{
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(CanTrim));
|
||||
OnPropertyChanged(nameof(CanUntrim));
|
||||
OnPropertyChanged(nameof(SelectedXCIFiles));
|
||||
OnPropertiesChanged(
|
||||
nameof(Status),
|
||||
nameof(CanTrim),
|
||||
nameof(CanUntrim),
|
||||
nameof(SelectedXCIFiles));
|
||||
|
||||
if (displayedChanged)
|
||||
OnPropertyChanged(nameof(SelectedDisplayedXCIFiles));
|
||||
@@ -131,11 +134,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void ProcessingChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(Processing));
|
||||
OnPropertyChanged(nameof(Cancel));
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(CanTrim));
|
||||
OnPropertyChanged(nameof(CanUntrim));
|
||||
OnPropertiesChanged(
|
||||
nameof(Processing),
|
||||
nameof(Cancel),
|
||||
nameof(Status),
|
||||
nameof(CanTrim),
|
||||
nameof(CanUntrim));
|
||||
}
|
||||
|
||||
private IEnumerable<XCITrimmerFileModel> GetSelectedDisplayedXCIFiles()
|
||||
@@ -360,7 +364,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
value = _processingApplication.Value with { PercentageProgress = null };
|
||||
|
||||
if (value.HasValue)
|
||||
_displayedXCIFiles.ReplaceWith(value.Value);
|
||||
_displayedXCIFiles.ReplaceWith(value);
|
||||
|
||||
_processingApplication = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
Margin="7, 0"
|
||||
Height="25"
|
||||
Width="25"
|
||||
ToolTip.Tip="{Binding Title}"
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Thiccjinx.png?assembly=Ryujinx.UI.Common" />
|
||||
ToolTip.Tip="{Binding Title}" />
|
||||
<Menu
|
||||
Name="Menu"
|
||||
Height="32"
|
||||
|
||||
@@ -3,8 +3,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using Gommon;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
@@ -12,8 +10,6 @@ using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
@@ -33,6 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
InitializeComponent();
|
||||
|
||||
RyuLogo.IsVisible = !ConfigurationState.Instance.ShowTitleBar;
|
||||
RyuLogo.Source = MainWindowViewModel.IconBitmap;
|
||||
|
||||
ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
|
||||
ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();
|
||||
@@ -124,26 +121,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
ViewModel.LoadConfigurableHotKeys();
|
||||
}
|
||||
|
||||
public static readonly AppletMetadata MiiApplet = new("miiEdit", 0x0100000000001009);
|
||||
|
||||
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
const string AppletName = "miiEdit";
|
||||
const ulong AppletProgramId = 0x0100000000001009;
|
||||
const string AppletVersion = "1.0.0";
|
||||
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(AppletProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (!string.IsNullOrEmpty(contentPath))
|
||||
if (MiiApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData))
|
||||
{
|
||||
ApplicationData applicationData = new()
|
||||
{
|
||||
Name = AppletName,
|
||||
Id = AppletProgramId,
|
||||
Path = contentPath
|
||||
};
|
||||
|
||||
var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion);
|
||||
|
||||
await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,14 +132,7 @@
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Name="DockedStatus"
|
||||
Margin="5,0,5,0"
|
||||
@@ -149,14 +142,7 @@
|
||||
PointerReleased="DockedStatus_PointerReleased"
|
||||
Text="{Binding DockedStatusText}"
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<SplitButton
|
||||
Name="AspectRatioStatus"
|
||||
Padding="5,0,5,0"
|
||||
@@ -203,14 +189,7 @@
|
||||
</MenuFlyout>
|
||||
</SplitButton.Flyout>
|
||||
</SplitButton>
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<ToggleSplitButton
|
||||
Name="VolumeStatus"
|
||||
Padding="5,0,5,0"
|
||||
@@ -254,14 +233,7 @@
|
||||
</Flyout>
|
||||
</ToggleSplitButton.Flyout>
|
||||
</ToggleSplitButton>
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -269,14 +241,7 @@
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding GameStatusText}"
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -298,13 +263,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowShaderCompilationHint}"
|
||||
Text="{Binding ShaderCountText}" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding ShowShaderCompilationHint}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding ShowShaderCompilationHint}" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -312,14 +271,7 @@
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Text="{Binding BackendText}"
|
||||
TextAlignment="Start" />
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
Background="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding !ShowLoadProgress}" />
|
||||
<TextBlock
|
||||
Margin="5,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -334,13 +286,7 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowFirmwareStatus}"
|
||||
Orientation="Horizontal">
|
||||
<Border
|
||||
Width="2"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding IsGameRunning}" />
|
||||
<controls:MiniVerticalSeparator IsVisible="{Binding IsGameRunning}" />
|
||||
<TextBlock
|
||||
Name="FirmwareStatus"
|
||||
Margin="5, 0, 0, 0"
|
||||
|
||||
@@ -62,12 +62,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
// Change the volume by 5% at a time
|
||||
float newValue = Window.ViewModel.Volume + (float)e.Delta.Y * 0.05f;
|
||||
|
||||
Window.ViewModel.Volume = newValue switch
|
||||
{
|
||||
< 0 => 0,
|
||||
> 1 => 1,
|
||||
_ => newValue,
|
||||
};
|
||||
Window.ViewModel.Volume = Math.Clamp(newValue, 0, 1);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Title = App.FormatTitle(LocaleKeys.Amiibo);
|
||||
Title = RyujinxApp.FormatTitle(LocaleKeys.Amiibo);
|
||||
}
|
||||
|
||||
public AmiiboWindow()
|
||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Title = App.FormatTitle(LocaleKeys.Amiibo);
|
||||
Title = RyujinxApp.FormatTitle(LocaleKeys.Amiibo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Title = App.FormatTitle(LocaleKeys.CheatWindowTitle);
|
||||
Title = RyujinxApp.FormatTitle(LocaleKeys.CheatWindowTitle);
|
||||
}
|
||||
|
||||
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
|
||||
@@ -93,7 +93,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
DataContext = this;
|
||||
|
||||
Title = App.FormatTitle(LocaleKeys.CheatWindowTitle);
|
||||
Title = RyujinxApp.FormatTitle(LocaleKeys.CheatWindowTitle);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
|
||||
@@ -9,11 +9,6 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main"
|
||||
Cursor="{Binding Cursor}"
|
||||
Title="{Binding Title}"
|
||||
WindowState="{Binding WindowState}"
|
||||
Width="{Binding WindowWidth}"
|
||||
Height="{Binding WindowHeight}"
|
||||
MinWidth="800"
|
||||
MinHeight="500"
|
||||
d:DesignHeight="720"
|
||||
|
||||
@@ -39,8 +39,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class MainWindow : StyleableAppWindow
|
||||
{
|
||||
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
|
||||
|
||||
public MainWindowViewModel ViewModel { get; }
|
||||
|
||||
internal readonly AvaHostUIHandler UiHandler;
|
||||
@@ -76,7 +74,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
DataContext = ViewModel = MainWindowViewModel = new MainWindowViewModel
|
||||
DataContext = ViewModel = new MainWindowViewModel
|
||||
{
|
||||
Window = this
|
||||
};
|
||||
@@ -86,17 +84,19 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
UiHandler = new AvaHostUIHandler(this);
|
||||
|
||||
ViewModel.Title = App.FormatTitle();
|
||||
ViewModel.Title = RyujinxApp.FormatTitle();
|
||||
|
||||
TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowTitleBar;
|
||||
TitleBar.TitleBarHitTestType = (ConfigurationState.Instance.ShowTitleBar) ? TitleBarHitTestType.Simple : TitleBarHitTestType.Complex;
|
||||
|
||||
// Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024)
|
||||
TitleBarHeight = (ConfigurationState.Instance.ShowTitleBar ? TitleBar.Height : 0);
|
||||
|
||||
// NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point.
|
||||
StatusBarHeight = StatusBarView.StatusBar.MinHeight;
|
||||
MenuBarHeight = MenuBar.MinHeight;
|
||||
|
||||
TitleBar.Height = MenuBarHeight;
|
||||
|
||||
// Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024)
|
||||
TitleBarHeight = (ConfigurationState.Instance.ShowTitleBar ? TitleBar.Height : 0);
|
||||
|
||||
ApplicationList.DataContext = DataContext;
|
||||
ApplicationGrid.DataContext = DataContext;
|
||||
@@ -117,7 +117,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
/// </summary>
|
||||
private static void OnPlatformColorValuesChanged(object sender, PlatformColorValues e)
|
||||
{
|
||||
if (Application.Current is App app)
|
||||
if (Application.Current is RyujinxApp app)
|
||||
app.ApplyConfiguredTheme(ConfigurationState.Instance.UI.BaseStyle);
|
||||
}
|
||||
|
||||
@@ -167,24 +167,28 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var ldnGameDataArray = e.LdnData;
|
||||
ViewModel.LastLdnGameData = ldnGameDataArray;
|
||||
var ldnGameDataArray = e.LdnData.ToList();
|
||||
ViewModel.LdnData.Clear();
|
||||
foreach (var application in ViewModel.Applications)
|
||||
{
|
||||
ViewModel.LdnData[application.IdString] = new LdnGameDataArray(
|
||||
ldnGameDataArray,
|
||||
ref application.ControlHolder.Value
|
||||
);
|
||||
|
||||
UpdateApplicationWithLdnData(application);
|
||||
}
|
||||
|
||||
ViewModel.RefreshView();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateApplicationWithLdnData(ApplicationData application)
|
||||
{
|
||||
if (application.ControlHolder.ByteSpan.Length > 0 && ViewModel.LastLdnGameData != null)
|
||||
if (application.HasControlHolder && ViewModel.LdnData.TryGetValue(application.IdString, out var ldnGameDatas))
|
||||
{
|
||||
IEnumerable<LdnGameData> ldnGameData = ViewModel.LastLdnGameData.Where(game => application.ControlHolder.Value.LocalCommunicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16)));
|
||||
|
||||
application.PlayerCount = ldnGameData.Sum(game => game.PlayerCount);
|
||||
application.GameCount = ldnGameData.Count();
|
||||
application.PlayerCount = ldnGameDatas.PlayerCount;
|
||||
application.GameCount = ldnGameDatas.GameCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager)
|
||||
{
|
||||
Title = App.FormatTitle(LocaleKeys.Settings);
|
||||
Title = RyujinxApp.FormatTitle(LocaleKeys.Settings);
|
||||
|
||||
DataContext = ViewModel = new SettingsViewModel(virtualFileSystem, contentManager);
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
if (!Version.TryParse(Program.Version, out Version currentVersion))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to convert the current {App.FullAppName} version!");
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to convert the current {RyujinxApp.FullAppName} version!");
|
||||
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||
@@ -159,7 +159,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
if (!Version.TryParse(_buildVer, out Version newVersion))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to convert the received {App.FullAppName} version from GitHub!");
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to convert the received {RyujinxApp.FullAppName} version from GitHub!");
|
||||
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||
@@ -266,7 +266,7 @@ namespace Ryujinx.Ava
|
||||
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
|
||||
ShowProgressBar = true,
|
||||
XamlRoot = App.MainWindow,
|
||||
XamlRoot = RyujinxApp.MainWindow,
|
||||
};
|
||||
|
||||
taskDialog.Opened += (s, e) =>
|
||||
@@ -490,7 +490,7 @@ namespace Ryujinx.Ava
|
||||
bytesWritten += readSize;
|
||||
|
||||
taskDialog.SetProgressBarState(GetPercentage(bytesWritten, totalBytes), TaskDialogProgressState.Normal);
|
||||
App.SetTaskbarProgressValue(bytesWritten, totalBytes);
|
||||
RyujinxApp.SetTaskbarProgressValue(bytesWritten, totalBytes);
|
||||
|
||||
updateFileStream.Write(buffer, 0, readSize);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user