From 38bb43bba362ca382d1dd489eff4a6ddcac6669e Mon Sep 17 00:00:00 2001 From: Macocian Alexandru Victor Date: Tue, 11 Jun 2024 14:45:06 +0200 Subject: [PATCH] Daybreak paths now fall back to root Daybreak folder (Closes #716) (#756) * Daybreak paths now fall back to root Daybreak folder (Closes #716) * Fix some more paths --- .../Configuration/ProjectConfiguration.cs | 3 +- .../Controls/ChromiumBrowserWrapper.xaml.cs | 2 +- Daybreak/Services/DSOAL/DSOALService.cs | 19 ++++++---- .../Services/DirectSong/DirectSongService.cs | 20 ++++++++-- .../GuildWarsExecutableManager.cs | 18 ++++----- Daybreak/Services/GWCA/GWCAInjector.cs | 9 +++-- Daybreak/Services/IconRetrieve/IconCache.cs | 11 ++++-- Daybreak/Services/Metrics/MetricsService.cs | 9 ++--- Daybreak/Services/Options/OptionsManager.cs | 5 ++- Daybreak/Services/Plugins/PluginsService.cs | 14 +++---- Daybreak/Services/ReShade/ReShadeService.cs | 38 ++++++++++++++----- .../Screenshots/OnlinePictureClient.cs | 7 +++- .../Services/SevenZip/SevenZipExtractor.cs | 5 ++- .../Startup/Actions/RenameInstallerAction.cs | 10 +++-- Daybreak/Services/Toolbox/ToolboxService.cs | 4 +- .../UBlockOrigin/UBlockOriginService.cs | 10 +++-- Daybreak/Services/UMod/UModService.cs | 13 ++++--- .../Services/Updater/ApplicationUpdater.cs | 18 ++++++--- Daybreak/Utils/PathUtils.cs | 21 ++++++++++ .../SimplePlugin/SimplePlugin.csproj | 2 +- 20 files changed, 164 insertions(+), 74 deletions(-) create mode 100644 Daybreak/Utils/PathUtils.cs diff --git a/Daybreak/Configuration/ProjectConfiguration.cs b/Daybreak/Configuration/ProjectConfiguration.cs index 66899ebb..99b22e39 100644 --- a/Daybreak/Configuration/ProjectConfiguration.cs +++ b/Daybreak/Configuration/ProjectConfiguration.cs @@ -88,6 +88,7 @@ using Daybreak.Services.ApplicationArguments; using Daybreak.Services.ApplicationArguments.ArgumentHandling; using Daybreak.Services.Window; using Daybreak.Launch; +using Daybreak.Utils; namespace Daybreak.Configuration; @@ -135,7 +136,7 @@ public class ProjectConfiguration : PluginConfigurationBase services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); services.AddSingleton(sp => sp.GetRequiredService().Cast()); - services.AddSingleton(sp => new LiteDatabase("Daybreak.db")); + services.AddSingleton(sp => new LiteDatabase(PathUtils.GetAbsolutePathFromRoot("Daybreak.db"))); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/Daybreak/Controls/ChromiumBrowserWrapper.xaml.cs b/Daybreak/Controls/ChromiumBrowserWrapper.xaml.cs index ce76fc96..39a151f9 100644 --- a/Daybreak/Controls/ChromiumBrowserWrapper.xaml.cs +++ b/Daybreak/Controls/ChromiumBrowserWrapper.xaml.cs @@ -172,7 +172,7 @@ public partial class ChromiumBrowserWrapper : UserControl return; } - CoreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", new CoreWebView2EnvironmentOptions + CoreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, PathUtils.GetAbsolutePathFromRoot("BrowserData"), new CoreWebView2EnvironmentOptions { EnableTrackingPrevention = true, AllowSingleSignOnUsingOSPrimaryAccount = true, diff --git a/Daybreak/Services/DSOAL/DSOALService.cs b/Daybreak/Services/DSOAL/DSOALService.cs index 6d2435d6..7aeadaac 100644 --- a/Daybreak/Services/DSOAL/DSOALService.cs +++ b/Daybreak/Services/DSOAL/DSOALService.cs @@ -6,6 +6,7 @@ using Daybreak.Services.Downloads; using Daybreak.Services.Notifications; using Daybreak.Services.Privilege; using Daybreak.Services.Registry; +using Daybreak.Utils; using Daybreak.Views; using Microsoft.Extensions.Logging; using System; @@ -29,13 +30,15 @@ internal sealed class DSOALService : IDSOALService public const string DSOALFixRegistryKey = "DSOAL/FixSymbolicLink"; private const string DownloadUrl = "https://github.com/ChthonVII/dsoal-GW1/releases/download/r420%2Bgw1_rev1/dsoal-GW1_r420+gw1_rev1.zip"; private const string ArchiveName = "dsoal-GW1_r420+gw1_rev1.zip"; - private const string DSOALDirectory = "DSOAL"; + private const string DSOALDirectorySubPath = "DSOAL"; private const string HRTFArchiveName = "HRTF_OAL_1.19.0.zip"; private const string DsoundDll = "dsound.dll"; private const string DSOALAldrvDll = "dsoal-aldrv.dll"; private const string AlsoftIni = "alsoft.ini"; private const string OpenAlDirectory = "openal"; + private static readonly string DSOALDirectory = PathUtils.GetAbsolutePathFromRoot(DSOALDirectorySubPath); + private readonly INotificationService notificationService; private readonly IRegistryService registryService; private readonly IPrivilegeManager privilegeManager; @@ -196,12 +199,12 @@ internal sealed class DSOALService : IDSOALService return false; } - Directory.CreateSymbolicLink(openalPath, Path.GetFullPath(DSOALDirectory)); + Directory.CreateSymbolicLink(openalPath, DSOALDirectory); return true; } var fi = new FileInfo(openalPath); - var desiredPath = Path.GetFullPath(DSOALDirectory); + var desiredPath = DSOALDirectory; if (fi.LinkTarget == desiredPath) { return true; @@ -215,16 +218,16 @@ internal sealed class DSOALService : IDSOALService } Directory.Delete(openalPath); - Directory.CreateSymbolicLink(openalPath, Path.GetFullPath(DSOALDirectory)); + Directory.CreateSymbolicLink(openalPath, DSOALDirectory); return true; } private void ExtractFiles() { - ZipFile.ExtractToDirectory(ArchiveName, Path.Combine(Directory.GetCurrentDirectory(), DSOALDirectory), true); - ZipFile.ExtractToDirectory(Path.Combine(DSOALDirectory, HRTFArchiveName), Path.Combine(Directory.GetCurrentDirectory(), DSOALDirectory), true); + ZipFile.ExtractToDirectory(ArchiveName, DSOALDirectory, true); + ZipFile.ExtractToDirectory(Path.Combine(DSOALDirectory, HRTFArchiveName), DSOALDirectory, true); var options = this.options.Value; - options.Path = Path.GetFullPath(DSOALDirectory); + options.Path = DSOALDirectory; this.options.UpdateOption(); File.Delete(ArchiveName); File.Delete(Path.Combine(DSOALDirectory, HRTFArchiveName)); @@ -240,6 +243,6 @@ internal sealed class DSOALService : IDSOALService Directory.Delete(openalPath); } - Directory.CreateSymbolicLink(openalPath, Path.GetFullPath(DSOALDirectory)); + Directory.CreateSymbolicLink(openalPath, DSOALDirectory); } } diff --git a/Daybreak/Services/DirectSong/DirectSongService.cs b/Daybreak/Services/DirectSong/DirectSongService.cs index 60d3efcb..835c7173 100644 --- a/Daybreak/Services/DirectSong/DirectSongService.cs +++ b/Daybreak/Services/DirectSong/DirectSongService.cs @@ -1,14 +1,13 @@ using Daybreak.Configuration.Options; -using Daybreak.Models; using Daybreak.Models.Mods; using Daybreak.Models.Progress; using Daybreak.Services.Downloads; using Daybreak.Services.Notifications; using Daybreak.Services.Privilege; using Daybreak.Services.SevenZip; +using Daybreak.Utils; using Daybreak.Views; using Microsoft.Extensions.Logging; -using System; using System.Collections.Generic; using System.Configuration; using System.Core.Extensions; @@ -23,11 +22,13 @@ internal sealed class DirectSongService : IDirectSongService { private const string DownloadUrl = "https://guildwarslegacy.com/DirectSong.7z"; private const string RegistryEditorName = "RegisterDirectSongDirectory.exe"; - private const string InstallationDirectory = "DirectSong"; + private const string InstallationDirectorySubPath = "DirectSong"; private const string DestinationZipFile = "DirectSong.7z"; private const string WMVCOREDll = "WMVCORE.DLL"; private const string DsGuildwarsDll = "ds_GuildWars.dll"; + private readonly static string InstallationDirectory = PathUtils.GetAbsolutePathFromRoot(InstallationDirectorySubPath); + private readonly INotificationService notificationService; private readonly IPrivilegeManager privilegeManager; private readonly ISevenZipExtractor sevenZipExtractor; @@ -123,7 +124,8 @@ internal sealed class DirectSongService : IDirectSongService public Task SetupDirectSong(DirectSongInstallationStatus directSongInstallationStatus, CancellationToken cancellationToken) { - if (this.InstallationTask is not null) + if (this.InstallationTask is not null && + !this.InstallationTask.IsCompleted) { return this.InstallationTask; } @@ -144,12 +146,16 @@ internal sealed class DirectSongService : IDirectSongService if (this.IsInstalled) { scopedLogger.LogInformation("Already installed"); + this.InstallationTask = default; + this.CachedInstallationStatus = default; return true; } if (!this.privilegeManager.AdminPrivileges) { this.privilegeManager.RequestAdminPrivileges("DirectSong installation requires Administrator privileges in order to set up the registry entries"); + this.InstallationTask = default; + this.CachedInstallationStatus = default; return false; } @@ -164,6 +170,8 @@ internal sealed class DirectSongService : IDirectSongService if (!await this.downloadService.DownloadFile(DownloadUrl, destinationPath, directSongInstallationStatus, cancellationToken)) { scopedLogger.LogError("Download failed. Check logs"); + this.InstallationTask = default; + this.CachedInstallationStatus = default; return false; } } @@ -176,6 +184,8 @@ internal sealed class DirectSongService : IDirectSongService }, cancellationToken)) { scopedLogger.LogError("Extraction failed"); + this.InstallationTask = default; + this.CachedInstallationStatus = default; return false; } @@ -184,6 +194,8 @@ internal sealed class DirectSongService : IDirectSongService if (!await RunRegisterDirectSongDirectory(cancellationToken)) { scopedLogger.LogError("Failed to set up registry entries"); + this.InstallationTask = default; + this.CachedInstallationStatus = default; return false; } diff --git a/Daybreak/Services/ExecutableManagement/GuildWarsExecutableManager.cs b/Daybreak/Services/ExecutableManagement/GuildWarsExecutableManager.cs index 61a63081..e38d66a4 100644 --- a/Daybreak/Services/ExecutableManagement/GuildWarsExecutableManager.cs +++ b/Daybreak/Services/ExecutableManagement/GuildWarsExecutableManager.cs @@ -15,7 +15,7 @@ namespace Daybreak.Services.ExecutableManagement; internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, IApplicationLifetimeService { private readonly static TimeSpan ExecutableVerificationLatency = TimeSpan.FromSeconds(5); - private readonly static object Lock = new(); + private readonly static SemaphoreSlim ExecutablesSemaphore = new(1, 1); private readonly CancellationTokenSource cancellationTokenSource = new(); private readonly ILiveUpdateableOptions liveUpdateableOptions; @@ -31,10 +31,10 @@ internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, public IEnumerable GetExecutableList() { - while (!Monitor.TryEnter(Lock)) { } + ExecutablesSemaphore.Wait(); var list = this.liveUpdateableOptions.Value.ExecutablePaths.Where(this.IsValidExecutable).ToList(); - Monitor.Exit(Lock); + ExecutablesSemaphore.Release(); return list; } @@ -42,7 +42,7 @@ internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, public void AddExecutable(string executablePath) { var fullPath = Path.GetFullPath(executablePath); - while (!Monitor.TryEnter(Lock)) { } + ExecutablesSemaphore.Wait(); var list = this.liveUpdateableOptions.Value.ExecutablePaths; if (list.None(e => e == executablePath)) @@ -52,13 +52,13 @@ internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, this.liveUpdateableOptions.Value.ExecutablePaths = list; this.liveUpdateableOptions.UpdateOption(); - Monitor.Exit(Lock); + ExecutablesSemaphore.Release(); } public void RemoveExecutable(string executablePath) { var fullPath = Path.GetFullPath(executablePath); - while (!Monitor.TryEnter(Lock)) { } + ExecutablesSemaphore.Wait(); var list = this.liveUpdateableOptions.Value.ExecutablePaths; if (list.Any(e => e == executablePath)) @@ -68,7 +68,7 @@ internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, this.liveUpdateableOptions.Value.ExecutablePaths = list; this.liveUpdateableOptions.UpdateOption(); - Monitor.Exit(Lock); + ExecutablesSemaphore.Release(); } public bool IsValidExecutable(string executablePath) @@ -90,7 +90,7 @@ internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, var scopedLogger = this.logger.CreateScopedLogger(nameof(this.VerifyExecutables), string.Empty); while (!cancellationToken.IsCancellationRequested) { - while (!Monitor.TryEnter(Lock)) { } + await ExecutablesSemaphore.WaitAsync(cancellationToken); var executables = this.liveUpdateableOptions.Value.ExecutablePaths; var deletedExecutable = false; @@ -114,7 +114,7 @@ internal sealed class GuildWarsExecutableManager : IGuildWarsExecutableManager, this.liveUpdateableOptions.UpdateOption(); } - Monitor.Exit(Lock); + ExecutablesSemaphore.Release(); await Task.Delay(ExecutableVerificationLatency, cancellationToken); } } diff --git a/Daybreak/Services/GWCA/GWCAInjector.cs b/Daybreak/Services/GWCA/GWCAInjector.cs index a4cdce69..ec4e3a17 100644 --- a/Daybreak/Services/GWCA/GWCAInjector.cs +++ b/Daybreak/Services/GWCA/GWCAInjector.cs @@ -3,12 +3,14 @@ using Daybreak.Models.GWCA; using Daybreak.Models.Mods; using Daybreak.Services.Injection; using Daybreak.Services.Notifications; +using Daybreak.Utils; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Configuration; using System.Core.Extensions; using System.Extensions; -using System.Linq; +using System.IO; +using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -17,7 +19,7 @@ namespace Daybreak.Services.GWCA; internal sealed class GWCAInjector : IGWCAInjector { private const int MaxRetries = 10; - private const string ModulePath = "GWCA/Daybreak.GWCA.dll"; + private const string ModuleSubPath = "GWCA/Daybreak.GWCA.dll"; private readonly INotificationService notificationService; private readonly IGWCAClient gwcaClient; @@ -65,8 +67,9 @@ internal sealed class GWCAInjector : IGWCAInjector public async Task OnGuildWarsCreated(GuildWarsCreatedContext guildWarsCreatedContext, CancellationToken cancellationToken) { + var modulePath = PathUtils.GetAbsolutePathFromRoot(ModuleSubPath); var scopedLogger = this.logger.CreateScopedLogger(nameof(this.OnGuildWarsCreated), guildWarsCreatedContext.ApplicationLauncherContext.ExecutablePath); - if (!await this.injector.Inject(guildWarsCreatedContext.ApplicationLauncherContext.Process, ModulePath, cancellationToken)) + if (!await this.injector.Inject(guildWarsCreatedContext.ApplicationLauncherContext.Process, modulePath, cancellationToken)) { scopedLogger.LogError("Unable to inject GWCA plugin into Guild Wars. Check above error messages for details"); this.notificationService.NotifyError( diff --git a/Daybreak/Services/IconRetrieve/IconCache.cs b/Daybreak/Services/IconRetrieve/IconCache.cs index a60b618f..93807ad5 100644 --- a/Daybreak/Services/IconRetrieve/IconCache.cs +++ b/Daybreak/Services/IconRetrieve/IconCache.cs @@ -1,5 +1,6 @@ using Daybreak.Configuration.Options; using Daybreak.Models.Guildwars; +using Daybreak.Utils; using HtmlAgilityPack; using Microsoft.Extensions.Logging; using System; @@ -8,6 +9,7 @@ using System.Core.Extensions; using System.Extensions; using System.IO; using System.Net.Http; +using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -18,8 +20,9 @@ internal sealed class IconCache : IIconCache private const string HighResolutionGalleryUrl = $"https://wiki.guildwars.com/wiki/File:{NamePlaceholder}_(large).jpg"; private const string WikiUrl = "https://wiki.guildwars.com"; private const string NamePlaceholder = "[NAME]"; - private const string IconsDirectoryName = "Icons"; - private const string IconsLocation = $"{IconsDirectoryName}/{NamePlaceholder}.jpg"; + private const string IconsDirectoryNameSubpath = "Icons"; + private readonly static string IconsDirectory = PathUtils.GetAbsolutePathFromRoot(IconsDirectoryNameSubpath); + private readonly static string IconsLocation = PathUtils.GetAbsolutePathFromRoot(IconsDirectoryNameSubpath, $"{NamePlaceholder}.jpg"); private readonly SemaphoreSlim diskSemaphore = new(1, 1); private readonly IHttpClient httpClient; @@ -35,9 +38,9 @@ internal sealed class IconCache : IIconCache this.options = options.ThrowIfNull(); this.logger = logger.ThrowIfNull(); - if (Directory.Exists(IconsDirectoryName) is false) + if (Directory.Exists(IconsDirectory) is false) { - Directory.CreateDirectory(IconsDirectoryName); + Directory.CreateDirectory(IconsDirectory); } } diff --git a/Daybreak/Services/Metrics/MetricsService.cs b/Daybreak/Services/Metrics/MetricsService.cs index d5d8abdb..674e8212 100644 --- a/Daybreak/Services/Metrics/MetricsService.cs +++ b/Daybreak/Services/Metrics/MetricsService.cs @@ -14,7 +14,8 @@ internal sealed class MetricsService : IMetricsService, IDisposable private const string MetricsNamespace = "Daybreak"; private const string MetricsStoreName = "MetricsStore"; - private readonly object metricStoreLock = new object(); + private static readonly SemaphoreSlim MetricsSemaphore = new(1, 1); + private readonly MeterListener meterListener; private readonly Meter daybreakMeter; private readonly ConcurrentDictionary> metricStore = new(); @@ -116,9 +117,7 @@ internal sealed class MetricsService : IMetricsService, IDisposable { MetricSet? newMetricSet = default; RecordedMetric? newRecordedMetric = default; - while(Monitor.TryEnter(this.metricStoreLock) is false) - { - } + MetricsSemaphore.Wait(); if (measurement is null) { @@ -141,7 +140,7 @@ internal sealed class MetricsService : IMetricsService, IDisposable } newRecordedMetric = new RecordedMetric { Instrument = instrument, Metric = metric }; - Monitor.Exit(this.metricStoreLock); + MetricsSemaphore.Release(); if (newMetricSet is not null) { diff --git a/Daybreak/Services/Options/OptionsManager.cs b/Daybreak/Services/Options/OptionsManager.cs index 15a573cb..89f2c0ae 100644 --- a/Daybreak/Services/Options/OptionsManager.cs +++ b/Daybreak/Services/Options/OptionsManager.cs @@ -1,4 +1,5 @@ using Daybreak.Attributes; +using Daybreak.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -14,7 +15,9 @@ namespace Daybreak.Services.Options; internal sealed class OptionsManager : IOptionsManager, IOptionsProducer, IOptionsUpdateHook, IOptionsProvider { - private const string OptionsFile = "Daybreak.options"; + private const string OptionsFileSubPath = "Daybreak.options"; + + private static readonly string OptionsFile = PathUtils.GetAbsolutePathFromRoot(OptionsFileSubPath); private readonly Dictionary optionsCache = []; private readonly Dictionary> optionsUpdateHooks = []; diff --git a/Daybreak/Services/Plugins/PluginsService.cs b/Daybreak/Services/Plugins/PluginsService.cs index ecff61cd..b16f5b59 100644 --- a/Daybreak/Services/Plugins/PluginsService.cs +++ b/Daybreak/Services/Plugins/PluginsService.cs @@ -11,6 +11,7 @@ using Daybreak.Services.Plugins.Resolvers; using Daybreak.Services.Plugins.Validators; using Daybreak.Services.Startup; using Daybreak.Services.Updater.PostUpdate; +using Daybreak.Utils; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Plumsy; @@ -33,10 +34,10 @@ namespace Daybreak.Services.Plugins; internal sealed class PluginsService : IPluginsService { private const string DllExtension = ".dll"; - private const string PluginsDirectory = "Plugins"; - - private static readonly object Lock = new(); + private const string PluginsDirectorySubPath = "Plugins"; + private static readonly string PluginsDirectory = PathUtils.GetAbsolutePathFromRoot(PluginsDirectorySubPath); + private readonly SemaphoreSlim pluginsSemaphore = new(1, 1); private readonly List loadedPlugins = []; private readonly ILiveUpdateableOptions liveUpdateableOptions; private readonly ILogger logger; @@ -109,10 +110,9 @@ internal sealed class PluginsService : IPluginsService browserExtensionsProducer.ThrowIfNull(); argumentHandlerProducer.ThrowIfNull(); - while (!Monitor.TryEnter(Lock)) { } - + this.pluginsSemaphore.Wait(); var scopedLogger = this.logger.CreateScopedLogger(nameof(this.LoadPlugins), string.Empty); - var pluginsPath = Path.GetFullPath(PluginsDirectory); + var pluginsPath = PluginsDirectory; if (!Directory.Exists(pluginsPath)) { scopedLogger.LogInformation("Creating plugins folder"); @@ -196,7 +196,7 @@ internal sealed class PluginsService : IPluginsService } } - Monitor.Exit(Lock); + this.pluginsSemaphore.Release(); } public async Task AddPlugin(string pathToPlugin) diff --git a/Daybreak/Services/ReShade/ReShadeService.cs b/Daybreak/Services/ReShade/ReShadeService.cs index 1995c7ce..ce8f1d3c 100644 --- a/Daybreak/Services/ReShade/ReShadeService.cs +++ b/Daybreak/Services/ReShade/ReShadeService.cs @@ -9,6 +9,7 @@ using Daybreak.Services.Notifications; using Daybreak.Services.ReShade.Notifications; using Daybreak.Services.ReShade.Utils; using Daybreak.Services.Scanner; +using Daybreak.Utils; using HtmlAgilityPack; using IniParser.Parser; using Ionic.Zip; @@ -18,6 +19,7 @@ using System; using System.Collections.Generic; using System.Configuration; using System.Core.Extensions; +using System.Data; using System.Extensions; using System.IO; using System.Linq; @@ -29,9 +31,10 @@ using System.Windows.Extensions.Services; namespace Daybreak.Services.ReShade; internal sealed class ReShadeService : IReShadeService, IApplicationLifetimeService { + private const string DesiredVersion = "4.9.1"; private const string PackagesIniUrl = "https://raw.githubusercontent.com/crosire/reshade-shaders/list/EffectPackages.ini"; private const string ReShadeHomepageUrl = "https://reshade.me"; - private const string ReShadePath = "ReShade"; + private const string ReShadeSubPath = "ReShade"; private const string ReShadeInstallerName = "ReShade_Setup.exe"; private const string DllName = "ReShade32.dll"; private const string ConfigIni = "ReShade.ini"; @@ -39,11 +42,13 @@ internal sealed class ReShadeService : IReShadeService, IApplicationLifetimeServ private const string ReShadeLog = "ReShade.log"; private const string PresetsFolder = "reshade-shaders"; - private static readonly string ReShadeDllPath = Path.Combine(Path.GetFullPath(ReShadePath), DllName); - private static readonly string ConfigIniPath = Path.Combine(Path.GetFullPath(ReShadePath), ConfigIni); - private static readonly string ReShadePresetPath = Path.Combine(Path.GetFullPath(ReShadePath), ReShadePreset); - private static readonly string ReShadeLogPath = Path.Combine(Path.GetFullPath(ReShadePath), ReShadeLog); - private static readonly string SourcePresetsFolderPath = Path.Combine(Path.GetFullPath(ReShadePath), PresetsFolder); + private static readonly string ReShadePath = PathUtils.GetAbsolutePathFromRoot(ReShadeSubPath); + + private static readonly string ReShadeDllPath = Path.Combine(ReShadePath, DllName); + private static readonly string ConfigIniPath = Path.Combine(ReShadePath, ConfigIni); + private static readonly string ReShadePresetPath = Path.Combine(ReShadePath, ReShadePreset); + private static readonly string ReShadeLogPath = Path.Combine(ReShadePath, ReShadeLog); + private static readonly string SourcePresetsFolderPath = Path.Combine(ReShadePath, PresetsFolder); private static readonly string[] TextureExtensions = [".png", ".jpg", ".jpeg"]; private static readonly string[] FxExtensions = [".fx",]; private static readonly string[] FxHeaderExtensions = [".fxh"]; @@ -222,7 +227,7 @@ internal sealed class ReShadeService : IReShadeService, IApplicationLifetimeServ var scopedLogger = this.logger.CreateScopedLogger(nameof(this.SetupReShade), string.Empty); scopedLogger.LogInformation("Retrieving ReShade homepage"); reShadeInstallationStatus.CurrentStep = ReShadeInstallationStatus.RetrievingLatestVersionUrl; - var downloadUrl = await this.GetLatestDownloadUrl(cancellationToken); + var downloadUrl = $"{ReShadeHomepageUrl}/downloads/ReShade_Setup_4.9.1.exe"; if (downloadUrl?.IsNullOrWhiteSpace() is not false) { scopedLogger.LogError("Unable to retrieve latest download url"); @@ -636,14 +641,29 @@ internal sealed class ReShadeService : IReShadeService, IApplicationLifetimeServ return; } - if (version.CompareTo(installedVersion) > 0) + if (!Models.Versioning.Version.TryParse(DesiredVersion, out var desiredVersion)) { - scopedLogger.LogInformation($"Found an update for ReShade. Current version: {installedVersion}. Latest version: {version}"); + scopedLogger.LogError("Unable to parse desired version"); + return; + } + + if (installedVersion.CompareTo(desiredVersion) != 0) + { + scopedLogger.LogInformation($"Current ReShade version does not match desired version {desiredVersion}. Fetching desired version"); if (await this.SetupReShade(new ReShadeInstallationStatus(), CancellationToken.None) is not true) { scopedLogger.LogError("Failed to update ReShade"); } } + + //if (version.CompareTo(installedVersion) > 0) + //{ + // scopedLogger.LogInformation($"Found an update for ReShade. Current version: {installedVersion}. Latest version: {version}"); + // if (await this.SetupReShade(new ReShadeInstallationStatus(), CancellationToken.None) is not true) + // { + // scopedLogger.LogError("Failed to update ReShade"); + // } + //} } /// diff --git a/Daybreak/Services/Screenshots/OnlinePictureClient.cs b/Daybreak/Services/Screenshots/OnlinePictureClient.cs index 37a82e45..b1107cae 100644 --- a/Daybreak/Services/Screenshots/OnlinePictureClient.cs +++ b/Daybreak/Services/Screenshots/OnlinePictureClient.cs @@ -3,6 +3,7 @@ using Daybreak.Models.Guildwars; using Daybreak.Services.Images; using Daybreak.Services.Scanner; using Daybreak.Services.Screenshots.Models; +using Daybreak.Utils; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -12,6 +13,7 @@ using System.Extensions; using System.IO; using System.Linq; using System.Net.Http; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows.Media; @@ -22,7 +24,10 @@ internal sealed class OnlinePictureClient : IOnlinePictureClient { private const string CloudFlareCookieValue = "fcfd523b2470336531e47baff3d2c2d6a0e2412a.1689426482.1"; private const string CloudFlareCookieKey = "wschkid"; - private const string CacheFolder = "ImageCache"; + private const string CacheFolderSubPath = "ImageCache"; + + private static readonly string CacheFolder = PathUtils.GetAbsolutePathFromRoot(CacheFolderSubPath); + private readonly IImageCache imageCache; private readonly IGuildwarsMemoryCache guildwarsMemoryCache; private readonly IHttpClient httpClient; diff --git a/Daybreak/Services/SevenZip/SevenZipExtractor.cs b/Daybreak/Services/SevenZip/SevenZipExtractor.cs index 415606cf..e8a15191 100644 --- a/Daybreak/Services/SevenZip/SevenZipExtractor.cs +++ b/Daybreak/Services/SevenZip/SevenZipExtractor.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Daybreak.Utils; +using Microsoft.Extensions.Logging; using System; using System.Core.Extensions; using System.Diagnostics; @@ -28,7 +29,7 @@ internal sealed class SevenZipExtractor : ISevenZipExtractor { StartInfo = new ProcessStartInfo { - FileName = ExtractorExeName, + FileName = PathUtils.GetAbsolutePathFromRoot(ExtractorExeName), Arguments = $"\"{sourceFile}\" \"{destinationDirectory}\"", RedirectStandardOutput = true, CreateNoWindow = true, diff --git a/Daybreak/Services/Startup/Actions/RenameInstallerAction.cs b/Daybreak/Services/Startup/Actions/RenameInstallerAction.cs index 7c59c2c3..24d46456 100644 --- a/Daybreak/Services/Startup/Actions/RenameInstallerAction.cs +++ b/Daybreak/Services/Startup/Actions/RenameInstallerAction.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Daybreak.Utils; +using Microsoft.Extensions.Logging; using System.Core.Extensions; using System.IO; @@ -6,8 +7,11 @@ namespace Daybreak.Services.Startup.Actions; internal sealed class RenameInstallerAction : StartupActionBase { - private const string TemporaryInstallerFileName = "Daybreak.Installer.Temp.exe"; - private const string InstallerFileName = "Daybreak.Installer.exe"; + private const string TemporaryInstallerFileNameSubPath = "Daybreak.Installer.Temp.exe"; + private const string InstallerFileNameSubPath = "Daybreak.Installer.exe"; + + private static readonly string TemporaryInstallerFileName = PathUtils.GetAbsolutePathFromRoot(TemporaryInstallerFileNameSubPath); + private static readonly string InstallerFileName = PathUtils.GetAbsolutePathFromRoot(InstallerFileNameSubPath); private readonly ILogger logger; diff --git a/Daybreak/Services/Toolbox/ToolboxService.cs b/Daybreak/Services/Toolbox/ToolboxService.cs index 06d81dda..afc574c6 100644 --- a/Daybreak/Services/Toolbox/ToolboxService.cs +++ b/Daybreak/Services/Toolbox/ToolboxService.cs @@ -8,6 +8,7 @@ using Daybreak.Services.Notifications; using Daybreak.Services.Scanner; using Daybreak.Services.Toolbox.Models; using Daybreak.Services.Toolbox.Utilities; +using Daybreak.Utils; using Microsoft.Extensions.Logging; using Microsoft.Win32; using System; @@ -24,8 +25,9 @@ namespace Daybreak.Services.Toolbox; internal sealed class ToolboxService : IToolboxService { - private const string ToolboxDestinationDirectory = "GWToolbox"; + private const string ToolboxDestinationDirectorySubPath = "GWToolbox"; + private static readonly string ToolboxDestinationDirectoryPath = PathUtils.GetAbsolutePathFromRoot(ToolboxDestinationDirectorySubPath); private static readonly string UsualToolboxLocation = Path.GetFullPath( Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "GWToolboxpp", "GWToolboxdll.dll")); diff --git a/Daybreak/Services/UBlockOrigin/UBlockOriginService.cs b/Daybreak/Services/UBlockOrigin/UBlockOriginService.cs index 63b6b06a..ef8a1cf6 100644 --- a/Daybreak/Services/UBlockOrigin/UBlockOriginService.cs +++ b/Daybreak/Services/UBlockOrigin/UBlockOriginService.cs @@ -16,6 +16,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Daybreak.Services.Notifications; using Daybreak.Services.Browser; +using Daybreak.Utils; namespace Daybreak.Services.UBlockOrigin; public sealed class UBlockOriginService : IBrowserExtension @@ -23,10 +24,11 @@ public sealed class UBlockOriginService : IBrowserExtension private const string TagPlaceholder = "[TAG_PLACEHOLDER]"; private const string ReleaseUrl = "https://github.com/gorhill/uBlock/releases/download/[TAG_PLACEHOLDER]/uBlock0_[TAG_PLACEHOLDER].chromium.zip"; private const string ReleasesUrl = "https://api.github.com/repos/gorhill/uBlock/git/refs/tags"; - private const string InstallationPath = "BrowserExtensions"; + private const string InstallationSubPath = "BrowserExtensions"; private const string ZipName = "ublock.chromium.zip"; private const string InstallationFolderName = "uBlock0.chromium"; + private static readonly string InstallationPath = PathUtils.GetAbsolutePathFromRoot(InstallationSubPath); private static readonly SemaphoreSlim SemaphoreSlim = new(1); private static volatile bool VersionUpToDate; @@ -111,7 +113,7 @@ public sealed class UBlockOriginService : IBrowserExtension } using var zipFile = ZipFile.Read(zipFilePath); - zipFile.ExtractAll(Path.GetFullPath(InstallationPath), ExtractExistingFileAction.OverwriteSilently); + zipFile.ExtractAll(InstallationPath, ExtractExistingFileAction.OverwriteSilently); zipFile.Dispose(); File.Delete(zipFilePath); VersionUpToDate = true; @@ -125,7 +127,7 @@ public sealed class UBlockOriginService : IBrowserExtension var scopedLogger = this.logger.CreateScopedLogger(nameof(this.GetLatestVersion), version); scopedLogger.LogInformation($"Retrieving version {version}"); var downloadUrl = ReleaseUrl.Replace(TagPlaceholder, version); - var destinationFolder = Path.GetFullPath(InstallationPath); + var destinationFolder = InstallationPath; var destinationPath = Path.Combine(destinationFolder, ZipName); var success = await this.downloadService.DownloadFile(downloadUrl, destinationPath, new UpdateStatus(), cancellationToken); if (!success) @@ -138,7 +140,7 @@ public sealed class UBlockOriginService : IBrowserExtension private async Task GetCurrentVersion(CancellationToken cancellationToken) { - var manifestFilePath = Path.GetFullPath(Path.Combine(InstallationPath, Path.Combine(InstallationFolderName, "manifest.json"))); + var manifestFilePath = Path.GetFullPath(InstallationPath, Path.Combine(InstallationFolderName, "manifest.json")); var fileInfo = new FileInfo(manifestFilePath); if (!fileInfo.Exists) { diff --git a/Daybreak/Services/UMod/UModService.cs b/Daybreak/Services/UMod/UModService.cs index c83f9c58..f98fe5ae 100644 --- a/Daybreak/Services/UMod/UModService.cs +++ b/Daybreak/Services/UMod/UModService.cs @@ -8,6 +8,7 @@ using Daybreak.Services.Downloads; using Daybreak.Services.Injection; using Daybreak.Services.Notifications; using Daybreak.Services.Toolbox.Models; +using Daybreak.Utils; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -28,10 +29,12 @@ internal sealed class UModService : IUModService private const string TagPlaceholder = "[TAG_PLACEHOLDER]"; private const string ReleaseUrl = "https://github.com/gwdevhub/gMod/releases/download/[TAG_PLACEHOLDER]/gMod.dll"; private const string ReleasesUrl = "https://api.github.com/repos/gwdevhub/gMod/git/refs/tags"; - private const string UModDirectory = "uMod"; + private const string UModDirectorySubPath = "uMod"; private const string UModDll = "uMod.dll"; private const string UModModList = "modlist.txt"; + private static readonly string UModDirectory = PathUtils.GetAbsolutePathFromRoot(UModDirectorySubPath); + private readonly IProcessInjector processInjector; private readonly INotificationService notificationService; private readonly IDownloadService downloadService; @@ -86,10 +89,10 @@ internal sealed class UModService : IUModService public async Task OnGuildWarsCreated(GuildWarsCreatedContext guildWarsCreatedContext, CancellationToken cancellationToken) { - var modListFilePath = Path.Combine(Path.GetFullPath(UModDirectory), UModModList); + var modListFilePath = Path.Combine(UModDirectory, UModModList); var lines = this.uModOptions.Value.Mods.Where(e => e.Enabled && e.PathToFile is not null).Select(e => e.PathToFile).ToList(); await File.WriteAllLinesAsync(modListFilePath, lines!, cancellationToken); - var result = await this.processInjector.Inject(guildWarsCreatedContext.ApplicationLauncherContext.Process, Path.Combine(Path.GetFullPath(UModDirectory), UModDll), cancellationToken); + var result = await this.processInjector.Inject(guildWarsCreatedContext.ApplicationLauncherContext.Process, Path.Combine(UModDirectory, UModDll), cancellationToken); if (result) { this.notificationService.NotifyInformation( @@ -180,7 +183,7 @@ internal sealed class UModService : IUModService public async Task CheckAndUpdateUMod(CancellationToken cancellationToken) { var scopedLogger = this.logger.CreateScopedLogger(nameof(this.CheckAndUpdateUMod), string.Empty); - var existingUMod = Path.Combine(Path.GetFullPath(UModDirectory), UModDll); + var existingUMod = Path.Combine(UModDirectory, UModDll); if (!this.IsInstalled) { scopedLogger.LogInformation("UMod is not installed"); @@ -285,7 +288,7 @@ internal sealed class UModService : IUModService scopedLogger.LogInformation($"Retrieving version {tag}"); var downloadUrl = ReleaseUrl.Replace(TagPlaceholder, tag); - var destinationFolder = Path.GetFullPath(UModDirectory); + var destinationFolder = UModDirectory; var destinationPath = Path.Combine(destinationFolder, UModDll); var success = await this.downloadService.DownloadFile(downloadUrl, destinationPath, uModInstallationStatus, cancellationToken); if (!success) diff --git a/Daybreak/Services/Updater/ApplicationUpdater.cs b/Daybreak/Services/Updater/ApplicationUpdater.cs index ea915785..cf59b61b 100644 --- a/Daybreak/Services/Updater/ApplicationUpdater.cs +++ b/Daybreak/Services/Updater/ApplicationUpdater.cs @@ -9,9 +9,11 @@ using Daybreak.Services.Notifications; using Daybreak.Services.Registry; using Daybreak.Services.Updater.Models; using Daybreak.Services.Updater.PostUpdate; +using Daybreak.Utils; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.ComponentModel.Design.Serialization; using System.Configuration; using System.Core.Extensions; using System.Data; @@ -32,10 +34,11 @@ namespace Daybreak.Services.Updater; internal sealed class ApplicationUpdater : IApplicationUpdater { - private const string TempInstallerFileName = "Daybreak.Installer.Temp.exe"; - private const string InstallerFileName = "Daybreak.Installer.exe"; + private const string UpdatePkgSubPath = "update.pkg"; + private const string TempInstallerFileNameSubPath = "Daybreak.Installer.Temp.exe"; + private const string InstallerFileNameSubPath = "Daybreak.Installer.exe"; private const string UpdatedKey = "LauncherUpdating"; - private const string TempFile = "tempfile.zip"; + private const string TempFileSubPath = "tempfile.zip"; private const string VersionTag = "{VERSION}"; private const string FileTag = "{FILE}"; private const string RefTagPrefix = "/refs/tags"; @@ -45,6 +48,11 @@ internal sealed class ApplicationUpdater : IApplicationUpdater private const string BlobStorageUrl = $"https://daybreak.blob.core.windows.net/{VersionTag}/{FileTag}"; private const int DownloadParallelTasks = 10; + private readonly static string TempInstallerFileName = PathUtils.GetAbsolutePathFromRoot(TempInstallerFileNameSubPath); + private readonly static string InstallerFileName = PathUtils.GetAbsolutePathFromRoot(InstallerFileNameSubPath); + private readonly static string TempFile = PathUtils.GetAbsolutePathFromRoot(TempFileSubPath); + private readonly static string UpdatePkg = PathUtils.GetAbsolutePathFromRoot(UpdatePkgSubPath); + private readonly static TimeSpan DownloadInfoUpdateInterval = TimeSpan.FromMilliseconds(16); private readonly CancellationTokenSource updateCancellationTokenSource = new(); @@ -281,7 +289,7 @@ internal sealed class ApplicationUpdater : IApplicationUpdater }) .ToList(); - using var packageStream = new FileStream("update.pkg", FileMode.Create); + using var packageStream = new FileStream(UpdatePkg, FileMode.Create); var downloaded = 0d; var downloadBuffer = new byte[8192]; var sizeToDownload = (double)filesToDownload.Sum(m => m.Size); @@ -359,7 +367,7 @@ internal sealed class ApplicationUpdater : IApplicationUpdater updateStatus.CurrentStep = DownloadStatus.Downloading(1, TimeSpan.Zero); - scopedLogger.LogInformation($"Prepared update package at {Path.GetFullPath("update.pkg")}"); + scopedLogger.LogInformation($"Prepared update package at {UpdatePkg}"); updateStatus.CurrentStep = UpdateStatus.PendingRestart; return true; } diff --git a/Daybreak/Utils/PathUtils.cs b/Daybreak/Utils/PathUtils.cs new file mode 100644 index 00000000..228db0bc --- /dev/null +++ b/Daybreak/Utils/PathUtils.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Daybreak.Utils; +public static class PathUtils +{ + private static readonly Lazy RootPath = new(() => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new InvalidOperationException("Unable to obtain application root path")); + + public static string GetRootFolder() + { + return RootPath.Value; + } + + public static string GetAbsolutePathFromRoot(params string[] subPaths) + { + var paths = subPaths.Prepend(GetRootFolder()).ToArray(); + return Path.GetFullPath(Path.Combine(paths)); + } +} diff --git a/Examples/Plugins/SimplePlugin/SimplePlugin/SimplePlugin.csproj b/Examples/Plugins/SimplePlugin/SimplePlugin/SimplePlugin.csproj index 85c5260d..8550a9c2 100644 --- a/Examples/Plugins/SimplePlugin/SimplePlugin/SimplePlugin.csproj +++ b/Examples/Plugins/SimplePlugin/SimplePlugin/SimplePlugin.csproj @@ -1,7 +1,7 @@  - net6.0-windows + net8.0-windows enable enable