mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-11 20:11:29 +00:00
Fix fuck ups
This commit is contained in:
parent
515ddf195f
commit
d68ec8692e
3 changed files with 299 additions and 333 deletions
|
@ -185,7 +185,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, containerPath));
|
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogLoadNcaErrorMessage], ex.Message, containerPath));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,17 @@ using Avalonia.Media;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
using DynamicData.Binding;
|
using DynamicData.Binding;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Tools.Fs;
|
||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
|
using Ryujinx.Ava.UI.Renderer;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
@ -87,7 +90,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
private float _volume;
|
private float _volume;
|
||||||
private string _backendText;
|
private string _backendText;
|
||||||
|
|
||||||
private bool _canUpdate;
|
private bool _canUpdate = true;
|
||||||
private Cursor _cursor;
|
private Cursor _cursor;
|
||||||
private string _title;
|
private string _title;
|
||||||
private string _currentEmulatedGamePath;
|
private string _currentEmulatedGamePath;
|
||||||
|
@ -176,11 +179,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public bool CanUpdate
|
public bool CanUpdate
|
||||||
{
|
{
|
||||||
get => _canUpdate;
|
get => _canUpdate && EnableNonGameRunningControls && Modules.Updater.CanUpdate(false);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_canUpdate = value;
|
_canUpdate = value;
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,6 +344,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
|
||||||
|
|
||||||
|
public bool EnabledDeviceSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
|
||||||
|
|
||||||
|
public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
||||||
|
|
||||||
public string LoadHeading
|
public string LoadHeading
|
||||||
{
|
{
|
||||||
get => _loadHeading;
|
get => _loadHeading;
|
||||||
|
@ -675,6 +683,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
get => ConsoleHelper.SetConsoleWindowStateSupported;
|
get => ConsoleHelper.SetConsoleWindowStateSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ManageFileTypesVisible
|
||||||
|
{
|
||||||
|
get => FileAssociationHelper.IsTypeAssociationSupported;
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<ApplicationData> Applications
|
public ObservableCollection<ApplicationData> Applications
|
||||||
{
|
{
|
||||||
get => _applications;
|
get => _applications;
|
||||||
|
@ -733,19 +746,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
switch (ConfigurationState.Instance.Ui.GridSize)
|
return ConfigurationState.Instance.Ui.GridSize.Value switch
|
||||||
{
|
{
|
||||||
case 1:
|
1 => 78,
|
||||||
return 78;
|
2 => 100,
|
||||||
case 2:
|
3 => 120,
|
||||||
return 100;
|
4 => 140,
|
||||||
case 3:
|
_ => 16,
|
||||||
return 120;
|
};
|
||||||
case 4:
|
|
||||||
return 140;
|
|
||||||
default:
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,19 +761,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
switch (ConfigurationState.Instance.Ui.GridSize)
|
return ConfigurationState.Instance.Ui.GridSize.Value switch
|
||||||
{
|
{
|
||||||
case 1:
|
1 => 120,
|
||||||
return 120;
|
2 => ShowNames ? 210 : 150,
|
||||||
case 2:
|
3 => ShowNames ? 240 : 180,
|
||||||
return ShowNames ? 210 : 150;
|
4 => ShowNames ? 280 : 220,
|
||||||
case 3:
|
_ => 16,
|
||||||
return ShowNames ? 240 : 180;
|
};
|
||||||
case 4:
|
|
||||||
return ShowNames ? 280 : 220;
|
|
||||||
default:
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -870,7 +873,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
public Action<bool> SwitchToGameControl { get; private set; }
|
public Action<bool> SwitchToGameControl { get; private set; }
|
||||||
public Action<Control> SetMainContent { get; private set; }
|
public Action<Control> SetMainContent { get; private set; }
|
||||||
public TopLevel TopLevel { get; private set; }
|
public TopLevel TopLevel { get; private set; }
|
||||||
public RendererHost RendererControl { get; private set; }
|
public RendererHost RendererHostControl { get; private set; }
|
||||||
public bool IsClosing { get; set; }
|
public bool IsClosing { get; set; }
|
||||||
public LibHacHorizonManager LibHacHorizonManager { get; internal set; }
|
public LibHacHorizonManager LibHacHorizonManager { get; internal set; }
|
||||||
public IHostUiHandler UiHandler { get; internal set; }
|
public IHostUiHandler UiHandler { get; internal set; }
|
||||||
|
@ -947,20 +950,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
if (firmwareVersion == null)
|
if (firmwareVersion == null)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename));
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string dialogTitle = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle], firmwareVersion.VersionString);
|
string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle, firmwareVersion.VersionString);
|
||||||
|
string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage, firmwareVersion.VersionString);
|
||||||
|
|
||||||
SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion();
|
SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
|
|
||||||
string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString);
|
|
||||||
|
|
||||||
if (currentVersion != null)
|
if (currentVersion != null)
|
||||||
{
|
{
|
||||||
dialogMessage += string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage], currentVersion.VersionString);
|
dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage, currentVersion.VersionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage];
|
dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage];
|
||||||
|
@ -993,7 +994,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
waitingDialog.Close();
|
waitingDialog.Close();
|
||||||
|
|
||||||
string message = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage], firmwareVersion.VersionString);
|
string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString);
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
|
|
||||||
|
@ -1063,7 +1064,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
IsLoadingIndeterminate = false;
|
IsLoadingIndeterminate = false;
|
||||||
break;
|
break;
|
||||||
case LoadState.Loaded:
|
case LoadState.Loaded:
|
||||||
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||||
IsLoadingIndeterminate = true;
|
IsLoadingIndeterminate = true;
|
||||||
CacheLoadStatus = "";
|
CacheLoadStatus = "";
|
||||||
break;
|
break;
|
||||||
|
@ -1079,7 +1080,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
IsLoadingIndeterminate = false;
|
IsLoadingIndeterminate = false;
|
||||||
break;
|
break;
|
||||||
case ShaderCacheLoadingState.Loaded:
|
case ShaderCacheLoadingState.Loaded:
|
||||||
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||||
IsLoadingIndeterminate = true;
|
IsLoadingIndeterminate = true;
|
||||||
CacheLoadStatus = "";
|
CacheLoadStatus = "";
|
||||||
break;
|
break;
|
||||||
|
@ -1091,35 +1092,27 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSaveDirectory(in SaveDataFilter filter, ApplicationData data, ulong titleId)
|
|
||||||
{
|
|
||||||
ApplicationHelper.OpenSaveDir(in filter, titleId, data.ControlHolder, data.TitleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void ExtractLogo()
|
private async void ExtractLogo()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, selection.Path);
|
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ExtractRomFs()
|
private async void ExtractRomFs()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
await ApplicationHelper.ExtractSection(NcaSectionType.Data, selection.Path);
|
await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ExtractExeFs()
|
private async void ExtractExeFs()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
await ApplicationHelper.ExtractSection(NcaSectionType.Code, selection.Path);
|
await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,7 +1137,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
private void InitializeGame()
|
private void InitializeGame()
|
||||||
{
|
{
|
||||||
RendererControl.RendererInitialized += GlRenderer_Created;
|
RendererHostControl.WindowCreated += RendererHost_Created;
|
||||||
|
|
||||||
AppHost.StatusUpdatedEvent += Update_StatusBar;
|
AppHost.StatusUpdatedEvent += Update_StatusBar;
|
||||||
AppHost.AppExit += AppHost_AppExit;
|
AppHost.AppExit += AppHost_AppExit;
|
||||||
|
@ -1203,7 +1196,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GlRenderer_Created(object sender, EventArgs e)
|
private void RendererHost_Created(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ShowLoading(false);
|
ShowLoading(false);
|
||||||
|
|
||||||
|
@ -1333,10 +1326,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeLanguage(object obj)
|
public void ChangeLanguage(object languageCode)
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.LoadDefaultLanguage();
|
LocaleManager.Instance.LoadLanguage((string)languageCode);
|
||||||
LocaleManager.Instance.LoadLanguage((string)obj);
|
|
||||||
|
if (Program.PreviewerDetached)
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Ui.LanguageCode.Value = (string)languageCode;
|
||||||
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ManageProfiles()
|
public async void ManageProfiles()
|
||||||
|
@ -1374,7 +1372,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionMessage], selection.TitleName),
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
@ -1401,7 +1399,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], file.Name, e));
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1438,7 +1436,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogShaderDeletionMessage], selection.TitleName),
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
@ -1463,7 +1461,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], directory.Name, e));
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1476,62 +1474,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.ShaderCachePurgeError], file.Name, e));
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenDeviceSaveDirectory()
|
|
||||||
{
|
|
||||||
ApplicationData selection = SelectedApplication;
|
|
||||||
if (selection != null)
|
|
||||||
{
|
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
|
||||||
{
|
|
||||||
async void Action()
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(Action);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, SaveDataType.Device, userId: default, saveDataId: default, index: default);
|
|
||||||
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenBcatSaveDirectory()
|
|
||||||
{
|
|
||||||
ApplicationData selection = SelectedApplication;
|
|
||||||
if (selection != null)
|
|
||||||
{
|
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
|
||||||
{
|
|
||||||
async void Action()
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(Action);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
|
|
||||||
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleFavorite()
|
public void ToggleFavorite()
|
||||||
{
|
{
|
||||||
ApplicationData selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
@ -1550,37 +1498,45 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public void OpenUserSaveDirectory()
|
public void OpenUserSaveDirectory()
|
||||||
{
|
{
|
||||||
ApplicationData selection = SelectedApplication;
|
OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low));
|
||||||
if (selection != null)
|
}
|
||||||
|
|
||||||
|
public void OpenDeviceSaveDirectory()
|
||||||
|
{
|
||||||
|
OpenSaveDirectory(SaveDataType.Device, userId: default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenBcatSaveDirectory()
|
||||||
|
{
|
||||||
|
OpenSaveDirectory(SaveDataType.Bcat, userId: default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenSaveDirectory(SaveDataType saveDataType, UserId userId)
|
||||||
|
{
|
||||||
|
if (SelectedApplication != null)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||||
{
|
{
|
||||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
async void Action()
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
||||||
{
|
});
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(Action);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
|
||||||
}
|
|
||||||
|
|
||||||
UserId userId = new((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low);
|
ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName);
|
||||||
SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
|
|
||||||
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenModsDirectory()
|
public void OpenModsDirectory()
|
||||||
{
|
{
|
||||||
ApplicationData selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath();
|
string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath();
|
||||||
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId);
|
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId);
|
||||||
|
|
||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
|
@ -1588,12 +1544,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public void OpenSdModsDirectory()
|
public void OpenSdModsDirectory()
|
||||||
{
|
{
|
||||||
ApplicationData selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
|
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath();
|
string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath();
|
||||||
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId);
|
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId);
|
||||||
|
|
||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
|
@ -1609,25 +1563,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public async void OpenDownloadableContentManager()
|
public async void OpenDownloadableContentManager()
|
||||||
{
|
{
|
||||||
ApplicationData selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName);
|
||||||
{
|
|
||||||
await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenCheatManager()
|
public async void OpenCheatManager()
|
||||||
{
|
{
|
||||||
ApplicationData selection = SelectedApplication;
|
if (SelectedApplication != null)
|
||||||
if (selection != null)
|
|
||||||
{
|
{
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
|
||||||
{
|
|
||||||
await new CheatWindow(VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(desktop.MainWindow);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1641,7 +1587,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
StatusBarProgressMaximum = 0;
|
StatusBarProgressMaximum = 0;
|
||||||
StatusBarProgressValue = 0;
|
StatusBarProgressValue = 0;
|
||||||
|
|
||||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
ReloadGameList?.Invoke();
|
ReloadGameList?.Invoke();
|
||||||
|
@ -1731,18 +1677,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
PrepareLoadScreen();
|
PrepareLoadScreen();
|
||||||
|
|
||||||
RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel);
|
RendererHostControl = new RendererHost();
|
||||||
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
|
|
||||||
{
|
|
||||||
RendererControl.CreateOpenGL();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RendererControl.CreateVulkan();
|
|
||||||
}
|
|
||||||
|
|
||||||
AppHost = new AppHost(
|
AppHost = new AppHost(
|
||||||
RendererControl,
|
RendererHostControl,
|
||||||
InputManager,
|
InputManager,
|
||||||
path,
|
path,
|
||||||
VirtualFileSystem,
|
VirtualFileSystem,
|
||||||
|
@ -1763,8 +1701,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
CanUpdate = false;
|
CanUpdate = false;
|
||||||
LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName;
|
|
||||||
TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
|
LoadHeading = TitleName = titleName;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
|
{
|
||||||
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Application.TitleName);
|
||||||
|
TitleName = AppHost.Device.Application.TitleName;
|
||||||
|
}
|
||||||
|
|
||||||
SwitchToRenderer(startFullscreen);
|
SwitchToRenderer(startFullscreen);
|
||||||
|
|
||||||
|
@ -1783,9 +1727,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
SwitchToGameControl(startFullscreen);
|
SwitchToGameControl(startFullscreen);
|
||||||
|
|
||||||
SetMainContent(RendererControl);
|
SetMainContent(RendererHostControl);
|
||||||
|
|
||||||
RendererControl.Focus();
|
RendererHostControl.Focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1815,14 +1759,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
if (version != null)
|
if (version != null)
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion,
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, version.VersionString);
|
||||||
version.VersionString);
|
|
||||||
|
|
||||||
hasApplet = version.Major > 3;
|
hasApplet = version.Major > 3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
IsAppletMenuActive = hasApplet;
|
IsAppletMenuActive = hasApplet;
|
||||||
|
@ -1853,8 +1796,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
HandleRelaunch();
|
HandleRelaunch();
|
||||||
});
|
});
|
||||||
|
|
||||||
RendererControl.RendererInitialized -= GlRenderer_Created;
|
RendererHostControl.WindowCreated -= RendererHost_Created;
|
||||||
RendererControl = null;
|
RendererHostControl = null;
|
||||||
|
|
||||||
SelectedIcon = null;
|
SelectedIcon = null;
|
||||||
|
|
||||||
|
|
|
@ -21,207 +21,230 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using System.Text;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels;
|
||||||
|
|
||||||
|
public class TitleUpdateViewModel : BaseModel
|
||||||
{
|
{
|
||||||
public class TitleUpdateViewModel : BaseModel
|
public TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
|
public readonly string _titleUpdateJsonPath;
|
||||||
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
|
private ulong _titleId { get; }
|
||||||
|
private string _titleName { get; }
|
||||||
|
|
||||||
|
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||||
|
private AvaloniaList<object> _views = new();
|
||||||
|
private object _selectedUpdate;
|
||||||
|
|
||||||
|
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
||||||
{
|
{
|
||||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
get => _titleUpdates;
|
||||||
public readonly string _titleUpdateJsonPath;
|
set
|
||||||
private VirtualFileSystem _virtualFileSystem { get; }
|
|
||||||
private ulong _titleId { get; }
|
|
||||||
private string _titleName { get; }
|
|
||||||
|
|
||||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
|
||||||
private AvaloniaList<object> _views = new();
|
|
||||||
private object _selectedUpdate;
|
|
||||||
|
|
||||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
|
||||||
{
|
{
|
||||||
get => _titleUpdates;
|
_titleUpdates = value;
|
||||||
set
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvaloniaList<object> Views
|
||||||
|
{
|
||||||
|
get => _views;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_views = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SelectedUpdate
|
||||||
|
{
|
||||||
|
get => _selectedUpdate;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_selectedUpdate = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
|
{
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
|
_titleId = titleId;
|
||||||
|
_titleName = titleName;
|
||||||
|
|
||||||
|
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
||||||
|
|
||||||
|
_titleUpdateWindowData = new TitleUpdateMetadata
|
||||||
{
|
{
|
||||||
_titleUpdates = value;
|
Selected = "",
|
||||||
OnPropertyChanged();
|
Paths = new List<string>()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AvaloniaList<object> Views
|
LoadUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadUpdates()
|
||||||
|
{
|
||||||
|
foreach (string path in _titleUpdateWindowData.Paths)
|
||||||
{
|
{
|
||||||
get => _views;
|
AddUpdate(path);
|
||||||
set
|
|
||||||
{
|
|
||||||
_views = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object SelectedUpdate
|
// NOTE: Save the list again to remove leftovers.
|
||||||
|
Save();
|
||||||
|
|
||||||
|
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
||||||
|
|
||||||
|
SelectedUpdate = selected;
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortUpdates()
|
||||||
|
{
|
||||||
|
var list = TitleUpdates.ToList();
|
||||||
|
|
||||||
|
list.Sort((first, second) =>
|
||||||
{
|
{
|
||||||
get => _selectedUpdate;
|
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
||||||
set
|
|
||||||
{
|
{
|
||||||
_selectedUpdate = value;
|
return -1;
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
}
|
||||||
|
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
Views.Clear();
|
||||||
|
Views.Add(new BaseModel());
|
||||||
|
Views.AddRange(list);
|
||||||
|
|
||||||
|
if (SelectedUpdate == null)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[0];
|
||||||
}
|
}
|
||||||
|
else if (!TitleUpdates.Contains(SelectedUpdate))
|
||||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
if (Views.Count > 1)
|
||||||
|
|
||||||
_titleId = titleId;
|
|
||||||
_titleName = titleName;
|
|
||||||
|
|
||||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
SelectedUpdate = Views[1];
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
|
||||||
|
|
||||||
_titleUpdateWindowData = new TitleUpdateMetadata
|
|
||||||
{
|
|
||||||
Selected = "",
|
|
||||||
Paths = new List<string>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadUpdates()
|
|
||||||
{
|
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
|
||||||
{
|
|
||||||
AddUpdate(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
|
||||||
|
|
||||||
SelectedUpdate = selected;
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortUpdates()
|
|
||||||
{
|
|
||||||
var list = TitleUpdates.ToList();
|
|
||||||
|
|
||||||
list.Sort((first, second) =>
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
Views.Clear();
|
|
||||||
Views.Add(new BaseModel());
|
|
||||||
Views.AddRange(list);
|
|
||||||
|
|
||||||
if (SelectedUpdate == null)
|
|
||||||
{
|
{
|
||||||
SelectedUpdate = Views[0];
|
SelectedUpdate = Views[0];
|
||||||
}
|
}
|
||||||
else if (!TitleUpdates.Contains(SelectedUpdate))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddUpdate(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
||||||
|
{
|
||||||
|
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (Views.Count > 1)
|
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
||||||
|
|
||||||
|
if (controlNca != null && patchNca != null)
|
||||||
{
|
{
|
||||||
SelectedUpdate = Views[1];
|
ApplicationControlProperty controlData = new();
|
||||||
|
|
||||||
|
using UniqueRef<IFile> nacpFile = new();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddUpdate(string path)
|
|
||||||
{
|
|
||||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
|
||||||
{
|
|
||||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
|
||||||
{
|
|
||||||
ApplicationControlProperty controlData = new();
|
|
||||||
|
|
||||||
using UniqueRef<IFile> nacpFile = new();
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path));
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
|
|
||||||
public void RemoveUpdate(TitleUpdateModel update)
|
|
||||||
{
|
|
||||||
TitleUpdates.Remove(update);
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Add()
|
|
||||||
{
|
|
||||||
OpenFileDialog dialog = new()
|
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
Dispatcher.UIThread.Post(async () =>
|
||||||
AllowMultiple = true
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Filters.Add(new FileDialogFilter
|
|
||||||
{
|
|
||||||
Name = "NSP",
|
|
||||||
Extensions = { "nsp" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
{
|
||||||
foreach (string file in files)
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
||||||
{
|
});
|
||||||
AddUpdate(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveUpdate(TitleUpdateModel update)
|
||||||
|
{
|
||||||
|
TitleUpdates.Remove(update);
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Add()
|
||||||
|
{
|
||||||
|
OpenFileDialog dialog = new()
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||||
|
AllowMultiple = true
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.Filters.Add(new FileDialogFilter
|
||||||
|
{
|
||||||
|
Name = "NSP",
|
||||||
|
Extensions = { "nsp" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
|
{
|
||||||
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
AddUpdate(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Paths.Clear();
|
||||||
|
_titleUpdateWindowData.Selected = "";
|
||||||
|
|
||||||
|
foreach (TitleUpdateModel update in TitleUpdates)
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||||
|
|
||||||
|
if (update == SelectedUpdate)
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Selected = update.Path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue