Ryujinx/src/Ryujinx/Ui/Windows/CheatWindow.cs
TSRBerry 5c3cfb84c0
Add support for multi game XCIs (#5638)
* Add default values to ApplicationData directly

* Refactor application loading

It should now be possible to load multi game XCIs.
Included updates won't be detected for now.
Opening a game from the command line currently only opens the first one.

* Only include program NCAs where at least one tuple item is not null

* Get application data by title id and add programIndex check back

* Refactor application loading again and remove duplicate code

* Actually use patch ncas for updates

* Fix number of applications found with multi game xcis

* Don't load bundled updates from multi game xcis

* Change ApplicationData.TitleId type to ulong & Add TitleIdString property

* Use cnmt files and ContentCollection to load programs

* Ava: Add updates and DLCs from gamecarts

* Get the cnmt file from its NCA

* Ava: Identify bundled updates in updater window

* Fix the (hopefully) last few bugs

* Add idOffset parameter to GetNcaByType

* Handle missing file for dlc.json

* Ava: Shorten error message for invalid files

* Gtk: Add additional string for bundled updates in TitleUpdateWindow

* Hopefully fix DLC issues

* Apply formatting

* Finally fix DLC issues

* Adjust property names and fileSize field

* Read the correct update file

* Fix wrong casing for application id strings

* Rename TitleId to ApplicationId

* Address review comments

* Fix formatting issues

* Apply suggestions from code review

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Gracefully fail when loading pfs for update and dlc window

* Fix applications with multiple programs

* Fix DLCWindow crash on GTK

* Fix some GUI issues

* Remove IsXci again

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-11-11 21:56:57 +01:00

163 lines
6.2 KiB
C#

using Gtk;
using LibHac.Tools.FsSystem;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GUI = Gtk.Builder.ObjectAttribute;
namespace Ryujinx.Ui.Windows
{
public class CheatWindow : Window
{
private readonly string _enabledCheatsPath;
private readonly bool _noCheatsFound;
#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[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, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { }
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow"))
{
builder.Autoconnect(this);
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
_baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";
_buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath)}";
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));
_enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt");
_cheatTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string), typeof(string));
CellRendererToggle enableToggle = new();
enableToggle.Toggled += (sender, args) =>
{
_cheatTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path));
bool newValue = !(bool)_cheatTreeView.Model.GetValue(treeIter, 0);
_cheatTreeView.Model.SetValue(treeIter, 0, newValue);
if (_cheatTreeView.Model.IterChildren(out TreeIter childIter, treeIter))
{
do
{
_cheatTreeView.Model.SetValue(childIter, 0, newValue);
}
while (_cheatTreeView.Model.IterNext(ref childIter));
}
};
_cheatTreeView.AppendColumn("Enabled", enableToggle, "active", 0);
_cheatTreeView.AppendColumn("Name", new CellRendererText(), "text", 1);
_cheatTreeView.AppendColumn("Path", new CellRendererText(), "text", 2);
var buildIdColumn = _cheatTreeView.AppendColumn("Build Id", new CellRendererText(), "text", 3);
buildIdColumn.Visible = false;
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(System.IO.Path.Combine(modsBasePath, "contents")), titleId);
string currentCheatFile = string.Empty;
string buildId = string.Empty;
TreeIter parentIter = default;
foreach (var cheat in mods.Cheats)
{
if (cheat.Path.FullName != currentCheatFile)
{
currentCheatFile = cheat.Path.FullName;
string parentPath = currentCheatFile.Replace(titleModsPath, "");
buildId = System.IO.Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
parentIter = ((TreeStore)_cheatTreeView.Model).AppendValues(false, buildId, parentPath, "");
}
string cleanName = cheat.Name[1..^7];
((TreeStore)_cheatTreeView.Model).AppendValues(parentIter, enabled.Contains($"{buildId}-{cheat.Name}"), cleanName, "", buildId);
cheatAdded++;
}
if (cheatAdded == 0)
{
((TreeStore)_cheatTreeView.Model).AppendValues(false, "No Cheats Found", "", "");
_cheatTreeView.GetColumn(0).Visible = false;
_noCheatsFound = true;
_saveButton.Visible = false;
}
_cheatTreeView.ExpandAll();
}
private void SaveButton_Clicked(object sender, EventArgs args)
{
if (_noCheatsFound)
{
return;
}
List<string> enabledCheats = new();
if (_cheatTreeView.Model.GetIterFirst(out TreeIter parentIter))
{
do
{
if (_cheatTreeView.Model.IterChildren(out TreeIter childIter, parentIter))
{
do
{
var enabled = (bool)_cheatTreeView.Model.GetValue(childIter, 0);
if (enabled)
{
var name = _cheatTreeView.Model.GetValue(childIter, 1).ToString();
var buildId = _cheatTreeView.Model.GetValue(childIter, 3).ToString();
enabledCheats.Add($"{buildId}-<{name} Cheat>");
}
}
while (_cheatTreeView.Model.IterNext(ref childIter));
}
}
while (_cheatTreeView.Model.IterNext(ref parentIter));
}
Directory.CreateDirectory(System.IO.Path.GetDirectoryName(_enabledCheatsPath));
File.WriteAllLines(_enabledCheatsPath, enabledCheats);
Dispose();
}
private void CancelButton_Clicked(object sender, EventArgs args)
{
Dispose();
}
}
}