mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-12-12 07:36:59 +00:00
Initial work on a setup wizard
Setup wizard abstraction & architecture from TKMM
This commit is contained in:
parent
52700f71dc
commit
aee46e16cd
19 changed files with 743 additions and 10 deletions
|
|
@ -24841,6 +24841,206 @@
|
|||
"zh_CN": "如果您在设置中有某些 LDN 口令则可加入此游戏。",
|
||||
"zh_TW": "你只能加入與 LDN 網路密碼片語 (passphrase) 設定相同的遊戲。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardActionBack",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Back",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardActionNext",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Next",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardFirstPageTitle",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Welcome to Ryubing!",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardFirstPageContent",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Ryubing is a fork of the discontinued Nintendo Switch emulator, Ryujinx.\n\nThis setup wizard will guide you through the necessary steps needed for Ryubing to play your Switch games on PC.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardFirstPageAction",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Start Setup",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardKeysPageTitle",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Key Files",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardKeysPageDescription",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Please select the folder containing your prod/title .keys files:",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SetupWizardKeysPageFolderPopupTitle",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Please select the folder containing your prod/title .keys files",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,6 +219,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
||||
}
|
||||
|
||||
public bool HasKeySet { get; private set; }
|
||||
|
||||
public void ReloadKeySet()
|
||||
{
|
||||
KeySet ??= KeySet.CreateDefaultKeySet();
|
||||
|
|
@ -235,6 +237,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
LoadSetAtPath(AppDataManager.KeysDirPath);
|
||||
|
||||
HasKeySet = (prodKeyFile != null && titleKeyFile != null) || prodKeyFile != null;
|
||||
|
||||
void LoadSetAtPath(string basePath)
|
||||
{
|
||||
string localProdKeyFile = Path.Combine(basePath, "prod.keys");
|
||||
|
|
@ -263,7 +267,12 @@ namespace Ryujinx.HLE.FileSystem
|
|||
}
|
||||
}
|
||||
|
||||
ExternalKeyReader.ReadKeyFile(KeySet, prodKeyFile, devKeyFile, titleKeyFile, consoleKeyFile, null);
|
||||
ExternalKeyReader.ReadKeyFile(
|
||||
KeySet,
|
||||
prodKeyFile,
|
||||
devKeyFile,
|
||||
titleKeyFile,
|
||||
consoleKeyFile);
|
||||
}
|
||||
|
||||
public void ImportTickets(IFileSystem fs)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ namespace Ryujinx.Ava
|
|||
{
|
||||
internal static class Program
|
||||
{
|
||||
public static bool IsFirstStart { get; set; }
|
||||
|
||||
public static double WindowScaleFactor { get; set; }
|
||||
public static double DesktopScaleFactor { get; set; } = 1.0;
|
||||
public static string Version { get; private set; }
|
||||
|
|
@ -221,7 +223,6 @@ namespace Ryujinx.Ava
|
|||
|
||||
public static void ReloadConfig(bool isRunGameWithCustomConfig = false)
|
||||
{
|
||||
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||
|
||||
|
|
@ -247,6 +248,7 @@ namespace Ryujinx.Ava
|
|||
|
||||
ConfigurationState.Instance.LoadDefault();
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
||||
IsFirstStart = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
31
src/Ryujinx/Systems/SetupWizard/BaseSetupWizard.cs
Normal file
31
src/Ryujinx/Systems/SetupWizard/BaseSetupWizard.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using Avalonia.Controls.Presenters;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Systems.SetupWizard;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Systems.SetupWizard
|
||||
{
|
||||
public abstract class BaseSetupWizard(ContentPresenter presenter)
|
||||
{
|
||||
/// <summary>
|
||||
/// Define the logic and flow of this <see cref="BaseSetupWizard"/>.
|
||||
/// </summary>
|
||||
public abstract ValueTask Start();
|
||||
|
||||
protected ValueTask<bool> FirstPage()
|
||||
{
|
||||
SetupWizardPageBuilder builder = new(presenter, isFirstPage: true);
|
||||
|
||||
return builder
|
||||
.WithTitle(LocaleKeys.SetupWizardFirstPageTitle)
|
||||
.WithContent(LocaleKeys.SetupWizardFirstPageContent)
|
||||
.WithActionContent(LocaleKeys.SetupWizardFirstPageAction)
|
||||
.Show();
|
||||
}
|
||||
|
||||
protected SetupWizardPageBuilder NextPage()
|
||||
{
|
||||
return new SetupWizardPageBuilder(presenter);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/Ryujinx/Systems/SetupWizard/README.md
Normal file
3
src/Ryujinx/Systems/SetupWizard/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Ryubing Setup Wizard
|
||||
|
||||
Directly modified from the code found [here](https://github.com/TKMM-Team/Tkmm/tree/master/src/Tkmm/Wizard).
|
||||
62
src/Ryujinx/Systems/SetupWizard/SetupWizardPage.cs
Normal file
62
src/Ryujinx/Systems/SetupWizard/SetupWizardPage.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using Avalonia.Controls.Presenters;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Systems.SetupWizard;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Systems.SetupWizard
|
||||
{
|
||||
public partial class SetupWizardPage(bool isFirstPage = false) : BaseModel
|
||||
{
|
||||
protected bool? _result;
|
||||
protected readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
public bool IsFirstPage { get; } = isFirstPage;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _title;
|
||||
|
||||
[ObservableProperty]
|
||||
private object? _content;
|
||||
|
||||
[ObservableProperty]
|
||||
private object? _helpContent;
|
||||
|
||||
[ObservableProperty]
|
||||
private object? _actionContent = LocaleManager.Instance[LocaleKeys.SetupWizardActionNext];
|
||||
|
||||
[RelayCommand]
|
||||
private void MoveBack()
|
||||
{
|
||||
_result = false;
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void MoveNext()
|
||||
{
|
||||
_result = true;
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
public async ValueTask<bool> Show(ContentPresenter presenter)
|
||||
{
|
||||
presenter.Content = new SetupWizardPageView
|
||||
{
|
||||
DataContext = this,
|
||||
};
|
||||
|
||||
try {
|
||||
await Task.Delay(-1, _cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException) {
|
||||
return _result ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/Ryujinx/Systems/SetupWizard/SetupWizardPageBuilder.cs
Normal file
69
src/Ryujinx/Systems/SetupWizard/SetupWizardPageBuilder.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.SetupWizard;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Systems.SetupWizard
|
||||
{
|
||||
public class SetupWizardPageBuilder(ContentPresenter presenter, bool isFirstPage = false)
|
||||
{
|
||||
private readonly SetupWizardPage _page = new(isFirstPage);
|
||||
|
||||
public SetupWizardPage Build()
|
||||
{
|
||||
return _page;
|
||||
}
|
||||
|
||||
public SetupWizardPageBuilder WithTitle(LocaleKeys title) => WithTitle(LocaleManager.Instance[title]);
|
||||
|
||||
public SetupWizardPageBuilder WithTitle(string title)
|
||||
{
|
||||
_page.Title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SetupWizardPageBuilder WithContent(LocaleKeys content) => WithContent(LocaleManager.Instance[content]);
|
||||
|
||||
public SetupWizardPageBuilder WithContent(object? content)
|
||||
{
|
||||
if (content is StyledElement { Parent: ContentControl parent }) {
|
||||
parent.Content = null;
|
||||
}
|
||||
|
||||
_page.Content = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SetupWizardPageBuilder WithHelpContent(LocaleKeys content) => WithHelpContent(LocaleManager.Instance[content]);
|
||||
|
||||
public SetupWizardPageBuilder WithHelpContent(object? content)
|
||||
{
|
||||
_page.HelpContent = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SetupWizardPageBuilder WithContent<TControl>(object? context = null) where TControl : Control, new()
|
||||
{
|
||||
_page.Content = new TControl {
|
||||
DataContext = context
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SetupWizardPageBuilder WithActionContent(LocaleKeys content) => WithActionContent(LocaleManager.Instance[content]);
|
||||
|
||||
public SetupWizardPageBuilder WithActionContent(object? content)
|
||||
{
|
||||
_page.ActionContent = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValueTask<bool> Show()
|
||||
{
|
||||
return _page.Show(presenter);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/Ryujinx/Systems/SetupWizard/SetupWizardPageView.axaml
Normal file
77
src/Ryujinx/Systems/SetupWizard/SetupWizardPageView.axaml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:fa="using:Projektanker.Icons.Avalonia"
|
||||
xmlns:wiz="using:Ryujinx.Ava.Systems.SetupWizard"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="wiz:SetupWizardPage"
|
||||
x:Class="Ryujinx.Systems.SetupWizard.SetupWizardPageView">
|
||||
<Grid RowDefinitions="*,Auto" Margin="60">
|
||||
<ScrollViewer>
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<TextBlock Grid.Row="0"
|
||||
TextWrapping="WrapWithOverflow"
|
||||
FontFamily="{StaticResource HyliaGlyph}"
|
||||
FontSize="46"
|
||||
Text="{Binding Title}" />
|
||||
|
||||
<ContentPresenter Grid.Row="1"
|
||||
Content="{Binding}"
|
||||
IsVisible="{Binding !#InfoToggle.IsChecked}"
|
||||
TextWrapping="WrapWithOverflow"
|
||||
Margin="0,15,0,0">
|
||||
<ContentPresenter.DataTemplates>
|
||||
<DataTemplate DataType="{x:Type wiz:SetupWizardPage}">
|
||||
<ContentControl Content="{Binding Content}" VerticalAlignment="Stretch"/>
|
||||
</DataTemplate>
|
||||
</ContentPresenter.DataTemplates>
|
||||
</ContentPresenter>
|
||||
|
||||
<Grid Grid.Row="2"
|
||||
ColumnDefinitions="Auto,*"
|
||||
IsVisible="{Binding #InfoToggle.IsChecked}">
|
||||
<StackPanel Spacing="5" VerticalAlignment="Top">
|
||||
<HyperlinkButton NavigateUri="https://discord.gg/PEuzjrFXUA" Content="Join Discord" />
|
||||
</StackPanel>
|
||||
<ContentPresenter Content="{Binding}"
|
||||
Grid.Column="1"
|
||||
Margin="20,0,0,0"
|
||||
TextWrapping="WrapWithOverflow">
|
||||
<ContentPresenter.DataTemplates>
|
||||
<DataTemplate DataType="{x:Type wiz:SetupWizardPage}">
|
||||
<ContentControl Content="{Binding HelpContent}" />
|
||||
</DataTemplate>
|
||||
</ContentPresenter.DataTemplates>
|
||||
</ContentPresenter>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid ColumnDefinitions="Auto,Auto,*" Grid.Row="1">
|
||||
<ToggleButton Name="InfoToggle"
|
||||
Padding="6">
|
||||
<fa:Icon Value="fa-solid fa-circle-info" />
|
||||
</ToggleButton>
|
||||
|
||||
<Button IsVisible="{Binding !IsFirstPage}"
|
||||
Grid.Column="1"
|
||||
Content="{ext:Locale SetupWizardActionBack}"
|
||||
Margin="10,0,0,0"
|
||||
Command="{Binding MoveBackCommand}" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
Grid.Column="2">
|
||||
<Button Content="{Binding ActionContent}"
|
||||
Command="{Binding MoveNextCommand}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
17
src/Ryujinx/Systems/SetupWizard/SetupWizardPageView.axaml.cs
Normal file
17
src/Ryujinx/Systems/SetupWizard/SetupWizardPageView.axaml.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.Systems.SetupWizard;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
|
||||
namespace Ryujinx.Systems.SetupWizard
|
||||
{
|
||||
public partial class SetupWizardPageView : RyujinxControl<SetupWizardPage>
|
||||
{
|
||||
public SetupWizardPageView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ using Ryujinx.Ava.UI.Windows;
|
|||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
|
|
|
|||
22
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml
Normal file
22
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:pages="clr-namespace:Ryujinx.UI.SetupWizard.Pages"
|
||||
xmlns:markup="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.UI.SetupWizard.Pages.SetupKeysPage"
|
||||
x:DataType="pages:SetupKeysPageViewModel">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{markup:Locale SetupWizardKeysPageDescription}" Margin="0,0,0,10"/>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Name="KeysFolderPathField" Text="{Binding KeysFolderPath}" IsReadOnly="True" />
|
||||
<Button Grid.Column="1"
|
||||
Content="..."
|
||||
Command="{Binding BrowseCommand}"
|
||||
CommandParameter="{Binding #KeysFolderPathField}"
|
||||
Margin="5,0,0,0"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
13
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs
Normal file
13
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using Ryujinx.Ava.UI.Controls;
|
||||
|
||||
namespace Ryujinx.UI.SetupWizard.Pages
|
||||
{
|
||||
public partial class SetupKeysPage : RyujinxControl<SetupKeysPageViewModel>
|
||||
{
|
||||
public SetupKeysPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs
Normal file
34
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.UI.SetupWizard.Pages
|
||||
{
|
||||
public partial class SetupKeysPageViewModel : BaseModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial string? KeysFolderPath { get; set; }
|
||||
|
||||
[RelayCommand]
|
||||
private static async Task Browse(TextBox tb)
|
||||
{
|
||||
var result = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions {
|
||||
Title = LocaleManager.Instance[LocaleKeys.SetupWizardKeysPageFolderPopupTitle],
|
||||
AllowMultiple = false
|
||||
}) switch {
|
||||
[var target] => target.TryGetLocalPath(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
tb.Text = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs
Normal file
78
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using Avalonia.Controls.Presenters;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.Systems.SetupWizard;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using Ryujinx.UI.SetupWizard.Pages;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Logger = Ryujinx.Common.Logging.Logger;
|
||||
|
||||
namespace Ryujinx.Ava.UI.SetupWizard
|
||||
{
|
||||
public class RyujinxSetupWizard(ContentPresenter presenter, MainWindowViewModel mwvm, Action onClose) : BaseSetupWizard(presenter)
|
||||
{
|
||||
private bool _configWasModified = false;
|
||||
|
||||
public bool HasFirmware => mwvm.ContentManager.GetCurrentFirmwareVersion() != null;
|
||||
|
||||
public override async ValueTask Start()
|
||||
{
|
||||
RyujinxSetupWizardWindow.IsUsingSetupWizard = true;
|
||||
Start:
|
||||
await FirstPage();
|
||||
|
||||
Keys:
|
||||
if (!mwvm.VirtualFileSystem.HasKeySet)
|
||||
{
|
||||
Retry:
|
||||
SetupKeysPageViewModel kpvm = new();
|
||||
bool result = await NextPage()
|
||||
.WithTitle(LocaleKeys.SetupWizardKeysPageTitle)
|
||||
.WithContent<SetupKeysPage>(kpvm)
|
||||
.Show();
|
||||
|
||||
if (!result)
|
||||
goto Start;
|
||||
|
||||
if (!Directory.Exists(kpvm.KeysFolderPath))
|
||||
goto Retry;
|
||||
|
||||
await mwvm.HandleKeysInstallation(kpvm.KeysFolderPath);
|
||||
}
|
||||
|
||||
Firmware:
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
// i know its always false thats the fucking point, its not done
|
||||
if (!HasFirmware && false)
|
||||
{
|
||||
if (!mwvm.VirtualFileSystem.HasKeySet)
|
||||
goto Keys;
|
||||
|
||||
Retry:
|
||||
SetupKeysPageViewModel kpvm = new();
|
||||
bool result = await NextPage()
|
||||
.WithTitle(LocaleKeys.SetupWizardKeysPageTitle)
|
||||
.WithContent<SetupKeysPage>(kpvm)
|
||||
.Show();
|
||||
|
||||
if (!result)
|
||||
goto Keys;
|
||||
|
||||
if (!Directory.Exists(kpvm.KeysFolderPath))
|
||||
goto Retry;
|
||||
|
||||
await mwvm.HandleKeysInstallation(kpvm.KeysFolderPath);
|
||||
}
|
||||
|
||||
Return:
|
||||
onClose();
|
||||
|
||||
if (_configWasModified)
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.GlobalConfigurationPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml
Normal file
20
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<windows:StyleableAppWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:markup="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:windows="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:setupWizard="clr-namespace:Ryujinx.Ava.UI.SetupWizard"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
CanResize="False"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.UI.SetupWizard.RyujinxSetupWizardWindow"
|
||||
x:DataType="setupWizard:RyujinxSetupWizard"
|
||||
Title="{markup:Locale SetupWizardFirstPageTitle}">
|
||||
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Name="FlushControls">
|
||||
<controls:RyujinxLogo ToolTip.Tip="{markup:Locale SetupWizardFirstPageTitle}"/>
|
||||
</Grid>
|
||||
<ContentPresenter Grid.Row="1" Name="WizardPresenter"/>
|
||||
</Grid>
|
||||
</windows:StyleableAppWindow>
|
||||
83
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs
Normal file
83
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.SetupWizard;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.UI.SetupWizard
|
||||
{
|
||||
public partial class RyujinxSetupWizardWindow : StyleableAppWindow
|
||||
{
|
||||
public static bool IsUsingSetupWizard { get; set; }
|
||||
|
||||
public RyujinxSetupWizardWindow() : base(useCustomTitleBar: true)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
FlushControls.IsVisible = !ConfigurationState.Instance.ShowOldUI;
|
||||
}
|
||||
}
|
||||
|
||||
public static RyujinxSetupWizardWindow CreateWindow(MainWindowViewModel mwvm, out RyujinxSetupWizard setupWizard)
|
||||
{
|
||||
RyujinxSetupWizardWindow window = new();
|
||||
window.DataContext = setupWizard = new RyujinxSetupWizard(window.WizardPresenter, mwvm, () =>
|
||||
{
|
||||
window.Close();
|
||||
IsUsingSetupWizard = false;
|
||||
});
|
||||
window.Height = 600;
|
||||
window.Width = 750;
|
||||
return window;
|
||||
}
|
||||
|
||||
public static bool CanShowSetupWizard =>
|
||||
!File.Exists(Path.Combine(AppDataManager.BaseDirPath, ".DoNotShowSetupWizard"));
|
||||
|
||||
public static bool DisableSetupWizard()
|
||||
{
|
||||
if (!CanShowSetupWizard)
|
||||
return false; //cannot disable; file already doesn't exist, so it's disabled.
|
||||
|
||||
string disableFile = Path.Combine(AppDataManager.BaseDirPath, ".DoNotShowSetupWizard");
|
||||
|
||||
try
|
||||
{
|
||||
File.Create(disableFile, 0).Dispose();
|
||||
File.SetAttributes(disableFile, File.GetAttributes(disableFile) | FileAttributes.Hidden);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.PrintStack(LogClass.Application, e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EnableSetupWizard()
|
||||
{
|
||||
if (CanShowSetupWizard)
|
||||
return false; //cannot enable; file already exists, so it's enabled.
|
||||
|
||||
string disableFile = Path.Combine(AppDataManager.BaseDirPath, ".DoNotShowSetupWizard");
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(disableFile);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.PrintStack(LogClass.Application, e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +45,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -870,7 +871,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
private void RefreshGrid()
|
||||
{
|
||||
IObservableList<ApplicationData> appsList = Applications.ToObservableChangeSet()
|
||||
_ = Applications.ToObservableChangeSet()
|
||||
.Filter(Filter)
|
||||
.Sort(GetComparer())
|
||||
.Bind(out ReadOnlyObservableCollection<ApplicationData> apps)
|
||||
|
|
@ -1013,7 +1014,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private async Task HandleKeysInstallation(string filename)
|
||||
public async Task HandleKeysInstallation(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
<viewModels:CompatibilityViewModel />
|
||||
</window:StyleableAppWindow.DataContext>
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
|
||||
<!-- UI FlushControls -->
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto" Name="FlushControls">
|
||||
<controls:RyujinxLogo
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ using Ryujinx.HLE.HOS;
|
|||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -138,7 +139,18 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
|
||||
Executor.ExecuteBackgroundAsync(async () =>
|
||||
{
|
||||
await ShowIntelMacWarningAsync();
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ShowIntelMacWarningAsync();
|
||||
|
||||
if (Program.IsFirstStart && RyujinxSetupWizardWindow.CanShowSetupWizard)
|
||||
{
|
||||
Task windowTask = ShowAsync(RyujinxSetupWizardWindow.CreateWindow(ViewModel, out var wiz), this);
|
||||
_ = wiz.Start();
|
||||
await windowTask;
|
||||
}
|
||||
});
|
||||
|
||||
if (CommandLineState.FirmwareToInstallPathArg.TryGet(out FilePath fwPath))
|
||||
{
|
||||
if (fwPath is { ExistsAsFile: true, Extension: "xci" or "zip" } || fwPath.ExistsAsDirectory)
|
||||
|
|
@ -150,6 +162,8 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
else
|
||||
Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file.");
|
||||
}
|
||||
|
||||
await CheckLaunchState();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -399,7 +413,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!RyujinxSetupWizardWindow.IsUsingSetupWizard)
|
||||
{
|
||||
ShowKeyErrorOnLoad = false;
|
||||
|
||||
|
|
@ -538,8 +552,6 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
{
|
||||
LoadApplications();
|
||||
}
|
||||
|
||||
_ = CheckLaunchState();
|
||||
}
|
||||
|
||||
private void SetMainContent(Control content = null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue