This commit is contained in:
Isaac Marovitz 2024-02-25 16:42:15 +08:00 committed by GitHub
commit 89165ed18c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 207 additions and 174 deletions

View file

@ -595,11 +595,10 @@
"SelectUpdateDialogTitle": "Select update files", "SelectUpdateDialogTitle": "Select update files",
"SelectModDialogTitle": "Select mod directory", "SelectModDialogTitle": "Select mod directory",
"UserProfileWindowTitle": "User Profiles Manager", "UserProfileWindowTitle": "User Profiles Manager",
"CheatWindowTitle": "Cheats Manager",
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager", "UpdateWindowTitle": "Title Update Manager",
"CheatWindowHeading": "Cheats Available for {0} [{1}]", "CheatWindowHeading": "Manage Cheats for {0} ({1})",
"BuildId": "BuildId:", "BuildId": "Build ID:",
"DlcWindowHeading": "{0} Downloadable Content(s)", "DlcWindowHeading": "{0} Downloadable Content(s)",
"ModWindowHeading": "{0} Mod(s)", "ModWindowHeading": "{0} Mod(s)",
"UserProfilesEditProfile": "Edit Selected", "UserProfilesEditProfile": "Edit Selected",

View file

@ -118,11 +118,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null) if (viewModel?.SelectedApplication != null)
{ {
await new CheatWindow( await CheatWindow.Show(
viewModel.VirtualFileSystem, viewModel.VirtualFileSystem,
viewModel.SelectedApplication.TitleId, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber),
viewModel.SelectedApplication.TitleName, viewModel.SelectedApplication.TitleName,
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window); viewModel.SelectedApplication.Path);
} }
} }

View file

@ -0,0 +1,92 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels
{
public class CheatWindowViewModel : BaseModel
{
private readonly string _enabledCheatsPath;
public AvaloniaList<CheatNode> LoadedCheats { get; } = new();
public string BuildId { get; }
public CheatWindowViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titlePath)
{
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("x16"));
_enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt");
string[] enabled = Array.Empty<string>();
if (File.Exists(_enabledCheatsPath))
{
enabled = File.ReadAllLines(_enabledCheatsPath);
}
var mods = new ModLoader.ModCache();
ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleId);
string currentCheatFile = string.Empty;
string buildId = string.Empty;
CheatNode currentGroup = null;
foreach (var cheat in mods.Cheats)
{
if (cheat.Path.FullName != currentCheatFile)
{
currentCheatFile = cheat.Path.FullName;
string parentPath = currentCheatFile.Replace(titleModsPath, "");
buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
currentGroup = new CheatNode("", buildId, parentPath, true);
LoadedCheats.Add(currentGroup);
}
var model = new CheatNode(cheat.Name, buildId, "", false, enabled.Contains($"{buildId}-{cheat.Name}"));
currentGroup?.SubNodes.Add(model);
}
}
public async void CopyToClipboard()
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
await desktop.MainWindow.Clipboard.SetTextAsync(BuildId);
}
}
public void Save()
{
List<string> enabledCheats = new();
foreach (var cheats in LoadedCheats)
{
foreach (var cheat in cheats.SubNodes)
{
if (cheat.IsEnabled)
{
enabledCheats.Add(cheat.BuildIdKey);
}
}
}
Directory.CreateDirectory(Path.GetDirectoryName(_enabledCheatsPath));
File.WriteAllLines(_enabledCheatsPath, enabledCheats);
}
}
}

View file

@ -15,6 +15,7 @@ using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -170,11 +171,11 @@ namespace Ryujinx.Ava.UI.Views.Main
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString(); string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await new CheatWindow( await CheatWindow.Show(
Window.VirtualFileSystem, Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, ulong.Parse(ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, NumberStyles.HexNumber),
name, name,
Window.ViewModel.SelectedApplication.Path).ShowDialog(Window); Window.ViewModel.SelectedApplication.Path);
ViewModel.AppHost.Device.EnableCheats(); ViewModel.AppHost.Device.EnableCheats();
} }

