Revert "Add support for multi game XCIs (#5638)" (#5914)

This reverts commit 5c3cfb84c0.
This commit is contained in:
gdkchan 2023-11-11 23:35:30 -03:00 committed by GitHub
parent 6228331fd1
commit 51065d9129
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 819 additions and 1171 deletions

View file

@ -7,7 +7,6 @@ using Ryujinx.Common.SystemInterop;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
using Ryujinx.Ui;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
@ -333,12 +332,7 @@ namespace Ryujinx
if (CommandLineState.LaunchPathArg != null)
{
ApplicationData applicationData = new()
{
Path = CommandLineState.LaunchPathArg,
};
mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg);
mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
}
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))

View file

@ -39,7 +39,6 @@ using Silk.NET.Vulkan;
using SPB.Graphics.Vulkan;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
@ -71,7 +70,7 @@ namespace Ryujinx.Ui
private bool _gameLoaded;
private bool _ending;
private ApplicationData _currentApplicationData = null;
private string _currentEmulatedGamePath = null;
private string _lastScannedAmiiboId = "";
private bool _lastScannedAmiiboShowAll = false;
@ -182,12 +181,8 @@ namespace Ryujinx.Ui
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile);
_userChannelPersistence = new UserChannelPersistence();
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
// Instantiate GUI objects.
_applicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel);
_applicationLibrary = new ApplicationLibrary(_virtualFileSystem);
_uiHandler = new GtkHostUiHandler(this);
_deviceExitStatus = new AutoResetEvent(false);
@ -789,7 +784,7 @@ namespace Ryujinx.Ui
}
}
private bool LoadApplication(string path, ulong titleId, bool isFirmwareTitle)
private bool LoadApplication(string path, bool isFirmwareTitle)
{
SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
@ -863,7 +858,7 @@ namespace Ryujinx.Ui
case ".xci":
Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
return _emulationContext.LoadXci(path, titleId);
return _emulationContext.LoadXci(path);
case ".nca":
Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
@ -872,7 +867,7 @@ namespace Ryujinx.Ui
case ".pfs0":
Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
return _emulationContext.LoadNsp(path, titleId);
return _emulationContext.LoadNsp(path);
default:
Logger.Info?.Print(LogClass.Application, "Loading as Homebrew.");
try
@ -893,7 +888,7 @@ namespace Ryujinx.Ui
return false;
}
public void RunApplication(ApplicationData application, bool startFullscreen = false)
public void RunApplication(string path, bool startFullscreen = false)
{
if (_gameLoaded)
{
@ -915,14 +910,14 @@ namespace Ryujinx.Ui
bool isFirmwareTitle = false;
if (application.Path.StartsWith("@SystemContent"))
if (path.StartsWith("@SystemContent"))
{
application.Path = VirtualFileSystem.SwitchPathToSystemPath(application.Path);
path = VirtualFileSystem.SwitchPathToSystemPath(path);
isFirmwareTitle = true;
}
if (!LoadApplication(application.Path, application.Id, isFirmwareTitle))
if (!LoadApplication(path, isFirmwareTitle))
{
_emulationContext.Dispose();
SwitchToGameTable();
@ -932,7 +927,7 @@ namespace Ryujinx.Ui
SetupProgressUiHandlers();
_currentApplicationData = application;
_currentEmulatedGamePath = path;
_deviceExitStatus.Reset();
@ -1173,7 +1168,7 @@ namespace Ryujinx.Ui
_tableStore.AppendValues(
args.AppData.Favorite,
new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
$"{args.AppData.Name}\n{args.AppData.IdString.ToUpper()}",
$"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
args.AppData.Developer,
args.AppData.Version,
args.AppData.TimePlayedString,
@ -1261,22 +1256,9 @@ namespace Ryujinx.Ui
{
_gameTableSelection.GetSelected(out TreeIter treeIter);
ApplicationData application = new()
{
Favorite = (bool)_tableStore.GetValue(treeIter, 0),
Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0],
Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber),
Developer = (string)_tableStore.GetValue(treeIter, 3),
Version = (string)_tableStore.GetValue(treeIter, 4),
TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)),
LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)),
FileExtension = (string)_tableStore.GetValue(treeIter, 7),
FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)),
Path = (string)_tableStore.GetValue(treeIter, 9),
ControlHolder = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10),
};
string path = (string)_tableStore.GetValue(treeIter, 9);
RunApplication(application);
RunApplication(path);
}
private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args)
@ -1334,22 +1316,13 @@ namespace Ryujinx.Ui
return;
}
ApplicationData application = new()
{
Favorite = (bool)_tableStore.GetValue(treeIter, 0),
Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0],
Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber),
Developer = (string)_tableStore.GetValue(treeIter, 3),
Version = (string)_tableStore.GetValue(treeIter, 4),
TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)),
LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)),
FileExtension = (string)_tableStore.GetValue(treeIter, 7),
FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)),
Path = (string)_tableStore.GetValue(treeIter, 9),
ControlHolder = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10),
};
string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString();
string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0];
string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
_ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, application);
BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
_ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, titleFilePath, titleName, titleId, controlData);
}
private void Load_Application_File(object sender, EventArgs args)
@ -1371,12 +1344,7 @@ namespace Ryujinx.Ui
if (fileChooser.Run() == (int)ResponseType.Accept)
{
ApplicationData applicationData = new()
{
Path = fileChooser.Filename,
};
RunApplication(applicationData);
RunApplication(fileChooser.Filename);
}
}
@ -1386,13 +1354,7 @@ namespace Ryujinx.Ui
if (fileChooser.Run() == (int)ResponseType.Accept)
{
ApplicationData applicationData = new()
{
Name = System.IO.Path.GetFileNameWithoutExtension(fileChooser.Filename),
Path = fileChooser.Filename,
};
RunApplication(applicationData);
RunApplication(fileChooser.Filename);
}
}
@ -1407,14 +1369,7 @@ namespace Ryujinx.Ui
{
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
ApplicationData applicationData = new()
{
Name = "miiEdit",
Id = 0x0100000000001009ul,
Path = contentPath,
};
RunApplication(applicationData);
RunApplication(contentPath);
}
private void Open_Ryu_Folder(object sender, EventArgs args)
@ -1690,13 +1645,13 @@ namespace Ryujinx.Ui
{
_userChannelPersistence.ShouldRestart = false;
RunApplication(_currentApplicationData);
RunApplication(_currentEmulatedGamePath);
}
else
{
// otherwise, clear state.
_userChannelPersistence = new UserChannelPersistence();
_currentApplicationData = null;
_currentEmulatedGamePath = null;
_actionMenu.Sensitive = false;
_firmwareInstallFile.Sensitive = true;
_firmwareInstallDirectory.Sensitive = true;
@ -1758,7 +1713,7 @@ namespace Ryujinx.Ui
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties
.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
_currentApplicationData.Path);
_currentEmulatedGamePath);
window.Destroyed += CheatWindow_Destroyed;
window.Show();

View file

@ -16,7 +16,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
@ -24,6 +23,7 @@ using Ryujinx.Ui.Windows;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
@ -36,13 +36,17 @@ namespace Ryujinx.Ui.Widgets
private readonly VirtualFileSystem _virtualFileSystem;
private readonly AccountManager _accountManager;
private readonly HorizonClient _horizonClient;
private readonly BlitStruct<ApplicationControlProperty> _controlData;
private readonly ApplicationData _title;
private readonly string _titleFilePath;
private readonly string _titleName;
private readonly string _titleIdText;
private readonly ulong _titleId;
private MessageDialog _dialog;
private bool _cancel;
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, ApplicationData applicationData)
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
{
_parent = parent;
@ -51,13 +55,23 @@ namespace Ryujinx.Ui.Widgets
_virtualFileSystem = virtualFileSystem;
_accountManager = accountManager;
_horizonClient = horizonClient;
_title = applicationData;
_titleFilePath = titleFilePath;
_titleName = titleName;
_titleIdText = titleId;
_controlData = controlData;
_openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(_title.ControlHolder.ByteSpan) && _title.ControlHolder.Value.UserAccountSaveDataSize > 0;
_openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(_title.ControlHolder.ByteSpan) && _title.ControlHolder.Value.DeviceSaveDataSize > 0;
_openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(_title.ControlHolder.ByteSpan) && _title.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId))
{
GtkDialog.CreateErrorDialog("The selected game did not have a valid Title Id");
string fileExt = System.IO.Path.GetExtension(_title.Path).ToLower();
return;
}
_openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0;
_openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0;
_openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0;
string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower();
bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci";
_extractRomFsMenuItem.Sensitive = hasNca;
@ -123,7 +137,7 @@ namespace Ryujinx.Ui.Widgets
private void OpenSaveDir(in SaveDataFilter saveDataFilter)
{
if (!TryFindSaveData(_title.Name, _title.Id, _title.ControlHolder, in saveDataFilter, out ulong saveDataId))
if (!TryFindSaveData(_titleName, _titleId, _controlData, in saveDataFilter, out ulong saveDataId))
{
return;
}
@ -176,7 +190,7 @@ namespace Ryujinx.Ui.Widgets
{
Title = "Ryujinx - NCA Section Extractor",
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"),
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_title.Path)}...",
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...",
WindowPosition = WindowPosition.Center,
};
@ -188,18 +202,18 @@ namespace Ryujinx.Ui.Widgets
}
});
using FileStream file = new(_title.Path, FileMode.Open, FileAccess.Read);
using FileStream file = new(_titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
if ((System.IO.Path.GetExtension(_title.Path).ToLower() == ".nsp") ||
(System.IO.Path.GetExtension(_title.Path).ToLower() == ".pfs0") ||
(System.IO.Path.GetExtension(_title.Path).ToLower() == ".xci"))
if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci"))
{
IFileSystem pfs;
if (System.IO.Path.GetExtension(_title.Path).ToLower() == ".xci")
if (System.IO.Path.GetExtension(_titleFilePath) == ".xci")
{
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
@ -235,7 +249,7 @@ namespace Ryujinx.Ui.Widgets
}
}
}
else if (System.IO.Path.GetExtension(_title.Path).ToLower() == ".nca")
else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca")
{
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
}
@ -252,11 +266,7 @@ namespace Ryujinx.Ui.Widgets
return;
}
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
if (updatePatchNca != null)
{
@ -450,44 +460,44 @@ namespace Ryujinx.Ui.Widgets
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
{
var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
var saveDataFilter = SaveDataFilter.Make(_title.Id, saveType: default, userId, saveDataId: default, index: default);
var saveDataFilter = SaveDataFilter.Make(_titleId, saveType: default, userId, saveDataId: default, index: default);
OpenSaveDir(in saveDataFilter);
}
private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
{
var saveDataFilter = SaveDataFilter.Make(_title.Id, SaveDataType.Device, userId: default, saveDataId: default, index: default);
var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Device, userId: default, saveDataId: default, index: default);
OpenSaveDir(in saveDataFilter);
}
private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
{
var saveDataFilter = SaveDataFilter.Make(_title.Id, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
OpenSaveDir(in saveDataFilter);
}
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
{
new TitleUpdateWindow(_parent, _virtualFileSystem, _title).Show();
new TitleUpdateWindow(_parent, _virtualFileSystem, _titleIdText, _titleName).Show();
}
private void ManageDlc_Clicked(object sender, EventArgs args)
{
new DlcWindow(_virtualFileSystem, _title.IdString, _title).Show();
new DlcWindow(_virtualFileSystem, _titleIdText, _titleName).Show();
}
private void ManageCheats_Clicked(object sender, EventArgs args)
{
new CheatWindow(_virtualFileSystem, _title.Id, _title.Name, _title.Path).Show();
new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show();
}
private void OpenTitleModDir_Clicked(object sender, EventArgs args)
{
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _title.IdString);
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText);
OpenHelper.OpenFolder(titleModsPath);
}
@ -495,7 +505,7 @@ namespace Ryujinx.Ui.Widgets
private void OpenTitleSdModDir_Clicked(object sender, EventArgs args)
{
string sdModsBasePath = ModLoader.GetSdModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _title.IdString);
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText);
OpenHelper.OpenFolder(titleModsPath);
}
@ -517,7 +527,7 @@ namespace Ryujinx.Ui.Widgets
private void OpenPtcDir_Clicked(object sender, EventArgs args)
{
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "cpu");
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu");
string mainPath = System.IO.Path.Combine(ptcDir, "0");
string backupPath = System.IO.Path.Combine(ptcDir, "1");
@ -534,7 +544,7 @@ namespace Ryujinx.Ui.Widgets
private void OpenShaderCacheDir_Clicked(object sender, EventArgs args)
{
string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "shader");
string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader");
if (!Directory.Exists(shaderCacheDir))
{
@ -546,10 +556,10 @@ namespace Ryujinx.Ui.Widgets
private void PurgePtcCache_Clicked(object sender, EventArgs args)
{
DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "cpu", "1"));
DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1"));
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_title.Name}</b>\n\nAre you sure you want to proceed?");
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
List<FileInfo> cacheFiles = new();
@ -583,9 +593,9 @@ namespace Ryujinx.Ui.Widgets
private void PurgeShaderCache_Clicked(object sender, EventArgs args)
{
DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "shader"));
DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader"));
using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_title.Name}</b>\n\nAre you sure you want to proceed?");
using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
List<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new();
@ -627,11 +637,8 @@ namespace Ryujinx.Ui.Widgets
private void CreateShortcut_Clicked(object sender, EventArgs args)
{
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_title.Path, ConfigurationState.Instance.System.Language, _title.Id);
ShortcutHelper.CreateAppShortcut(_title.Path, _title.Name, _title.IdString, appIcon);
byte[] appIcon = new ApplicationLibrary(_virtualFileSystem).GetApplicationIcon(_titleFilePath, ConfigurationState.Instance.System.Language);
ShortcutHelper.CreateAppShortcut(_titleFilePath, _titleName, _titleIdText, appIcon);
}
}
}

View file

@ -1,9 +1,7 @@
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;
@ -29,13 +27,8 @@ namespace Ryujinx.Ui.Windows
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)}";
_buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}";
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));

View file

@ -9,12 +9,9 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Widgets;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using GUI = Gtk.Builder.ObjectAttribute;
@ -23,7 +20,7 @@ namespace Ryujinx.Ui.Windows
public class DlcWindow : Window
{
private readonly VirtualFileSystem _virtualFileSystem;
private readonly string _applicationId;
private readonly string _titleId;
private readonly string _dlcJsonPath;
private readonly List<DownloadableContentContainer> _dlcContainerList;
@ -35,16 +32,16 @@ namespace Ryujinx.Ui.Windows
[GUI] TreeSelection _dlcTreeSelection;
#pragma warning restore CS0649, IDE0044
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, ApplicationData title) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, title) { }
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, titleName) { }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationId, ApplicationData title) : base(builder.GetRawOwnedObject("_dlcWindow"))
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_dlcWindow"))
{
builder.Autoconnect(this);
_applicationId = applicationId;
_titleId = titleId;
_virtualFileSystem = virtualFileSystem;
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationId, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {title.Name} [{applicationId.ToUpper()}]";
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]";
try
{
@ -75,12 +72,9 @@ namespace Ryujinx.Ui.Windows
};
_dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0);
_dlcTreeView.AppendColumn("ApplicationId", new CellRendererText(), "text", 1);
_dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1);
_dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2);
// NOTE: Try to load downloadable contents from PFS first.
AddDlc(title.Path, true);
foreach (DownloadableContentContainer dlcContainer in _dlcContainerList)
{
if (File.Exists(dlcContainer.ContainerPath))
@ -95,10 +89,7 @@ namespace Ryujinx.Ui.Windows
using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath);
PartitionFileSystem pfs = new();
if (pfs.Initialize(containerFile.AsStorage()).IsFailure())
{
continue;
}
pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure();
_virtualFileSystem.ImportTickets(pfs);
@ -137,57 +128,6 @@ namespace Ryujinx.Ui.Windows
return null;
}
private void AddDlc(string path, bool ignoreNotFound = false)
{
if (!File.Exists(path))
{
return;
}
using FileStream containerFile = File.OpenRead(path);
PartitionFileSystem pfs = new();
pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure();
bool containsDlc = false;
_virtualFileSystem.ImportTickets(pfs);
TreeIter? parentIter = null;
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 = TryCreateNca(ncaFile.Get.AsStorage(), path);
if (nca == null)
{
continue;
}
if (nca.Header.ContentType == NcaContentType.PublicData)
{
if (nca.GetProgramIdBase() != (ulong.Parse(_applicationId, NumberStyles.HexNumber) & ~0x1FFFUL))
{
break;
}
parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", path);
((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath);
containsDlc = true;
}
}
if (!containsDlc && !ignoreNotFound)
{
GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!");
}
}
private void AddButton_Clicked(object sender, EventArgs args)
{
FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel")
@ -207,7 +147,52 @@ namespace Ryujinx.Ui.Windows
{
foreach (string containerPath in fileChooser.Filenames)
{
AddDlc(containerPath);
if (!File.Exists(containerPath))
{
return;
}
using FileStream containerFile = File.OpenRead(containerPath);
PartitionFileSystem pfs = new();
pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure();
bool containsDlc = false;
_virtualFileSystem.ImportTickets(pfs);
TreeIter? parentIter = null;
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 = TryCreateNca(ncaFile.Get.AsStorage(), containerPath);
if (nca == null)
{
continue;
}
if (nca.Header.ContentType == NcaContentType.PublicData)
{
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId)
{
break;
}
parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath);
((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath);
containsDlc = true;
}
}
if (!containsDlc)
{
GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!");
}
}
}

View file

@ -4,15 +4,12 @@ using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ns;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Widgets;
using System;
using System.Collections.Generic;
@ -27,7 +24,7 @@ namespace Ryujinx.Ui.Windows
{
private readonly MainWindow _parent;
private readonly VirtualFileSystem _virtualFileSystem;
private readonly ApplicationData _title;
private readonly string _titleId;
private readonly string _updateJsonPath;
private TitleUpdateMetadata _titleUpdateWindowData;
@ -41,17 +38,17 @@ namespace Ryujinx.Ui.Windows
[GUI] RadioButton _noUpdateRadioButton;
#pragma warning restore CS0649, IDE0044
public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, applicationData) { }
public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, titleId, titleName) { }
private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_titleUpdateWindow"))
private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_titleUpdateWindow"))
{
_parent = parent;
builder.Autoconnect(this);
_title = applicationData;
_titleId = titleId;
_virtualFileSystem = virtualFileSystem;
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "updates.json");
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json");
_radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
try
@ -67,10 +64,7 @@ namespace Ryujinx.Ui.Windows
};
}
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdString}]";
// Try to get updates from PFS first
AddUpdate(_title.Path, true);
_baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]";
foreach (string path in _titleUpdateWindowData.Paths)
{
@ -90,41 +84,18 @@ namespace Ryujinx.Ui.Windows
}
}
private void AddUpdate(string path, bool ignoreNotFound = false)
private void AddUpdate(string path)
{
if (File.Exists(path))
{
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
IFileSystem pfs;
PartitionFileSystem nsp = new();
nsp.Initialize(file.AsStorage()).ThrowIfFailure();
try
{
if (System.IO.Path.GetExtension(path).ToLower() == ".xci")
{
pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
}
else
{
var pfsTemp = new PartitionFileSystem();
pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
pfs = pfsTemp;
}
Dictionary<ulong, ContentCollection> updates = pfs.GetUpdateData(_virtualFileSystem, checkLevel);
Nca patchNca = null;
Nca controlNca = null;
if (updates.TryGetValue(_title.Id, out ContentCollection update))
{
patchNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Program);
controlNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Control);
}
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0);
if (controlNca != null && patchNca != null)
{
@ -135,14 +106,7 @@ namespace Ryujinx.Ui.Windows
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
string radioLabel = $"Version {controlData.DisplayVersionString.ToString()} - {path}";
if (System.IO.Path.GetExtension(path).ToLower() == ".xci")
{
radioLabel = "Bundled: " + radioLabel;
}
RadioButton radioButton = new(radioLabel);
RadioButton radioButton = new($"Version {controlData.DisplayVersionString.ToString()} - {path}");
radioButton.JoinGroup(_noUpdateRadioButton);
_availableUpdatesBox.Add(radioButton);
@ -153,10 +117,7 @@ namespace Ryujinx.Ui.Windows
}
else
{
if (!ignoreNotFound)
{
GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
}
GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
}
}
catch (Exception exception)