wabbajack/Wabbajack.App.Wpf/View Models/ModListVM.cs

136 lines
5.1 KiB
C#
Raw Normal View History

2021-12-26 21:56:44 +00:00
using ReactiveUI;
using System;
using System.IO;
using System.IO.Compression;
using System.Reactive;
using System.Reactive.Linq;
2021-12-27 05:13:28 +00:00
using System.Threading.Tasks;
2021-12-26 21:56:44 +00:00
using System.Windows.Media.Imaging;
2021-12-27 05:13:28 +00:00
using Microsoft.Extensions.Logging;
using ReactiveUI.Fody.Helpers;
2021-12-26 21:56:44 +00:00
using Wabbajack.Common;
2021-12-27 05:13:28 +00:00
using Wabbajack.DTOs;
using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Installer;
2021-12-30 00:15:37 +00:00
using Wabbajack;
2021-12-27 05:13:28 +00:00
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
2021-12-30 00:15:37 +00:00
using Consts = Wabbajack.Consts;
2021-12-26 21:56:44 +00:00
namespace Wabbajack
{
public class ModListVM : ViewModel
{
2021-12-27 05:13:28 +00:00
private readonly DTOSerializer _dtos;
private readonly ILogger<ModListVM> _logger;
2021-12-26 21:56:44 +00:00
public ModList SourceModList { get; private set; }
public ModlistMetadata SourceModListMetadata { get; private set; }
2021-12-27 05:13:28 +00:00
[Reactive]
public Exception Error { get; set; }
2021-12-26 21:56:44 +00:00
public AbsolutePath ModListPath { get; }
public string Name => SourceModList?.Name;
public string Readme => SourceModList?.Readme;
public string Author => SourceModList?.Author;
public string Description => SourceModList?.Description;
public Uri Website => SourceModList?.Website;
public Version Version => SourceModList?.Version;
public Version WabbajackVersion => SourceModList?.WabbajackVersion;
public bool IsNSFW => SourceModList?.IsNSFW ?? false;
// Image isn't exposed as a direct property, but as an observable.
// This acts as a caching mechanism, as interested parties will trigger it to be created,
// and the cached image will automatically be released when the last interested party is gone.
public IObservable<BitmapImage> ImageObservable { get; }
2021-12-27 05:13:28 +00:00
public ModListVM(ILogger<ModListVM> logger, AbsolutePath modListPath, DTOSerializer dtos)
2021-12-26 21:56:44 +00:00
{
2021-12-27 05:13:28 +00:00
_dtos = dtos;
_logger = logger;
2021-12-26 21:56:44 +00:00
ModListPath = modListPath;
2021-12-27 05:13:28 +00:00
Task.Run(async () =>
2021-12-26 21:56:44 +00:00
{
2021-12-27 05:13:28 +00:00
try
2021-12-26 21:56:44 +00:00
{
2021-12-27 05:13:28 +00:00
SourceModList = await StandardInstaller.LoadFromFile(_dtos, modListPath);
var metadataPath = modListPath.WithExtension(Ext.ModlistMetadataExtension);
if (metadataPath.FileExists())
2021-12-26 21:56:44 +00:00
{
2021-12-27 05:13:28 +00:00
try
{
SourceModListMetadata = await metadataPath.FromJson<ModlistMetadata>();
}
catch (Exception)
{
SourceModListMetadata = null;
}
2021-12-26 21:56:44 +00:00
}
}
2021-12-27 05:13:28 +00:00
catch (Exception ex)
{
Error = ex;
_logger.LogError(ex, "Exception while loading the modlist!");
}
});
2021-12-26 21:56:44 +00:00
ImageObservable = Observable.Return(Unit.Default)
// Download and retrieve bytes on background thread
.ObserveOn(RxApp.TaskpoolScheduler)
.SelectAsync(async filePath =>
{
try
{
2021-12-27 05:13:28 +00:00
await using var fs = ModListPath.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
2021-12-26 21:56:44 +00:00
using var ar = new ZipArchive(fs, ZipArchiveMode.Read);
var ms = new MemoryStream();
var entry = ar.GetEntry("modlist-image.png");
if (entry == null) return default(MemoryStream);
await using var e = entry.Open();
e.CopyTo(ms);
return ms;
}
catch (Exception ex)
{
2021-12-27 05:13:28 +00:00
_logger.LogError(ex, "Exception while caching Mod List image {Name}", Name);
2021-12-26 21:56:44 +00:00
return default(MemoryStream);
}
})
// Create Bitmap image on GUI thread
.ObserveOnGuiThread()
.Select(memStream =>
{
if (memStream == null) return default(BitmapImage);
try
{
return UIUtils.BitmapImageFromStream(memStream);
}
catch (Exception ex)
{
2021-12-27 05:13:28 +00:00
_logger.LogError(ex, "Exception while caching Mod List image {Name}", Name);
2021-12-26 21:56:44 +00:00
return default(BitmapImage);
}
})
// If ever would return null, show WJ logo instead
.Select(x => x ?? ResourceLinks.WabbajackLogoNoText.Value)
.Replay(1)
.RefCount();
}
public void OpenReadme()
{
if (string.IsNullOrEmpty(Readme)) return;
2021-12-27 05:13:28 +00:00
UIUtils.OpenWebsite(new Uri(Readme));
2021-12-26 21:56:44 +00:00
}
public override void Dispose()
{
base.Dispose();
// Just drop reference explicitly, as it's large, so it can be GCed
// Even if someone is holding a stale reference to the VM
SourceModList = null;
}
}
}