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>
This commit is contained in:
parent
55557525b1
commit
5c3cfb84c0
33 changed files with 1168 additions and 816 deletions
|
@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
private bool _canUpdate = true;
|
||||
private Cursor _cursor;
|
||||
private string _title;
|
||||
private string _currentEmulatedGamePath;
|
||||
private ApplicationData _currentApplicationData;
|
||||
private readonly AutoResetEvent _rendererWaitEvent;
|
||||
private WindowState _windowState;
|
||||
private double _windowWidth;
|
||||
|
@ -106,7 +106,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
public ApplicationData ListSelectedApplication;
|
||||
public ApplicationData GridSelectedApplication;
|
||||
|
||||
private string TitleName { get; set; }
|
||||
internal AppHost AppHost { get; set; }
|
||||
|
||||
public MainWindowViewModel()
|
||||
|
@ -930,8 +929,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
return SortMode switch
|
||||
{
|
||||
#pragma warning disable IDE0055 // Disable formatting
|
||||
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
|
||||
: SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
|
||||
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Name)
|
||||
: SortExpressionComparer<ApplicationData>.Descending(app => app.Name),
|
||||
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
|
||||
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
|
||||
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
|
||||
|
@ -968,7 +967,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
if (arg is ApplicationData app)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(_searchText) || app.TitleName.ToLower().Contains(_searchText.ToLower());
|
||||
return string.IsNullOrWhiteSpace(_searchText) || app.Name.ToLower().Contains(_searchText.ToLower());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1097,7 +1096,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
IsLoadingIndeterminate = false;
|
||||
break;
|
||||
case LoadState.Loaded:
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
||||
IsLoadingIndeterminate = true;
|
||||
CacheLoadStatus = "";
|
||||
break;
|
||||
|
@ -1117,7 +1116,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
IsLoadingIndeterminate = false;
|
||||
break;
|
||||
case ShaderCacheLoadingState.Loaded:
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
||||
IsLoadingIndeterminate = true;
|
||||
CacheLoadStatus = "";
|
||||
break;
|
||||
|
@ -1168,13 +1167,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
UserChannelPersistence.ShouldRestart = false;
|
||||
|
||||
await LoadApplication(_currentEmulatedGamePath);
|
||||
await LoadApplication(_currentApplicationData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, clear state.
|
||||
UserChannelPersistence = new UserChannelPersistence();
|
||||
_currentEmulatedGamePath = null;
|
||||
_currentApplicationData = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1451,7 +1450,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
if (result.Count > 0)
|
||||
{
|
||||
await LoadApplication(result[0].Path.LocalPath);
|
||||
ApplicationData applicationData = new()
|
||||
{
|
||||
Path = result[0].Path.LocalPath,
|
||||
};
|
||||
|
||||
await LoadApplication(applicationData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1465,11 +1469,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
if (result.Count > 0)
|
||||
{
|
||||
await LoadApplication(result[0].Path.LocalPath);
|
||||
ApplicationData applicationData = new()
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(result[0].Path.LocalPath),
|
||||
Path = result[0].Path.LocalPath,
|
||||
};
|
||||
|
||||
await LoadApplication(applicationData);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false)
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
|
@ -1489,7 +1499,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
Logger.RestartTime();
|
||||
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
|
@ -1498,7 +1508,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
AppHost = new AppHost(
|
||||
RendererHostControl,
|
||||
InputManager,
|
||||
path,
|
||||
application.Path,
|
||||
application.Id,
|
||||
VirtualFileSystem,
|
||||
ContentManager,
|
||||
AccountManager,
|
||||
|
@ -1516,17 +1527,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
CanUpdate = false;
|
||||
|
||||
LoadHeading = TitleName = titleName;
|
||||
LoadHeading = application.Name;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(titleName))
|
||||
if (string.IsNullOrWhiteSpace(application.Name))
|
||||
{
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||
application.Name = AppHost.Device.Processes.ActiveApplication.Name;
|
||||
}
|
||||
|
||||
SwitchToRenderer(startFullscreen);
|
||||
|
||||
_currentEmulatedGamePath = path;
|
||||
_currentApplicationData = application;
|
||||
|
||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||
gameThread.Start();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue