fix: 统一 WpfGui 工作目录 (#14072)

* 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>
This commit is contained in:
MistEO
2025-09-12 21:25:33 +08:00
committed by GitHub
parent a71eb71eff
commit 36da60ed9d
22 changed files with 225 additions and 129 deletions

View File

@@ -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<ConfigurationHelper>();
@@ -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<Root>(File.ReadAllText(_configurationFile), _options);
parsed = JsonSerializer.Deserialize<Root>(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<Root>(File.ReadAllText(_configurationBakFile), _options);
parsed = JsonSerializer.Deserialize<Root>(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);
}
}
}

View File

@@ -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<string, ConcurrentDictionary<string, string>> _kvsMap;
private static string _current = ConfigurationKeys.DefaultConfiguration;
private static ConcurrentDictionary<string, string> _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);
}
}

View File

@@ -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<string, (string DisplayName, string ClientName)>(
@@ -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;

View File

@@ -27,8 +27,8 @@ namespace MaaWpfGui.Helper
{
private static readonly ILogger _logger = Log.ForContext<ETagCache>();
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<string, string> _etagCache = [];
private static Dictionary<string, DateTimeOffset> _lastModifiedCache = [];

View File

@@ -27,7 +27,7 @@ namespace MaaWpfGui.Helper
public static async Task<bool> 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))

View File

@@ -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);

View File

@@ -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
/// <returns>反序列化数据</returns>
public static T? Get<T>(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
/// <returns>是否设置成功</returns>
public static bool Set<T>(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
/// <returns>是否成功删除</returns>
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
/// <returns>是否存在</returns>
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);
}
}

View File

@@ -0,0 +1,72 @@
// <copyright file="PathsHelper.cs" company="MaaAssistantArknights">
// 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
// </copyright>
#nullable enable
using System;
using System.IO;
namespace MaaWpfGui.Helper;
/// <summary>
/// Helper class for managing commonly used application paths.
/// </summary>
public static class PathsHelper
{
private static string? _base;
/// <summary>
/// Gets the application's working directory (AppDomain.CurrentDomain.BaseDirectory).
/// </summary>
public static string BaseDir => _base ??= AppDomain.CurrentDomain.BaseDirectory;
private static string? _resource;
/// <summary>
/// Gets the resource directory under the working directory.
/// </summary>
public static string ResourceDir => _resource ??= Path.Combine(BaseDir, "resource");
private static string? _cache;
/// <summary>
/// Gets the full path to the application's cache directory.
/// </summary>
public static string CacheDir => _cache ??= Path.Combine(BaseDir, "cache");
private static string? _config;
/// <summary>
/// Gets the full path to the directory containing GUI configuration files for the application.
/// </summary>
public static string ConfigDir => _config ??= Path.Combine(BaseDir, "config");
private static string? _debug;
/// <summary>
/// Gets the full path to the directory used for storing debug files.
/// </summary>
/// <remarks>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.</remarks>
public static string DebugDir => _debug ??= Path.Combine(BaseDir, "debug");
private static string? _data;
/// <summary>
/// Gets the full path to the application's data directory.
/// </summary>
/// <remarks>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.</remarks>
public static string DataDir => _data ??= Path.Combine(BaseDir, "data");
}

View File

@@ -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);
}
/// <summary>
@@ -338,19 +351,6 @@ namespace MaaWpfGui.Main
}
}
public static string MainResourcePath()
{
var uiVersion = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.1";
if (uiVersion == "0.0.1")
{
return Directory.GetCurrentDirectory() + @"\..\..\..";
}
else
{
return Directory.GetCurrentDirectory();
}
}
/// <summary>
/// 加载全局资源。新版 core 全惰性加载资源,所以可以无脑调用
/// </summary>
@@ -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.jsonapi 为了兼容,仍然存在 resource/tasks.json

View File

@@ -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<ClassNameEnricher>()
.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();

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -218,7 +218,7 @@ namespace MaaWpfGui.Services.Web
public async Task<bool> 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);

View File

@@ -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<JObject?> RequestMaaApiWithCache(string api)
{

View File

@@ -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

View File

@@ -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
/// </summary>
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)
{

View File

@@ -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");

View File

@@ -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)

View File

@@ -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);

View File

@@ -161,10 +161,10 @@ public class VersionUpdateSettingsUserControlModel : PropertyChangedBase
{
bool isDefaultClient = new HashSet<string> { 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)))

View File

@@ -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);