View file

@ -1,81 +1,66 @@
<window:StyleableWindow <UserControl
x:Class="Ryujinx.Ava.UI.Windows.CheatWindow" x:Class="Ryujinx.Ava.UI.Windows.CheatWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="clr-namespace:Ryujinx.Ava.UI.Models" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
Width="500" Width="500"
Height="500" Height="380"
MinWidth="500" x:DataType="viewModels:CheatWindowViewModel"
MinHeight="500"
x:DataType="window:CheatWindow"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d" mc:Ignorable="d"
Focusable="True"> Focusable="True">
<Window.Styles> <Grid>
<Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</Window.Styles>
<Grid Name="CheatGrid" Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Panel
<ColumnDefinition Width="*" /> Margin="0 0 0 10"
<ColumnDefinition Width="*" /> Grid.Row="0">
</Grid.ColumnDefinitions> <Grid>
<TextBlock <Grid.ColumnDefinitions>
Grid.Row="1" <ColumnDefinition Width="Auto" />
Grid.Column="0" <ColumnDefinition Width="*" />
Grid.ColumnSpan="2" <ColumnDefinition Width="Auto" />
MaxWidth="500" </Grid.ColumnDefinitions>
Margin="20,15,20,5" <StackPanel
HorizontalAlignment="Center" Orientation="Horizontal"
VerticalAlignment="Center" Grid.Column="0">
LineHeight="18" <TextBlock
Text="{Binding Heading}" Padding="0 0 10 0"
TextAlignment="Center" Text="{locale:Locale BuildId}" />
TextWrapping="Wrap" /> <Button
<TextBlock Name="CopyButton"
Grid.Row="2" Command="{Binding CopyToClipboard}">
Grid.Column="0" <TextBlock Text="{Binding BuildId}" />
MaxWidth="500" </Button>
Margin="140,15,20,5" </StackPanel>
HorizontalAlignment="Center" <TextBox
VerticalAlignment="Center" Grid.Column="2"
LineHeight="30" MinHeight="29"
Text="{locale:Locale BuildId}" MaxHeight="29"
TextAlignment="Center" MinWidth="180"
TextWrapping="Wrap" /> MaxWidth="180"
<TextBox HorizontalAlignment="Stretch"
Grid.Row="2" Watermark="{locale:Locale Search}"
Grid.Column="1" Text="" />
Margin="0,5,110,5" </Grid>
MinWidth="160" </Panel>
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding BuildId}"
IsReadOnly="True" />
<Border <Border
Grid.Row="3" Grid.Row="1"
Grid.Column="0" Margin="0 0 0 24"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="Gray" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
BorderThickness="1"> BorderThickness="1"
CornerRadius="5"
Padding="2.5">
<TreeView <TreeView
Name="CheatsView" Name="CheatsView"
MinHeight="300" SelectionMode="Multiple, Toggle"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding LoadedCheats}"> ItemsSource="{Binding LoadedCheats}">
@ -98,29 +83,47 @@
</TreeView.ItemTemplate> </TreeView.ItemTemplate>
</TreeView> </TreeView>
</Border> </Border>
<DockPanel <Panel
Grid.Row="4" Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Right"> <StackPanel
Orientation="Horizontal"
Spacing="10"
HorizontalAlignment="Left">
<Button
Name="AddButton"
MinWidth="90"
Margin="5"
Command="{Binding}">
<TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" />
</Button>
<Button
Name="RemoveAllButton"
MinWidth="90"
Margin="5"
Command="{Binding}">
<TextBlock Text="{locale:Locale DlcManagerRemoveAllButton}" />
</Button>
</StackPanel>
<StackPanel
Orientation="Horizontal"
Spacing="10"
HorizontalAlignment="Right">
<Button <Button
Name="SaveButton" Name="SaveButton"
MinWidth="90" MinWidth="90"
Margin="5" Margin="5"
Command="{Binding Save}" Click="SaveAndClose">
IsVisible="{Binding !NoCheatsFound}">
<TextBlock Text="{locale:Locale SettingsButtonSave}" /> <TextBlock Text="{locale:Locale SettingsButtonSave}" />
</Button> </Button>
<Button <Button
Name="CancelButton" Name="CancelButton"
MinWidth="90" MinWidth="90"
Margin="5" Margin="5"
Command="{Binding Close}"> Click="Close">
<TextBlock Text="{locale:Locale InputDialogCancel}" /> <TextBlock Text="{locale:Locale InputDialogCancel}" />
</Button> </Button>
</DockPanel> </StackPanel>
</DockPanel> </Panel>
</Grid> </Grid>
</window:StyleableWindow> </UserControl>

View file

@ -1,123 +1,61 @@
using Avalonia.Collections; using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using System.Threading.Tasks;
using Ryujinx.UI.App.Common;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {
public partial class CheatWindow : StyleableWindow public partial class CheatWindow : UserControl
{ {
private readonly string _enabledCheatsPath; public CheatWindowViewModel ViewModel;
public bool NoCheatsFound { get; }
public AvaloniaList<CheatNode> LoadedCheats { get; }
public string Heading { get; }
public string BuildId { get; }
public CheatWindow() public CheatWindow()
{ {
DataContext = this; DataContext = this;
InitializeComponent(); InitializeComponent();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
} }
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath) public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath)
{ {
LoadedCheats = new AvaloniaList<CheatNode>(); DataContext = ViewModel = new CheatWindowViewModel(virtualFileSystem, titleId, titlePath);
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
InitializeComponent(); InitializeComponent();
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, titleId);
ulong titleIdValue = ulong.Parse(titleId, NumberStyles.HexNumber);
_enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt");
string[] enabled = Array.Empty<string>();
if (File.Exists(_enabledCheatsPath))
{
enabled = File.ReadAllLines(_enabledCheatsPath);
}
int cheatAdded = 0;
var mods = new ModLoader.ModCache();
ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleIdValue);
string currentCheatFile = string.Empty;
string buildId = string.Empty;
CheatNode currentGroup = null;
foreach (var cheat in mods.Cheats)
{
if (cheat.Path.FullName != currentCheatFile)
{
currentCheatFile = cheat.Path.FullName;
string parentPath = currentCheatFile.Replace(titleModsPath, "");
buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
currentGroup = new CheatNode("", buildId, parentPath, true);
LoadedCheats.Add(currentGroup);
}
var model = new CheatNode(cheat.Name, buildId, "", false, enabled.Contains($"{buildId}-{cheat.Name}"));
currentGroup?.SubNodes.Add(model);
cheatAdded++;
}
if (cheatAdded == 0)
{
NoCheatsFound = true;
}
DataContext = this;
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
} }
public void Save() public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath)
{ {
if (NoCheatsFound) ContentDialog contentDialog = new()
{ {
return; PrimaryButtonText = "",
} SecondaryButtonText = "",
CloseButtonText = "",
Content = new CheatWindow(virtualFileSystem, titleId, titleName, titlePath),
Title = string.Format(LocaleManager.Instance[LocaleKeys.CheatWindowHeading], titleName, titleId.ToString("X16")),
};
List<string> enabledCheats = new(); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false));
foreach (var cheats in LoadedCheats) contentDialog.Styles.Add(bottomBorder);
{
foreach (var cheat in cheats.SubNodes)
{
if (cheat.IsEnabled)
{
enabledCheats.Add(cheat.BuildIdKey);
}
}
}
Directory.CreateDirectory(Path.GetDirectoryName(_enabledCheatsPath)); await ContentDialogHelper.ShowAsync(contentDialog);
}
File.WriteAllLines(_enabledCheatsPath, enabledCheats); private void SaveAndClose(object sender, RoutedEventArgs e)
{
ViewModel.Save();
((ContentDialog)Parent).Hide();
}
Close(); private void Close(object sender, RoutedEventArgs e)
{
((ContentDialog)Parent).Hide();
} }
} }
} }