From 36da60ed9da08cb49bd8b0edcfabcf852ad48d4c Mon Sep 17 00:00:00 2001 From: MistEO Date: Fri, 12 Sep 2025 21:25:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=20WpfGui=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E7=9B=AE=E5=BD=95=20(#14072)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 统一WpfGui工作目录 * chore: 漏了的cache目录 * chore: Auto update by pre-commit hooks [skip changelog] * fix: 多了一层相对目录 * feat: wpf 创建软连接 * feat: 添加 PathsHelper,提取通用地址 * feat: 提取通用目录 * fix: Combine * fix: 重构依赖后 MaaWpfGui 的 resource 路径丢失 (#14077) fix: mklink * chore: 删除弹窗 * chore: 自定义基建使用绝对路径 * perf: 简化命名 * fix: 遗漏的issue rp * fix: 基建 * perf: config.new * feat: 去除本地作业前缀 * chore: 怎么改名了 * rft: rename 添加一个Dir后缀, 直接using static * fix: remove不再使用的../../../resrouce * perf: 短点 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: uye <99072975+ABA2396@users.noreply.github.com> Co-authored-by: Status102 <102887808+status102@users.noreply.github.com> --- .../Configuration/Factory/ConfigFactory.cs | 39 +++++----- src/MaaWpfGui/Helper/ConfigurationHelper.cs | 32 ++++----- src/MaaWpfGui/Helper/DataHelper.cs | 8 +-- src/MaaWpfGui/Helper/ETagCache.cs | 4 +- src/MaaWpfGui/Helper/HttpResponseHelper.cs | 2 +- src/MaaWpfGui/Helper/ItemListHelper.cs | 6 +- src/MaaWpfGui/Helper/JsonDataHelper.cs | 11 ++- src/MaaWpfGui/Helper/PathsHelper.cs | 72 +++++++++++++++++++ src/MaaWpfGui/Main/AsstProxy.cs | 52 ++++++++------ src/MaaWpfGui/Main/Bootstrapper.cs | 45 +++++++----- src/MaaWpfGui/Models/ResourceUpdater.cs | 4 +- src/MaaWpfGui/Services/MaaService.cs | 2 + src/MaaWpfGui/Services/StageManager.cs | 8 +-- src/MaaWpfGui/Services/Web/HttpService.cs | 2 +- src/MaaWpfGui/Services/Web/MaaApiService.cs | 2 +- .../ViewModels/UI/AnnouncementViewModel.cs | 4 +- .../ViewModels/UI/CopilotViewModel.cs | 17 ++++- .../ViewModels/UI/VersionUpdateViewModel.cs | 2 +- .../AchievementSettingsUserControlModel.cs | 4 +- .../Settings/IssueReportUserControlModel.cs | 32 ++++----- .../VersionUpdateSettingsUserControlModel.cs | 4 +- .../InfrastSettingsUserControlModel.cs | 2 +- 22 files changed, 225 insertions(+), 129 deletions(-) create mode 100644 src/MaaWpfGui/Helper/PathsHelper.cs diff --git a/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs b/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs index cef22adfaa..44705690ce 100644 --- a/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs +++ b/src/MaaWpfGui/Configuration/Factory/ConfigFactory.cs @@ -31,15 +31,14 @@ using Serilog; [assembly: PropertyChanged.FilterType("MaaWpfGui.Configuration.")] namespace MaaWpfGui.Configuration.Factory; +using static MaaWpfGui.Helper.PathsHelper; public static class ConfigFactory { - public const string ConfigFileName = "config/gui.new.json"; - private static readonly string _configurationFile = Path.Combine(Environment.CurrentDirectory, ConfigFileName); + public static readonly string ConfigFile = Path.Combine(ConfigDir, "gui.new.json"); // TODO: write backup method. WIP: https://github.com/Cryolitia/MaaAssistantArknights/tree/config - // ReSharper disable once UnusedMember.Local - private static readonly string _configurationBakFile = Path.Combine(Environment.CurrentDirectory, "config/gui.new.json.bak"); + private static readonly string _configBakFile = Path.Combine(ConfigDir, "gui.new.json.bak"); private static readonly ILogger _logger = Log.ForContext(); @@ -59,21 +58,21 @@ public static class ConfigFactory { lock (_lock) { - if (Directory.Exists("config") is false) + if (Directory.Exists(ConfigDir) is false) { - Directory.CreateDirectory("config"); + Directory.CreateDirectory(ConfigDir); } Root? parsed = null; - if (File.Exists(_configurationFile)) + if (File.Exists(ConfigFile)) { try { - parsed = JsonSerializer.Deserialize(File.ReadAllText(_configurationFile), _options); + parsed = JsonSerializer.Deserialize(File.ReadAllText(ConfigFile), _options); if (parsed is null) { _logger.Warning("Failed to load configuration file, copying configuration file to error file"); - File.Copy(_configurationFile, _configurationFile + ".err", true); + File.Copy(ConfigFile, ConfigFile + ".err", true); } } catch (Exception e) @@ -82,21 +81,21 @@ public static class ConfigFactory } } - if (parsed is null && File.Exists(_configurationBakFile)) + if (parsed is null && File.Exists(_configBakFile)) { _logger.Information("trying to use backup file"); try { - parsed = JsonSerializer.Deserialize(File.ReadAllText(_configurationBakFile), _options); + parsed = JsonSerializer.Deserialize(File.ReadAllText(_configBakFile), _options); if (parsed is not null) { _logger.Information("Backup file loaded successfully, copying backup file to configuration file"); - File.Copy(_configurationBakFile, _configurationFile, true); + File.Copy(_configBakFile, ConfigFile, true); } else { _logger.Warning("Failed to load backup file, copying backup file to error file"); - File.Copy(_configurationBakFile, _configurationBakFile + ".err", true); + File.Copy(_configBakFile, _configBakFile + ".err", true); } } catch (Exception e) @@ -152,13 +151,13 @@ public static class ConfigFactory SpecificConfigBind(keyValue.Key, keyValue.Value); } - if (Save(_configurationBakFile, parsed)) + if (Save(_configBakFile, parsed)) { - _logger.Information("{File} saved", _configurationBakFile); + _logger.Information("{File} saved", _configBakFile); } else { - _logger.Warning("{File} save failed", _configurationBakFile); + _logger.Warning("{File} save failed", _configBakFile); } return parsed; @@ -274,7 +273,7 @@ public static class ConfigFactory { try { - File.WriteAllText(file ?? _configurationFile, JsonSerializer.Serialize(root ?? Root, _options)); + File.WriteAllText(file ?? ConfigFile, JsonSerializer.Serialize(root ?? Root, _options)); } catch (Exception e) { @@ -291,7 +290,7 @@ public static class ConfigFactory await _semaphore.WaitAsync(); try { - var filePath = file ?? _configurationFile; + var filePath = file ?? ConfigFile; var jsonString = JsonSerializer.Serialize(Root, _options); await File.WriteAllTextAsync(filePath, jsonString); return true; @@ -313,11 +312,11 @@ public static class ConfigFactory { if (Save()) { - _logger.Information("{File} saved", _configurationFile); + _logger.Information("{File} saved", ConfigFile); } else { - _logger.Warning("{File} save failed", _configurationFile); + _logger.Warning("{File} save failed", ConfigFile); } } } diff --git a/src/MaaWpfGui/Helper/ConfigurationHelper.cs b/src/MaaWpfGui/Helper/ConfigurationHelper.cs index fd29c4d25c..95d3290dde 100644 --- a/src/MaaWpfGui/Helper/ConfigurationHelper.cs +++ b/src/MaaWpfGui/Helper/ConfigurationHelper.cs @@ -25,10 +25,10 @@ namespace MaaWpfGui.Helper { public class ConfigurationHelper { - public const string ConfigurationFile = "config/gui.json"; - private static readonly string _configurationFile = Path.Combine(Environment.CurrentDirectory, ConfigurationFile); - private static readonly string _configurationBakFile = Path.Combine(Environment.CurrentDirectory, "config/gui.json.bak"); - private static readonly string _configurationErrorFile = Path.Combine(Environment.CurrentDirectory, "config/gui.error.json"); + public static readonly string ConfigFile = Path.Combine(PathsHelper.ConfigDir, "gui.json"); + public static readonly string ConfigBakFile = Path.Combine(PathsHelper.ConfigDir, "gui.json.bak"); + public static readonly string ConfigErrorFile = Path.Combine(PathsHelper.ConfigDir, "gui.error.json"); + private static ConcurrentDictionary> _kvsMap; private static string _current = ConfigurationKeys.DefaultConfiguration; private static ConcurrentDictionary _kvs; @@ -276,26 +276,26 @@ namespace MaaWpfGui.Helper private static bool ParseConfig(out JObject parsed) { - parsed = ParseJsonFile(_configurationFile); + parsed = ParseJsonFile(ConfigFile); if (parsed is not null) { return true; } - if (File.Exists(_configurationFile)) + if (File.Exists(ConfigFile)) { _logger.Warning("Failed to load configuration file, copying configuration file to error file"); - File.Copy(_configurationFile, _configurationErrorFile, true); + File.Copy(ConfigFile, ConfigErrorFile, true); } - if (File.Exists(_configurationBakFile)) + if (File.Exists(ConfigBakFile)) { _logger.Information("trying to use backup file"); - parsed = ParseJsonFile(_configurationBakFile); + parsed = ParseJsonFile(ConfigBakFile); if (parsed is not null) { _logger.Information("Backup file loaded successfully, copying backup file to configuration file"); - File.Copy(_configurationBakFile, _configurationFile, true); + File.Copy(ConfigBakFile, ConfigFile, true); return true; } } @@ -325,13 +325,13 @@ namespace MaaWpfGui.Helper } SetKvOrMigrate(parsed); - if (Save(_configurationBakFile)) + if (Save(ConfigBakFile)) { - _logger.Information("{File} saved", _configurationBakFile); + _logger.Information("{File} saved", ConfigBakFile); } else { - _logger.Warning("{File} save failed", _configurationBakFile); + _logger.Warning("{File} save failed", ConfigBakFile); } return true; @@ -396,7 +396,7 @@ namespace MaaWpfGui.Helper }, Formatting.Indented); - File.WriteAllText(file ?? _configurationFile, jsonStr); + File.WriteAllText(file ?? ConfigFile, jsonStr); } catch (Exception e) { @@ -490,11 +490,11 @@ namespace MaaWpfGui.Helper if (Save()) { - _logger.Information("{File} saved", _configurationFile); + _logger.Information("{File} saved", ConfigFile); } else { - _logger.Warning("{File} save failed", _configurationFile); + _logger.Warning("{File} save failed", ConfigFile); } } diff --git a/src/MaaWpfGui/Helper/DataHelper.cs b/src/MaaWpfGui/Helper/DataHelper.cs index be118b1cff..6030347b01 100644 --- a/src/MaaWpfGui/Helper/DataHelper.cs +++ b/src/MaaWpfGui/Helper/DataHelper.cs @@ -78,7 +78,7 @@ namespace MaaWpfGui.Helper private static void LoadBattleData() { - string filePath = Path.Combine(AsstProxy.MainResourcePath(), "resource/battle_data.json"); + string filePath = Path.Combine(PathsHelper.ResourceDir, "battle_data.json"); if (!File.Exists(filePath)) { return; @@ -124,8 +124,8 @@ namespace MaaWpfGui.Helper _ => string.Empty, }; - var clientTags = ParseRecruit(Path.Combine(AsstProxy.MainResourcePath(), "resource", clientPath, "recruitment.json")); - var displayTags = ParseRecruit(Path.Combine(AsstProxy.MainResourcePath(), "resource", displayPath, "recruitment.json")); + var clientTags = ParseRecruit(Path.Combine(PathsHelper.ResourceDir, clientPath, "recruitment.json")); + var displayTags = ParseRecruit(Path.Combine(PathsHelper.ResourceDir, displayPath, "recruitment.json")); RecruitTags = clientTags.Keys .Select(key => new KeyValuePair( @@ -167,7 +167,7 @@ namespace MaaWpfGui.Helper private static void LoadMapData() { MapData = []; - var path = Path.Combine(AsstProxy.MainResourcePath(), "resource", "Arknights-Tile-Pos", "overview.json"); + var path = Path.Combine(PathsHelper.ResourceDir, "Arknights-Tile-Pos", "overview.json"); if (!File.Exists(path)) { return; diff --git a/src/MaaWpfGui/Helper/ETagCache.cs b/src/MaaWpfGui/Helper/ETagCache.cs index 50b4ab0498..33e0991392 100644 --- a/src/MaaWpfGui/Helper/ETagCache.cs +++ b/src/MaaWpfGui/Helper/ETagCache.cs @@ -27,8 +27,8 @@ namespace MaaWpfGui.Helper { private static readonly ILogger _logger = Log.ForContext(); - private static readonly string _etagFile = Path.Combine(Environment.CurrentDirectory, "cache/etag.json"); - private static readonly string _lastModifiedFile = Path.Combine(Environment.CurrentDirectory, "cache/last_modified.json"); + private static readonly string _etagFile = Path.Combine(PathsHelper.CacheDir, "etag.json"); + private static readonly string _lastModifiedFile = Path.Combine(PathsHelper.CacheDir, "last_modified.json"); private static Dictionary _etagCache = []; private static Dictionary _lastModifiedCache = []; diff --git a/src/MaaWpfGui/Helper/HttpResponseHelper.cs b/src/MaaWpfGui/Helper/HttpResponseHelper.cs index c7f5014903..498dcb8fe0 100644 --- a/src/MaaWpfGui/Helper/HttpResponseHelper.cs +++ b/src/MaaWpfGui/Helper/HttpResponseHelper.cs @@ -27,7 +27,7 @@ namespace MaaWpfGui.Helper public static async Task SaveResponseToFileAsync(HttpResponseMessage? response, string saveTo, bool saveAndDeleteTmp = true) { - saveTo = Path.Combine(Environment.CurrentDirectory, saveTo); + saveTo = Path.Combine(PathsHelper.BaseDir, saveTo); var tempFile = saveTo + ".tmp"; if (File.Exists(tempFile)) diff --git a/src/MaaWpfGui/Helper/ItemListHelper.cs b/src/MaaWpfGui/Helper/ItemListHelper.cs index b4dc14e4c8..9bd234d9a5 100644 --- a/src/MaaWpfGui/Helper/ItemListHelper.cs +++ b/src/MaaWpfGui/Helper/ItemListHelper.cs @@ -40,14 +40,14 @@ namespace MaaWpfGui.Helper switch (language) { case "zh-cn": - filename = Path.Combine(AsstProxy.MainResourcePath(), "resource", "item_index.json"); + filename = Path.Combine(PathsHelper.ResourceDir, "item_index.json"); break; case "pallas": break; default: - filename = Path.Combine(AsstProxy.MainResourcePath(), "resource", "global", DataHelper.ClientDirectoryMapper[language], "resource", "item_index.json"); + filename = Path.Combine(PathsHelper.ResourceDir, "global", DataHelper.ClientDirectoryMapper[language], "resource", "item_index.json"); break; } @@ -100,7 +100,7 @@ namespace MaaWpfGui.Helper return cachedImage; } - var imagePath = Path.Combine(Environment.CurrentDirectory, $"resource/template/items/{itemId}.png"); + var imagePath = Path.Combine(PathsHelper.ResourceDir, $"template/items/{itemId}.png"); if (!File.Exists(imagePath)) { _imageCache.TryAdd(itemId, null); diff --git a/src/MaaWpfGui/Helper/JsonDataHelper.cs b/src/MaaWpfGui/Helper/JsonDataHelper.cs index 3bd40dfa55..31f64fb614 100644 --- a/src/MaaWpfGui/Helper/JsonDataHelper.cs +++ b/src/MaaWpfGui/Helper/JsonDataHelper.cs @@ -22,7 +22,6 @@ namespace MaaWpfGui.Helper { public static class JsonDataHelper { - private static readonly string _dataDir = "data"; private static readonly object _lock = new(); private static readonly ILogger _logger = Log.ForContext("SourceContext", "JsonDataHelper"); @@ -36,7 +35,7 @@ namespace MaaWpfGui.Helper /// 反序列化数据 public static T? Get(string key, T? defaultValue = default, string? dataDir = null) { - var filePath = Path.Combine(dataDir ?? _dataDir, $"{key}.json"); + var filePath = Path.Combine(dataDir ?? PathsHelper.DataDir, $"{key}.json"); if (!File.Exists(filePath)) { @@ -70,13 +69,13 @@ namespace MaaWpfGui.Helper /// 是否设置成功 public static bool Set(string key, T value, string? dataDir = null) { - var filePath = Path.Combine(dataDir ?? _dataDir, $"{key}.json"); + var filePath = Path.Combine(dataDir ?? PathsHelper.DataDir, $"{key}.json"); lock (_lock) { try { - Directory.CreateDirectory(_dataDir); + Directory.CreateDirectory(PathsHelper.DataDir); var json = JsonConvert.SerializeObject(value, Formatting.Indented); File.WriteAllText(filePath, json); return true; @@ -96,7 +95,7 @@ namespace MaaWpfGui.Helper /// 是否成功删除 public static bool Delete(string key) { - var filePath = Path.Combine(_dataDir, $"{key}.json"); + var filePath = Path.Combine(PathsHelper.DataDir, $"{key}.json"); try { @@ -122,7 +121,7 @@ namespace MaaWpfGui.Helper /// 是否存在 public static bool Exists(string key) { - var filePath = Path.Combine(_dataDir, $"{key}.json"); + var filePath = Path.Combine(PathsHelper.DataDir, $"{key}.json"); return File.Exists(filePath); } } diff --git a/src/MaaWpfGui/Helper/PathsHelper.cs b/src/MaaWpfGui/Helper/PathsHelper.cs new file mode 100644 index 0000000000..332851f498 --- /dev/null +++ b/src/MaaWpfGui/Helper/PathsHelper.cs @@ -0,0 +1,72 @@ +// +// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team) +// Copyright (C) 2021-2025 MaaAssistantArknights Contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License v3.0 only as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY +// + +#nullable enable + +using System; +using System.IO; + +namespace MaaWpfGui.Helper; + +/// +/// Helper class for managing commonly used application paths. +/// +public static class PathsHelper +{ + private static string? _base; + + /// + /// Gets the application's working directory (AppDomain.CurrentDomain.BaseDirectory). + /// + public static string BaseDir => _base ??= AppDomain.CurrentDomain.BaseDirectory; + + private static string? _resource; + + /// + /// Gets the resource directory under the working directory. + /// + public static string ResourceDir => _resource ??= Path.Combine(BaseDir, "resource"); + + private static string? _cache; + + /// + /// Gets the full path to the application's cache directory. + /// + public static string CacheDir => _cache ??= Path.Combine(BaseDir, "cache"); + + private static string? _config; + + /// + /// Gets the full path to the directory containing GUI configuration files for the application. + /// + public static string ConfigDir => _config ??= Path.Combine(BaseDir, "config"); + + private static string? _debug; + + /// + /// Gets the full path to the directory used for storing debug files. + /// + /// The debug directory is located within the application's base directory and is named + /// "debug". The directory may not exist until created by the application or related processes. + public static string DebugDir => _debug ??= Path.Combine(BaseDir, "debug"); + + private static string? _data; + + /// + /// Gets the full path to the application's data directory. + /// + /// The data directory is located within the application's base directory under a folder + /// named "data". This property returns the resolved path, creating the directory if it does not exist is the + /// responsibility of the caller. + public static string DataDir => _data ??= Path.Combine(BaseDir, "data"); +} diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index 596aff028a..b99f7cdcec 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -86,6 +86,17 @@ namespace MaaWpfGui.Main } } + private static unsafe bool AsstSetUserDir(string dirname) + { + fixed (byte* ptr = EncodeNullTerminatedUtf8(dirname)) + { + _logger.Information("AsstSetUserDir dirame: {Dirname}", dirname); + var ret = MaaService.AsstSetUserDir(ptr); + _logger.Information("AsstSetUserDir ret: {Ret}", ret); + return ret; + } + } + private static unsafe bool AsstLoadResource(string dirname) { fixed (byte* ptr = EncodeNullTerminatedUtf8(dirname)) @@ -325,6 +336,8 @@ namespace MaaWpfGui.Main TaskSettingVisibilityInfo.Instance.CurrentTask = string.Empty; } }; + + AsstSetUserDir(PathsHelper.BaseDir); } /// @@ -338,19 +351,6 @@ namespace MaaWpfGui.Main } } - public static string MainResourcePath() - { - var uiVersion = Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion.Split('+')[0] ?? "0.0.1"; - if (uiVersion == "0.0.1") - { - return Directory.GetCurrentDirectory() + @"\..\..\.."; - } - else - { - return Directory.GetCurrentDirectory(); - } - } - /// /// 加载全局资源。新版 core 全惰性加载资源,所以可以无脑调用 /// @@ -360,11 +360,11 @@ namespace MaaWpfGui.Main _logger.Information("Load Resource"); string clientType = SettingsViewModel.GameSettings.ClientType; - string mainRes = MainResourcePath(); + string mainRes = PathsHelper.ResourceDir; - string globalResource = mainRes + @"\resource\global\" + clientType; - string mainCache = Directory.GetCurrentDirectory() + @"\cache"; - string globalCache = mainCache + @"\resource\global\" + clientType; + string globalResource = Path.Combine(mainRes, "global", clientType); + string mainCache = PathsHelper.CacheDir; + string globalCache = Path.Combine(mainCache, "global", clientType); bool loaded; if (clientType is "" or "Official" or "Bilibili") @@ -387,15 +387,23 @@ namespace MaaWpfGui.Main static bool LoadResIfExists(string path) { - const string Resource = @"\resource"; - if (!Directory.Exists(path + Resource)) + if (!Directory.Exists(path)) { - _logger.Warning("Resource not found: {Path}", path + Resource); + _logger.Warning("Resource not found: {Path}", path); return true; } - _logger.Information("Load resource: {Path}", path + Resource); - return AsstLoadResource(path); + _logger.Information("Load resource: {Path}", path); + + // AsstLoadResource 需要的是 resource 的上级目录 + var parent = Directory.GetParent(path)?.FullName ?? string.Empty; + if (string.IsNullOrEmpty(parent)) + { + _logger.Warning("Resource path invalid: {Path}", path); + return false; + } + + return AsstLoadResource(parent); } // 新的目录结构为 tasks/tasks.json,api 为了兼容,仍然存在 resource/tasks.json diff --git a/src/MaaWpfGui/Main/Bootstrapper.cs b/src/MaaWpfGui/Main/Bootstrapper.cs index 4f4106844f..4cf4076120 100644 --- a/src/MaaWpfGui/Main/Bootstrapper.cs +++ b/src/MaaWpfGui/Main/Bootstrapper.cs @@ -60,10 +60,11 @@ namespace MaaWpfGui.Main private static Mutex _mutex; private static bool _hasMutex; - public const string UiLogFilename = "debug/gui.log"; - public const string UiLogBakFilename = "debug/gui.bak.log"; - public const string CoreLogFilename = "debug/asst.log"; - public const string CoreLogBakFilename = "debug/asst.bak.log"; + + public static readonly string UiLogFile = Path.Combine(PathsHelper.DebugDir, "gui.log"); + public static readonly string UiLogBakFile = Path.Combine(PathsHelper.DebugDir, "gui.bak.log"); + public static readonly string CoreLogFile = Path.Combine(PathsHelper.DebugDir, "asst.log"); + public static readonly string CoreLogBakFile = Path.Combine(PathsHelper.DebugDir, "asst.bak.log"); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr LoadLibrary(string dllName); @@ -90,10 +91,9 @@ namespace MaaWpfGui.Main var dllFiles = Directory.GetFiles(currentDirectory, "*.dll"); - return dllFiles + return [.. dllFiles .Select(Path.GetFileName) - .Where(fileName => !maaDlls.Contains(fileName) && !fileName.Contains("maa", StringComparison.OrdinalIgnoreCase)) - .ToList(); + .Where(fileName => !maaDlls.Contains(fileName) && !fileName.Contains("maa", StringComparison.OrdinalIgnoreCase))]; } catch (Exception) { @@ -149,21 +149,21 @@ namespace MaaWpfGui.Main Directory.CreateDirectory("debug"); } - if (File.Exists(UiLogFilename) && new FileInfo(UiLogFilename).Length > 4 * 1024 * 1024) + if (File.Exists(UiLogFile) && new FileInfo(UiLogFile).Length > 4 * 1024 * 1024) { - if (File.Exists(UiLogBakFilename)) + if (File.Exists(UiLogBakFile)) { - File.Delete(UiLogBakFilename); + File.Delete(UiLogBakFile); } - File.Move(UiLogFilename, UiLogBakFilename); + File.Move(UiLogFile, UiLogBakFile); } // Bootstrap serilog var loggerConfiguration = new LoggerConfiguration() .WriteTo.Debug(outputTemplate: "[{Timestamp:HH:mm:ss}][{Level:u3}]{ClassName} <{ThreadId}> {Message:lj}{NewLine}{Exception}") .WriteTo.File( - UiLogFilename, + UiLogFile, outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}][{Level:u3}]{ClassName} <{ThreadId}> {Message:lj}{NewLine}{Exception}") .Enrich.With() .Enrich.FromLogContext() @@ -181,8 +181,8 @@ namespace MaaWpfGui.Main loggerConfiguration = (maaEnv == "Debug" || withDebugFile) ? loggerConfiguration.MinimumLevel.Verbose() : loggerConfiguration.MinimumLevel.Information(); - var currentDirectory = Directory.GetCurrentDirectory(); - var folderName = Path.GetFileName(currentDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + var workingDirectory = PathsHelper.BaseDir; + var folderName = Path.GetFileName(workingDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); var isBuildOutputFolder = string.Equals(folderName, "Release", StringComparison.OrdinalIgnoreCase) || string.Equals(folderName, "Debug", StringComparison.OrdinalIgnoreCase) || @@ -196,7 +196,7 @@ namespace MaaWpfGui.Main _logger.Information("Built at {BuiltDate:O}", builtDate); _logger.Information("Maa ENV: {MaaEnv}", maaEnv); _logger.Information("Command Line: {Join}", string.Join(' ', args)); - _logger.Information("User Dir {GetCurrentDirectory}", currentDirectory); + _logger.Information("User Dir {BaseDirectory}", workingDirectory); if (withDebugFile) { _logger.Information("Start with DEBUG file"); @@ -228,7 +228,7 @@ namespace MaaWpfGui.Main } // 检查 resource 文件夹是否存在 - if (!Directory.Exists(Path.Combine(AsstProxy.MainResourcePath(), "resource"))) + if (!Directory.Exists(PathsHelper.ResourceDir)) { throw new DirectoryNotFoundException("resource folder not found!"); } @@ -275,7 +275,7 @@ namespace MaaWpfGui.Main return; } - if (!IsWritable(AppDomain.CurrentDomain.BaseDirectory)) + if (!IsWritable(PathsHelper.BaseDir)) { Task.Run(() => MessageBoxHelper.Show(LocalizationHelper.GetString("SoftwareLocationWarning"), LocalizationHelper.GetString("Error"), MessageBoxButton.OK, MessageBoxImage.Error)); } @@ -318,7 +318,7 @@ namespace MaaWpfGui.Main private static bool HandleMultipleInstances() { // 设置互斥量的名称 - string mutexName = "MAA_" + Directory.GetCurrentDirectory().Replace("\\", "_").Replace(":", string.Empty); + string mutexName = "MAA_" + PathsHelper.BaseDir.Replace("\\", "_").Replace(":", string.Empty); _mutex = new Mutex(true, mutexName, out var isOnlyInstance); try @@ -437,7 +437,14 @@ namespace MaaWpfGui.Main protected override void OnExit(ExitEventArgs e) { // MessageBox.Show("O(∩_∩)O 拜拜"); - Instances.TaskQueueViewModel.ResetAllTemporaryVariable(); + try + { + Instances.TaskQueueViewModel.ResetAllTemporaryVariable(); + } + catch + { + // ignored + } Release(); diff --git a/src/MaaWpfGui/Models/ResourceUpdater.cs b/src/MaaWpfGui/Models/ResourceUpdater.cs index 11b1d38438..95865708f3 100644 --- a/src/MaaWpfGui/Models/ResourceUpdater.cs +++ b/src/MaaWpfGui/Models/ResourceUpdater.cs @@ -76,7 +76,7 @@ namespace MaaWpfGui.Models { DirectoryMerge( Path.Combine(basePath, folder), - Path.Combine(Directory.GetCurrentDirectory(), folder)); + Path.Combine(PathsHelper.BaseDir, folder)); } } catch (Exception e) @@ -287,7 +287,7 @@ namespace MaaWpfGui.Models try { - DirectoryMerge(ExtractFolder, Directory.GetCurrentDirectory()); + DirectoryMerge(ExtractFolder, PathsHelper.BaseDir); } catch (Exception e) { diff --git a/src/MaaWpfGui/Services/MaaService.cs b/src/MaaWpfGui/Services/MaaService.cs index 73619437a5..5f0899b1b1 100644 --- a/src/MaaWpfGui/Services/MaaService.cs +++ b/src/MaaWpfGui/Services/MaaService.cs @@ -39,6 +39,8 @@ namespace MaaWpfGui.Services [DllImport("MaaCore.dll")] public static extern bool AsstSetStaticOption(AsstStaticOptionKey key, [MarshalAs(UnmanagedType.LPUTF8Str)]string value); + [DllImport("MaaCore.dll")] + public static extern unsafe bool AsstSetUserDir(byte* dirname); [DllImport("MaaCore.dll")] public static extern unsafe bool AsstLoadResource(byte* dirname); diff --git a/src/MaaWpfGui/Services/StageManager.cs b/src/MaaWpfGui/Services/StageManager.cs index a638c79926..b4c540b7ab 100644 --- a/src/MaaWpfGui/Services/StageManager.cs +++ b/src/MaaWpfGui/Services/StageManager.cs @@ -63,10 +63,10 @@ namespace MaaWpfGui.Services public async Task UpdateStageWeb() { // 清理旧的缓存文件 - const string CacheAllFileDownloadComplete = "cache/allFileDownloadComplete.json"; - const string LastUpdateTime = "cache/LastUpdateTime.json"; - const string StageAndTasksUpdateTime = "cache/stageAndTasksUpdateTime.json"; - var filesToClean = new[] { CacheAllFileDownloadComplete, LastUpdateTime, StageAndTasksUpdateTime }; + string cacheAllFileDownloadComplete = PathsHelper.CacheDir + "allFileDownloadComplete.json"; + string lastUpdateTime = PathsHelper.CacheDir + "LastUpdateTime.json"; + string stageAndTasksUpdateTime = PathsHelper.CacheDir + "stageAndTasksUpdateTime.json"; + var filesToClean = new[] { cacheAllFileDownloadComplete, lastUpdateTime, stageAndTasksUpdateTime }; foreach (var file in filesToClean) { diff --git a/src/MaaWpfGui/Services/Web/HttpService.cs b/src/MaaWpfGui/Services/Web/HttpService.cs index 30b5ca2437..b8919e1368 100755 --- a/src/MaaWpfGui/Services/Web/HttpService.cs +++ b/src/MaaWpfGui/Services/Web/HttpService.cs @@ -218,7 +218,7 @@ namespace MaaWpfGui.Services.Web public async Task DownloadFileAsync(Uri uri, string fileName, string? contentType = "application/octet-stream") { - string fileDir = Directory.GetCurrentDirectory(); + string fileDir = PathsHelper.BaseDir; string fileNameWithTemp = fileName + ".temp"; string fullFilePath = Path.Combine(fileDir, fileName); string fullFilePathWithTemp = Path.Combine(fileDir, fileNameWithTemp); diff --git a/src/MaaWpfGui/Services/Web/MaaApiService.cs b/src/MaaWpfGui/Services/Web/MaaApiService.cs index 149cef10fa..4d169573ce 100644 --- a/src/MaaWpfGui/Services/Web/MaaApiService.cs +++ b/src/MaaWpfGui/Services/Web/MaaApiService.cs @@ -24,7 +24,7 @@ namespace MaaWpfGui.Services.Web { public class MaaApiService : IMaaApiService { - private const string CacheDir = "cache/"; + private static readonly string CacheDir = PathsHelper.BaseDir + "/cache/"; public async Task RequestMaaApiWithCache(string api) { diff --git a/src/MaaWpfGui/ViewModels/UI/AnnouncementViewModel.cs b/src/MaaWpfGui/ViewModels/UI/AnnouncementViewModel.cs index 39daac55b1..598b4bf1f0 100644 --- a/src/MaaWpfGui/ViewModels/UI/AnnouncementViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/AnnouncementViewModel.cs @@ -139,8 +139,8 @@ namespace MaaWpfGui.ViewModels.UI private static readonly string _announcementInFile = SettingsViewModel.GuiSettings.Language switch { - "zh-cn" or "zh-tw" => Path.Combine(Environment.CurrentDirectory, "cache", "announcement.md"), - _ => Path.Combine(Environment.CurrentDirectory, "cache", "announcement_en.md"), + "zh-cn" or "zh-tw" => Path.Combine(PathsHelper.CacheDir, "announcement.md"), + _ => Path.Combine(PathsHelper.CacheDir, "announcement_en.md"), }; private static string AnnouncementInFile diff --git a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs index c3e4f7f573..455f47de8f 100644 --- a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs @@ -39,6 +39,7 @@ using Newtonsoft.Json; using Serilog; using Stylet; using static MaaWpfGui.Helper.CopilotHelper; +using static MaaWpfGui.Helper.PathsHelper; using static MaaWpfGui.Models.AsstTasks.AsstCopilotTask; using DataFormats = System.Windows.Forms.DataFormats; using Task = System.Threading.Tasks.Task; @@ -64,9 +65,9 @@ namespace MaaWpfGui.ViewModels.UI /// private CopilotBase? _copilotCache; private const string CopilotIdPrefix = "maa://"; - private const string TempCopilotFile = "cache/_temp_copilot.json"; + private static readonly string TempCopilotFile = Path.Combine(CacheDir, "_temp_copilot.json"); private static readonly string[] _supportExt = [".json", ".mp4", ".m4s", ".mkv", ".flv", ".avi"]; - private const string CopilotJsonDir = "config/copilot"; + private static readonly string CopilotJsonDir = Path.Combine(ConfigDir, "copilot"); private const string StageNameRegex = @"(?:[a-z]{0,3})(?:\d{0,2})-(?:(?:A|B|C|D|EX|S|TR|MO)-?)?(?:\d{1,2})"; private const string InvalidStageNameChars = @"[:',\.\(\)\|\[\]\?,。【】{};:]"; // 无效字符 @@ -647,6 +648,15 @@ namespace MaaWpfGui.ViewModels.UI int copilotId = 0; bool writeToCache = false; object? payload; + if (!File.Exists(filename)) + { + var resourceFile = Path.Combine(ResourceDir, "copilot", Path.GetFileName(filename)); + if (File.Exists(resourceFile)) + { + filename = resourceFile; + } + } + if (File.Exists(filename)) { var fileSize = new FileInfo(filename).Length; @@ -1024,7 +1034,8 @@ namespace MaaWpfGui.ViewModels.UI try { - comboBox.ItemsSource = Directory.GetFiles(@".\resource\copilot\", "*.json"); + var files = Directory.GetFiles(Path.Combine(ResourceDir, "copilot"), "*.json"); + comboBox.ItemsSource = files.Select(Path.GetFileName).ToList(); } catch (Exception exception) { diff --git a/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs b/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs index 83af9f58b2..3527668896 100644 --- a/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/VersionUpdateViewModel.cs @@ -203,7 +203,7 @@ public class VersionUpdateViewModel : Screen .ShowUpdateVersion(row: 2); } - string curDir = Directory.GetCurrentDirectory(); + string curDir = PathsHelper.BaseDir; string extractDir = Path.Combine(curDir, "NewVersionExtract"); // 新版本解压的路径 string oldFileDir = Path.Combine(curDir, ".old"); diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/AchievementSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/AchievementSettingsUserControlModel.cs index 9f4ef7f84b..86b627b5a7 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/Settings/AchievementSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/AchievementSettingsUserControlModel.cs @@ -42,7 +42,7 @@ public class AchievementSettingsUserControlModel : PropertyChangedBase public void BackupAchievements() { using var dialog = new FolderBrowserDialog(); - dialog.SelectedPath = Environment.CurrentDirectory; + dialog.SelectedPath = PathsHelper.BaseDir; dialog.ShowNewFolderButton = true; if (dialog.ShowDialog() != DialogResult.OK) @@ -75,7 +75,7 @@ public class AchievementSettingsUserControlModel : PropertyChangedBase var dlg = new Microsoft.Win32.OpenFileDialog { Filter = "JSON|*.json", - InitialDirectory = Environment.CurrentDirectory, + InitialDirectory = PathsHelper.BaseDir, }; if (dlg.ShowDialog() != true) diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs index 79a3cdf907..9f9fc4f012 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs @@ -39,14 +39,12 @@ public class IssueReportUserControlModel : PropertyChangedBase } private static readonly string[] _payloadFileNames = [ - Bootstrapper.UiLogFilename, - Bootstrapper.UiLogBakFilename, - Bootstrapper.CoreLogFilename, - Bootstrapper.CoreLogBakFilename, - ConfigurationHelper.ConfigurationFile, - ConfigFactory.ConfigFileName]; - - private const string DebugDir = "debug"; + Bootstrapper.UiLogFile, + Bootstrapper.UiLogBakFile, + Bootstrapper.CoreLogFile, + Bootstrapper.CoreLogBakFile, + ConfigurationHelper.ConfigFile, + ConfigFactory.ConfigFile]; public static IssueReportUserControlModel Instance { get; } @@ -54,12 +52,12 @@ public class IssueReportUserControlModel : PropertyChangedBase { try { - if (!Directory.Exists(DebugDir)) + if (!Directory.Exists(PathsHelper.DebugDir)) { - Directory.CreateDirectory(DebugDir); + Directory.CreateDirectory(PathsHelper.DebugDir); } - Process.Start("explorer.exe", DebugDir); + Process.Start("explorer.exe", PathsHelper.DebugDir); } catch (Exception ex) { @@ -73,7 +71,7 @@ public class IssueReportUserControlModel : PropertyChangedBase try { var reportFileName = $"report_{DateTimeOffset.Now:MM-dd_HH-mm-ss}.zip"; - string zipPath = Path.Combine(DebugDir, reportFileName); + string zipPath = Path.Combine(PathsHelper.DebugDir, reportFileName); string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); string debugTempPath = Path.Combine(tempPath, "debug"); string resourceTempPath = Path.Combine(tempPath, "resource"); @@ -94,7 +92,7 @@ public class IssueReportUserControlModel : PropertyChangedBase continue; } - string relativePath = Path.GetRelativePath(Environment.CurrentDirectory, file); + string relativePath = Path.GetRelativePath(PathsHelper.BaseDir, file); string dest = Path.Combine(tempPath, relativePath); Directory.CreateDirectory(Path.GetDirectoryName(dest)!); File.Copy(file, dest, overwrite: true); @@ -114,12 +112,12 @@ public class IssueReportUserControlModel : PropertyChangedBase } // 遍历 cache 文件夹下的文件,复制到 tempPath/cache - const string CacheResourceDir = "cache"; - if (Directory.Exists(CacheResourceDir)) + string cacheResourceDir = PathsHelper.CacheDir; + if (Directory.Exists(cacheResourceDir)) { - foreach (var file in Directory.EnumerateFiles(CacheResourceDir, "*", SearchOption.AllDirectories)) + foreach (var file in Directory.EnumerateFiles(cacheResourceDir, "*", SearchOption.AllDirectories)) { - string relativePath = Path.GetRelativePath(CacheResourceDir, file); + string relativePath = Path.GetRelativePath(cacheResourceDir, file); string dest = Path.Combine(tempPath, "cache", relativePath); Directory.CreateDirectory(Path.GetDirectoryName(dest)!); File.Copy(file, dest, overwrite: true); diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs index 186db9571a..26e5983f09 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/VersionUpdateSettingsUserControlModel.cs @@ -161,10 +161,10 @@ public class VersionUpdateSettingsUserControlModel : PropertyChangedBase { bool isDefaultClient = new HashSet { string.Empty, "Official", "Bilibili" }.Contains(clientType); - string defaultJsonPath = Path.Combine(AsstProxy.MainResourcePath(), "resource/version.json"); + string defaultJsonPath = Path.Combine(PathsHelper.ResourceDir, "version.json"); var jsonPath = isDefaultClient ? defaultJsonPath - : Path.Combine(AsstProxy.MainResourcePath(), $"resource/global/{clientType}/resource/version.json"); + : Path.Combine(PathsHelper.ResourceDir, $"global/{clientType}/resource/version.json"); string versionName; if (!File.Exists(defaultJsonPath) || (!isDefaultClient && !File.Exists(jsonPath))) diff --git a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/InfrastSettingsUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/InfrastSettingsUserControlModel.cs index ad617a0ade..ea5a186fc4 100644 --- a/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/InfrastSettingsUserControlModel.cs +++ b/src/MaaWpfGui/ViewModels/UserControl/TaskQueue/InfrastSettingsUserControlModel.cs @@ -290,7 +290,7 @@ public class InfrastSettingsUserControlModel : TaskViewModel SetAndNotify(ref _defaultInfrast, value); if (_defaultInfrast != UserDefined) { - CustomInfrastFile = @"resource\custom_infrast\" + value; + CustomInfrastFile = Path.Combine(PathsHelper.ResourceDir, "custom_infrast", value); } ConfigurationHelper.SetValue(ConfigurationKeys.DefaultInfrast, value);