aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Ava/UI/ViewModels
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Ava/UI/ViewModels')
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs60
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs47
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs56
3 files changed, 47 insertions, 116 deletions
diff --git a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
index 9f3a0045..cdecae77 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
@@ -17,12 +17,11 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
-using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
using Application = Avalonia.Application;
using Path = System.IO.Path;
@@ -39,7 +38,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
private string _search;
- private readonly ApplicationData _applicationData;
+ private readonly ulong _titleId;
private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -93,25 +92,18 @@ namespace Ryujinx.Ava.UI.ViewModels
public IStorageProvider StorageProvider;
- public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+ public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
{
_virtualFileSystem = virtualFileSystem;
- _applicationData = applicationData;
+ _titleId = titleId;
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
StorageProvider = desktop.MainWindow.StorageProvider;
}
- _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "dlc.json");
-
- if (!File.Exists(_downloadableContentJsonPath))
- {
- _downloadableContentContainerList = new List<DownloadableContentContainer>();
-
- Save();
- }
+ _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
try
{
@@ -128,9 +120,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private void LoadDownloadableContents()
{
- // NOTE: Try to load downloadable contents from PFS first.
- AddDownloadableContent(_applicationData.Path);
-
foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
{
if (File.Exists(downloadableContentContainer.ContainerPath))
@@ -138,11 +127,7 @@ namespace Ryujinx.Ava.UI.ViewModels
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
PartitionFileSystem partitionFileSystem = new();
-
- if (partitionFileSystem.Initialize(containerFile.AsStorage()).IsFailure())
- {
- continue;
- }
+ partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
_virtualFileSystem.ImportTickets(partitionFileSystem);
@@ -235,34 +220,22 @@ namespace Ryujinx.Ava.UI.ViewModels
foreach (var file in result)
{
- if (!AddDownloadableContent(file.Path.LocalPath))
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
- }
+ await AddDownloadableContent(file.Path.LocalPath);
}
}
- private bool AddDownloadableContent(string path)
+ private async Task AddDownloadableContent(string path)
{
if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
{
- return true;
+ return;
}
using FileStream containerFile = File.OpenRead(path);
- IFileSystem partitionFileSystem;
-
- if (Path.GetExtension(path).ToLower() == ".xci")
- {
- partitionFileSystem = new Xci(_virtualFileSystem.KeySet, containerFile.AsStorage()).OpenPartition(XciPartitionType.Secure);
- }
- else
- {
- var pfsTemp = new PartitionFileSystem();
- pfsTemp.Initialize(containerFile.AsStorage()).ThrowIfFailure();
- partitionFileSystem = pfsTemp;
- }
+ PartitionFileSystem partitionFileSystem = new();
+ partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
+ bool containsDownloadableContent = false;
_virtualFileSystem.ImportTickets(partitionFileSystem);
@@ -280,7 +253,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (nca.Header.ContentType == NcaContentType.PublicData)
{
- if (nca.GetProgramIdBase() != _applicationData.IdBase)
+ if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId)
{
break;
}
@@ -292,11 +265,14 @@ namespace Ryujinx.Ava.UI.ViewModels
OnPropertyChanged(nameof(UpdateCount));
Sort();
- return true;
+ containsDownloadableContent = true;
}
}
- return false;
+ if (!containsDownloadableContent)
+ {
+ await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
+ }
}
public void Remove(DownloadableContentModel model)
diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
index 692df483..80df5d39 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
@@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _canUpdate = true;
private Cursor _cursor;
private string _title;
- private ApplicationData _currentApplicationData;
+ private string _currentEmulatedGamePath;
private readonly AutoResetEvent _rendererWaitEvent;
private WindowState _windowState;
private double _windowWidth;
@@ -106,6 +106,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationData ListSelectedApplication;
public ApplicationData GridSelectedApplication;
+ private string TitleName { get; set; }
internal AppHost AppHost { get; set; }
public MainWindowViewModel()
@@ -929,8 +930,8 @@ namespace Ryujinx.Ava.UI.ViewModels
return SortMode switch
{
#pragma warning disable IDE0055 // Disable formatting
- ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Name)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Name),
+ ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
+ : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
@@ -967,7 +968,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
if (arg is ApplicationData app)
{
- return string.IsNullOrWhiteSpace(_searchText) || app.Name.ToLower().Contains(_searchText.ToLower());
+ return string.IsNullOrWhiteSpace(_searchText) || app.TitleName.ToLower().Contains(_searchText.ToLower());
}
return false;
@@ -1096,7 +1097,7 @@ namespace Ryujinx.Ava.UI.ViewModels
IsLoadingIndeterminate = false;
break;
case LoadState.Loaded:
- LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
+ LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true;
CacheLoadStatus = "";
break;
@@ -1116,7 +1117,7 @@ namespace Ryujinx.Ava.UI.ViewModels
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded:
- LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
+ LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true;
CacheLoadStatus = "";
break;
@@ -1167,13 +1168,13 @@ namespace Ryujinx.Ava.UI.ViewModels
{
UserChannelPersistence.ShouldRestart = false;
- await LoadApplication(_currentApplicationData);
+ await LoadApplication(_currentEmulatedGamePath);
}
else
{
// Otherwise, clear state.
UserChannelPersistence = new UserChannelPersistence();
- _currentApplicationData = null;
+ _currentEmulatedGamePath = null;
}
}
@@ -1450,12 +1451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (result.Count > 0)
{
- ApplicationData applicationData = new()
- {
- Path = result[0].Path.LocalPath,
- };
-
- await LoadApplication(applicationData);
+ await LoadApplication(result[0].Path.LocalPath);
}
}
@@ -1469,17 +1465,11 @@ namespace Ryujinx.Ava.UI.ViewModels
if (result.Count > 0)
{
- ApplicationData applicationData = new()
- {
- Name = Path.GetFileNameWithoutExtension(result[0].Path.LocalPath),
- Path = result[0].Path.LocalPath,
- };
-
- await LoadApplication(applicationData);
+ await LoadApplication(result[0].Path.LocalPath);
}
}
- public async Task LoadApplication(ApplicationData application, bool startFullscreen = false)
+ public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
{
if (AppHost != null)
{
@@ -1499,7 +1489,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.RestartTime();
- SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
+ SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
PrepareLoadScreen();
@@ -1508,8 +1498,7 @@ namespace Ryujinx.Ava.UI.ViewModels
AppHost = new AppHost(
RendererHostControl,
InputManager,
- application.Path,
- application.Id,
+ path,
VirtualFileSystem,
ContentManager,
AccountManager,
@@ -1527,17 +1516,17 @@ namespace Ryujinx.Ava.UI.ViewModels
CanUpdate = false;
- LoadHeading = application.Name;
+ LoadHeading = TitleName = titleName;
- if (string.IsNullOrWhiteSpace(application.Name))
+ if (string.IsNullOrWhiteSpace(titleName))
{
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
- application.Name = AppHost.Device.Processes.ActiveApplication.Name;
+ TitleName = AppHost.Device.Processes.ActiveApplication.Name;
}
SwitchToRenderer(startFullscreen);
- _currentApplicationData = application;
+ _currentEmulatedGamePath = path;
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
gameThread.Start();
diff --git a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
index 7bb96131..5090a8c7 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
@@ -1,3 +1,4 @@
+using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage;
@@ -7,7 +8,6 @@ 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.Ava.Common.Locale;
@@ -17,16 +17,12 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
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 System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using Application = Avalonia.Application;
-using ContentType = LibHac.Ncm.ContentType;
using Path = System.IO.Path;
using SpanHelpers = LibHac.Common.SpanHelpers;
@@ -37,7 +33,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public TitleUpdateMetadata TitleUpdateWindowData;
public readonly string TitleUpdateJsonPath;
private VirtualFileSystem VirtualFileSystem { get; }
- private ApplicationData ApplicationData { get; }
+ private ulong TitleId { get; }
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
private AvaloniaList<object> _views = new();
@@ -77,18 +73,18 @@ namespace Ryujinx.Ava.UI.ViewModels
public IStorageProvider StorageProvider;
- public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+ public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
{
VirtualFileSystem = virtualFileSystem;
- ApplicationData = applicationData;
+ TitleId = titleId;
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
StorageProvider = desktop.MainWindow.StorageProvider;
}
- TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdString, "updates.json");
+ TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
try
{
@@ -96,7 +92,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
catch
{
- Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdString} at {TitleUpdateJsonPath}");
+ Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}");
TitleUpdateWindowData = new TitleUpdateMetadata
{
@@ -112,9 +108,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private void LoadUpdates()
{
- // Try to load updates from PFS first
- AddUpdate(ApplicationData.Path, true);
-
foreach (string path in TitleUpdateWindowData.Paths)
{
AddUpdate(path);
@@ -169,41 +162,17 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
- private void AddUpdate(string path, bool ignoreNotFound = false)
+ private void AddUpdate(string path)
{
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
{
- IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
- ? IntegrityCheckLevel.ErrorOnInvalid
- : IntegrityCheckLevel.None;
-
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
- IFileSystem pfs;
-
try
{
- if (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(ApplicationData.Id, out ContentCollection content))
- {
- patchNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Program);
- controlNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Control);
- }
+ var pfs = new PartitionFileSystem();
+ pfs.Initialize(file.AsStorage()).ThrowIfFailure();
+ (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, pfs, TitleId.ToString("x16"), 0);
if (controlNca != null && patchNca != null)
{
@@ -218,10 +187,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
else
{
- if (!ignoreNotFound)
- {
- Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
- }
+ Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
}
}
catch (Exception ex)