aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs')
-rw-r--r--Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs266
1 files changed, 266 insertions, 0 deletions
diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs
new file mode 100644
index 00000000..972ffbc3
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs
@@ -0,0 +1,266 @@
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Threading;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using LibHac.Tools.Fs;
+using LibHac.Tools.FsSystem;
+using LibHac.Tools.FsSystem.NcaUtils;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.Ui.Controls;
+using Ryujinx.Ava.Ui.Models;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.FileSystem;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Path = System.IO.Path;
+
+namespace Ryujinx.Ava.Ui.Windows
+{
+ public partial class DownloadableContentManagerWindow : StyleableWindow
+ {
+ private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
+ private readonly string _downloadableContentJsonPath;
+
+ public VirtualFileSystem VirtualFileSystem { get; }
+ public AvaloniaList<DownloadableContentModel> DownloadableContents { get; set; } = new AvaloniaList<DownloadableContentModel>();
+ public ulong TitleId { get; }
+ public string TitleName { get; }
+
+ public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16"));
+
+ public DownloadableContentManagerWindow()
+ {
+ DataContext = this;
+
+ InitializeComponent();
+
+ Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
+ }
+
+ public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
+ {
+ VirtualFileSystem = virtualFileSystem;
+ TitleId = titleId;
+ TitleName = titleName;
+
+ _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
+
+ try
+ {
+ _downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath);
+ }
+ catch
+ {
+ _downloadableContentContainerList = new List<DownloadableContentContainer>();
+ }
+
+ DataContext = this;
+
+ InitializeComponent();
+
+ Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
+
+ LoadDownloadableContents();
+ }
+
+ private void LoadDownloadableContents()
+ {
+ foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
+ {
+ if (File.Exists(downloadableContentContainer.ContainerPath))
+ {
+ using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
+
+ PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
+
+ VirtualFileSystem.ImportTickets(pfs);
+
+ foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
+ {
+ using var ncaFile = new UniqueRef<IFile>();
+
+ pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+ Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
+ if (nca != null)
+ {
+ DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
+ downloadableContentContainer.ContainerPath,
+ downloadableContentNca.FullPath,
+ downloadableContentNca.Enabled));
+ }
+ }
+ }
+ }
+
+ // NOTE: Save the list again to remove leftovers.
+ Save();
+ }
+
+ private Nca TryCreateNca(IStorage ncaStorage, string containerPath)
+ {
+ try
+ {
+ return new Nca(VirtualFileSystem.KeySet, ncaStorage);
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, containerPath));
+ });
+ }
+
+ return null;
+ }
+
+ private async Task AddDownloadableContent(string path)
+ {
+ if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
+ {
+ return;
+ }
+
+ using (FileStream containerFile = File.OpenRead(path))
+ {
+ PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
+ bool containsDownloadableContent = false;
+
+ VirtualFileSystem.ImportTickets(pfs);
+
+ 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.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
+ {
+ break;
+ }
+
+ DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
+
+ containsDownloadableContent = true;
+ }
+ }
+
+ if (!containsDownloadableContent)
+ {
+ await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
+ }
+ }
+ }
+
+ private void RemoveDownloadableContents(bool removeSelectedOnly = false)
+ {
+ if (removeSelectedOnly)
+ {
+ DownloadableContents.RemoveAll(DownloadableContents.Where(x => x.Enabled).ToList());
+ }
+ else
+ {
+ DownloadableContents.Clear();
+ }
+ }
+
+ public void RemoveSelected()
+ {
+ RemoveDownloadableContents(true);
+ }
+
+ public void RemoveAll()
+ {
+ RemoveDownloadableContents();
+ }
+
+ public async void Add()
+ {
+ OpenFileDialog dialog = new OpenFileDialog()
+ {
+ Title = LocaleManager.Instance["SelectDlcDialogTitle"],
+ AllowMultiple = true
+ };
+
+ dialog.Filters.Add(new FileDialogFilter
+ {
+ Name = "NSP",
+ Extensions = { "nsp" }
+ });
+
+ string[] files = await dialog.ShowAsync(this);
+
+ if (files != null)
+ {
+ foreach (string file in files)
+ {
+ await AddDownloadableContent(file);
+ }
+ }
+ }
+
+ public void Save()
+ {
+ _downloadableContentContainerList.Clear();
+
+ DownloadableContentContainer container = default;
+
+ foreach (DownloadableContentModel downloadableContent in DownloadableContents)
+ {
+ if (container.ContainerPath != downloadableContent.ContainerPath)
+ {
+ if (!string.IsNullOrWhiteSpace(container.ContainerPath))
+ {
+ _downloadableContentContainerList.Add(container);
+ }
+
+ container = new DownloadableContentContainer
+ {
+ ContainerPath = downloadableContent.ContainerPath,
+ DownloadableContentNcaList = new List<DownloadableContentNca>()
+ };
+ }
+
+ container.DownloadableContentNcaList.Add(new DownloadableContentNca
+ {
+ Enabled = downloadableContent.Enabled,
+ TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
+ FullPath = downloadableContent.FullPath
+ });
+ }
+
+ if (!string.IsNullOrWhiteSpace(container.ContainerPath))
+ {
+ _downloadableContentContainerList.Add(container);
+ }
+
+ using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough))
+ {
+ downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
+ }
+ }
+
+ public void SaveAndClose()
+ {
+ Save();
+ Close();
+ }
+ }
+} \ No newline at end of file