Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Expose games build ID for cheat management #4340

Merged
merged 25 commits into from
May 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5c0678d
Ava UI: Expose games build ID for cheat management
gnisman Jan 24, 2023
cc5533b
Merge branch 'master' into expose-games-build-id
gnisman Jan 24, 2023
d2ed673
Fix bad merge
gnisman Jan 24, 2023
51ee35d
Change integrity check level to error on invalid
gnisman Jan 24, 2023
d65bf1e
Add support for GDK
gnisman Jan 26, 2023
aa437bf
Remove whitespace
gnisman Jan 26, 2023
f10f721
Add BID identifier
gnisman Jan 29, 2023
68207f4
PR Comments fix
gnisman Feb 3, 2023
47ea91d
Restore title id in cheats GTK window
gnisman Feb 3, 2023
83633e9
use halign center instead of margin_left
gnisman Feb 3, 2023
5da44c4
Merge branch 'master' into expose-games-build-id
gnisman Apr 1, 2023
5b1021f
Merge
gnisman Apr 1, 2023
8f4fab7
fix after merge
gnisman Apr 1, 2023
da5c9a0
PR comments fix - design AVA
gnisman Apr 19, 2023
b22a89a
PR fix - Move GetApplicationBuildId to ApplicationData class
gnisman Apr 21, 2023
496eb43
Merge branch 'master' into expose-games-build-id
gnisman Apr 21, 2023
5a7c458
PR comment fix - Add empty line before method
gnisman Apr 21, 2023
c19dee5
Merge 'origin/master' into expose-games-build-id
gnisman Apr 29, 2023
d14b220
Merge branch 'master' into expose-games-build-id
gnisman May 4, 2023
f953de5
Align with PR #4755
gnisman May 4, 2023
2518652
Merge branch 'master' into expose-games-build-id
gnisman May 5, 2023
2339333
PR comments fix
gnisman May 5, 2023
64c90f4
Change BuildId label to support translation
gnisman May 5, 2023
e655a69
Comments fix
gnisman May 6, 2023
40bb713
Remove unused BuildIdLabel property
gnisman May 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Ryujinx.Ava/Assets/Locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager",
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
"BuildId": "BuildId:",
"DlcWindowHeading": "{0} Downloadable Content(s)",
"UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel",
Expand Down
7 changes: 6 additions & 1 deletion src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Ui.App.Common;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.Common.Helper;
using System;
Expand Down Expand Up @@ -118,7 +119,11 @@ public async void OpenCheatManager_Click(object sender, RoutedEventArgs args)

if (viewModel?.SelectedApplication != null)
{
await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window);
await new CheatWindow(
viewModel.VirtualFileSystem,
viewModel.SelectedApplication.TitleId,
viewModel.SelectedApplication.TitleName,
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
Expand Down Expand Up @@ -176,7 +177,11 @@ public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e

string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();

await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window);
await new CheatWindow(
Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
Window.ViewModel.SelectedApplication.Path).ShowDialog(Window);

ViewModel.AppHost.Device.EnableCheats();
}
Expand Down
37 changes: 34 additions & 3 deletions src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,52 @@
</Window.Styles>
<Grid Name="CheatGrid" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxWidth="500"
Margin="20,15,20,20"
Margin="20,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="18"
Text="{Binding Heading}"
TextAlignment="Center"
TextWrapping="Wrap" />
<Border
<TextBlock
Grid.Row="2"
Grid.Column="0"
MaxWidth="500"
Margin="140,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="30"
Text="{locale:Locale BuildId}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Margin="0,5,110,5"
MinWidth="160"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding BuildId}"
IsReadOnly="True" />
<Border
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Expand Down Expand Up @@ -81,7 +110,9 @@
</TreeView>
</Border>
<DockPanel
Grid.Row="3"
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0"
HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Right">
Expand Down
10 changes: 7 additions & 3 deletions src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Avalonia.Collections;
using Avalonia;
using Avalonia.Collections;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -17,6 +19,7 @@ public partial class CheatWindow : StyleableWindow
private AvaloniaList<CheatsList> LoadedCheats { get; }

public string Heading { get; }
public string BuildId { get; }

public CheatWindow()
{
Expand All @@ -27,12 +30,13 @@ public CheatWindow()
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
}

public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
{
LoadedCheats = new AvaloniaList<CheatsList>();

Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());

BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
gnisman marked this conversation as resolved.
Show resolved Hide resolved

InitializeComponent();

string modsBasePath = ModLoader.GetModsBasePath();
Expand Down
128 changes: 128 additions & 0 deletions src/Ryujinx.Ui.Common/App/ApplicationData.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
using LibHac.Common;
using LibHac.Ns;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Loader;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using System;
using System.IO;

namespace Ryujinx.Ui.App.Common
{
Expand All @@ -19,5 +30,122 @@ public class ApplicationData
public double FileSizeBytes { get; set; }
public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }

public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
{
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);

Nca mainNca = null;
Nca patchNca = null;

if (!System.IO.Path.Exists(titleFilePath))
{
Logger.Error?.Print(LogClass.Application, $"File does not exists. {titleFilePath}");
return string.Empty;
}

string extension = System.IO.Path.GetExtension(titleFilePath).ToLower();

if (extension is ".nsp" or ".xci")
{
PartitionFileSystem pfs;

if (extension == ".xci")
{
Xci xci = new(virtualFileSystem.KeySet, file.AsStorage());

pfs = xci.OpenPartition(XciPartitionType.Secure);
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}

foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();

pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();

Nca nca = new(virtualFileSystem.KeySet, ncaFile.Get.AsStorage());

if (nca.Header.ContentType != NcaContentType.Program)
{
continue;
}

int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);

if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
patchNca = nca;
}
else
{
mainNca = nca;
}
}
}
else if (extension == ".nca")
{
mainNca = new Nca(virtualFileSystem.KeySet, file.AsStorage());
}

if (mainNca == null)
{
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");

return string.Empty;
}

(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);

if (updatePatchNca != null)
{
patchNca = updatePatchNca;
}

IFileSystem codeFs = null;

if (patchNca == null)
{
if (mainNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
}
}
else
{
if (patchNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
}
}

if (codeFs == null)
{
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");

return string.Empty;
}

const string mainExeFs = "main";

if (!codeFs.FileExists($"/{mainExeFs}"))
{
Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS");

return string.Empty;
}

using var nsoFile = new UniqueRef<IFile>();

codeFs.OpenFile(ref nsoFile.Ref, $"/{mainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure();

NsoReader reader = new NsoReader();
reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();

return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16];
}
}
}
9 changes: 6 additions & 3 deletions src/Ryujinx/Ui/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1626,9 +1626,12 @@ private void HideUi_Pressed(object sender, EventArgs args)

private void ManageCheats_Pressed(object sender, EventArgs args)
{
var window = new CheatWindow(_virtualFileSystem,
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString());
var window = new CheatWindow(
_virtualFileSystem,
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties
.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
_currentEmulatedGamePath);

window.Destroyed += CheatWindow_Destroyed;
window.Show();
Expand Down
2 changes: 1 addition & 1 deletion src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ private void ManageDlc_Clicked(object sender, EventArgs args)

private void ManageCheats_Clicked(object sender, EventArgs args)
{
new CheatWindow(_virtualFileSystem, _titleId, _titleName).Show();
new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show();
}

private void OpenTitleModDir_Clicked(object sender, EventArgs args)
Expand Down
7 changes: 5 additions & 2 deletions src/Ryujinx/Ui/Windows/CheatWindow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Gtk;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -17,16 +18,18 @@ public class CheatWindow : Window

#pragma warning disable CS0649, IDE0044
[GUI] Label _baseTitleInfoLabel;
[GUI] TextView _buildIdTextView;
[GUI] TreeView _cheatTreeView;
[GUI] Button _saveButton;
#pragma warning restore CS0649, IDE0044

public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName) { }
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { }

private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : base(builder.GetRawOwnedObject("_cheatWindow"))
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow"))
{
builder.Autoconnect(this);
_baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";
_buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}";

string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));
Expand Down
17 changes: 16 additions & 1 deletion src/Ryujinx/Ui/Windows/CheatWindow.glade
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="_buildIdTextView">
<property name="visible">True</property>
<property name="margin_top">10</property>
<property name="halign">center</property>
<property name="margin_bottom">10</property>
<property name="editable">False</property>
<property name="cursor_visible">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
Expand All @@ -57,7 +72,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
</object>
Expand Down