diff --git a/YaeAchievement/res/App.Designer.cs b/YaeAchievement/res/App.Designer.cs
index eab56c2..42da7df 100644
--- a/YaeAchievement/res/App.Designer.cs
+++ b/YaeAchievement/res/App.Designer.cs
@@ -446,6 +446,15 @@ namespace YaeAchievement.res {
}
}
+ ///
+ /// Looks up a localized string similar to An error occurred while reading the data. Please try again..
+ ///
+ internal static string StreamReadDataFail {
+ get {
+ return ResourceManager.GetString("StreamReadDataFail", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Checking update....
///
@@ -474,6 +483,15 @@ namespace YaeAchievement.res {
}
}
+ ///
+ /// Looks up a localized string similar to The process cannot access the file '{0}' because it is being used by another process. Please restart your computer and try again..
+ ///
+ internal static string UpdateFileShareViolation {
+ get {
+ return ResourceManager.GetString("UpdateFileShareViolation", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Has update: {0} => {1}.
///
diff --git a/YaeAchievement/res/App.resx b/YaeAchievement/res/App.resx
index 9f04f70..7e2291c 100644
--- a/YaeAchievement/res/App.resx
+++ b/YaeAchievement/res/App.resx
@@ -175,4 +175,10 @@
Please enter a number between 0 and {0}
+
+ An error occurred while reading the data. Please try again.
+
+
+ The process cannot access the file '{0}' because it is being used by another process. Please restart your computer and try again.
+
\ No newline at end of file
diff --git a/YaeAchievement/res/App.zh.resx b/YaeAchievement/res/App.zh.resx
index 58e25e8..f384dbc 100644
--- a/YaeAchievement/res/App.zh.resx
+++ b/YaeAchievement/res/App.zh.resx
@@ -168,4 +168,10 @@
请输入 0 到 {0} 之间的数字
+
+ 读取数据时发生错误,请重试
+
+
+ 文件 {0} 被其它程序占用,请 重启电脑 或 解除文件占用 后重试。
+
\ No newline at end of file
diff --git a/YaeAchievement/src/AppConfig.cs b/YaeAchievement/src/AppConfig.cs
index fd1f29f..3a7d938 100644
--- a/YaeAchievement/src/AppConfig.cs
+++ b/YaeAchievement/src/AppConfig.cs
@@ -1,5 +1,5 @@
using System.Text.RegularExpressions;
-using YaeAchievement.res;
+using Spectre.Console;
using YaeAchievement.Utilities;
namespace YaeAchievement;
@@ -28,11 +28,15 @@ public static partial class AppConfig {
.Where(File.Exists)
.MaxBy(File.GetLastWriteTime);
if (logPath == null) {
- throw new ApplicationException(App.ConfigNeedStartGenshin);
+ AnsiConsole.WriteLine(App.ConfigNeedStartGenshin);
+ Environment.Exit(-1);
}
- GamePath = GetGamePathFromLogFile(logPath)
- ?? GetGamePathFromLogFile($"{logPath}.last")
- ?? throw new ApplicationException(App.ConfigNeedStartGenshin);
+ var gamePath = GetGamePathFromLogFile(logPath) ?? GetGamePathFromLogFile($"{logPath}.last");
+ if (gamePath == null) {
+ AnsiConsole.WriteLine(App.ConfigNeedStartGenshin);
+ Environment.Exit(-1);
+ }
+ GamePath = gamePath;
SentrySdk.AddBreadcrumb(GamePath.EndsWith("YuanShen.exe") ? "CN" : "OS", "GamePath");
}
diff --git a/YaeAchievement/src/Export.cs b/YaeAchievement/src/Export.cs
index 38ad678..f24f6d8 100644
--- a/YaeAchievement/src/Export.cs
+++ b/YaeAchievement/src/Export.cs
@@ -1,5 +1,4 @@
using System.ComponentModel;
-using System.Diagnostics;
using System.Net;
using System.Text;
using System.Text.Json;
@@ -8,7 +7,6 @@ using Microsoft.Win32;
using Spectre.Console;
using YaeAchievement.Outputs;
using YaeAchievement.Parsers;
-using YaeAchievement.res;
using YaeAchievement.Utilities;
// ReSharper disable UnusedMember.Local
diff --git a/YaeAchievement/src/GlobalVars.cs b/YaeAchievement/src/GlobalVars.cs
index 0d9750b..88a130f 100644
--- a/YaeAchievement/src/GlobalVars.cs
+++ b/YaeAchievement/src/GlobalVars.cs
@@ -1,4 +1,7 @@
-using System.Diagnostics.CodeAnalysis;
+global using System.Diagnostics;
+global using YaeAchievement.res;
+
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Proto;
diff --git a/YaeAchievement/src/Parsers/AchievementAllDataNotify.cs b/YaeAchievement/src/Parsers/AchievementAllDataNotify.cs
index 4c174b7..e45562e 100644
--- a/YaeAchievement/src/Parsers/AchievementAllDataNotify.cs
+++ b/YaeAchievement/src/Parsers/AchievementAllDataNotify.cs
@@ -2,7 +2,6 @@
using System.Text.Json.Serialization;
using Google.Protobuf;
using Spectre.Console;
-using YaeAchievement.res;
using YaeAchievement.Utilities;
namespace YaeAchievement.Parsers;
@@ -31,7 +30,7 @@ public sealed class AchievementAllDataNotify {
private static AchievementAllDataNotify? Instance { get; set; }
public static bool OnReceive(BinaryReader reader) {
- var bytes = reader.ReadBytes(reader.ReadInt32());
+ var bytes = reader.ReadBytes();
CacheFile.Write("achievement_data", bytes);
Instance = ParseFrom(bytes);
return true;
diff --git a/YaeAchievement/src/Parsers/PlayerStoreNotify.cs b/YaeAchievement/src/Parsers/PlayerStoreNotify.cs
index 08138b1..da93dad 100644
--- a/YaeAchievement/src/Parsers/PlayerStoreNotify.cs
+++ b/YaeAchievement/src/Parsers/PlayerStoreNotify.cs
@@ -20,7 +20,7 @@ public sealed class PlayerStoreNotify {
public static PlayerStoreNotify Instance { get; } = new ();
public static bool OnReceive(BinaryReader reader) {
- var bytes = reader.ReadBytes(reader.ReadInt32());
+ var bytes = reader.ReadBytes();
Instance.ParseFrom(bytes);
return true;
}
diff --git a/YaeAchievement/src/Program.cs b/YaeAchievement/src/Program.cs
index df9ba3f..8362c75 100644
--- a/YaeAchievement/src/Program.cs
+++ b/YaeAchievement/src/Program.cs
@@ -2,7 +2,6 @@
using System.Text;
using Spectre.Console;
using YaeAchievement.Parsers;
-using YaeAchievement.res;
using YaeAchievement.Utilities;
using static YaeAchievement.Utils;
diff --git a/YaeAchievement/src/Utilities/Extensions/Stream.cs b/YaeAchievement/src/Utilities/Extensions/Stream.cs
index b515cf1..b1faa23 100644
--- a/YaeAchievement/src/Utilities/Extensions/Stream.cs
+++ b/YaeAchievement/src/Utilities/Extensions/Stream.cs
@@ -1,10 +1,29 @@
using System.Runtime.CompilerServices;
+using Spectre.Console;
// ReSharper disable CheckNamespace
namespace Google.Protobuf;
-public static class CodedInputStreamExtensions {
+internal static class BinaryReaderExtensions {
+
+ public static byte[] ReadBytes(this BinaryReader reader) {
+ try {
+ var length = reader.ReadInt32();
+ if (length is < 0 or > 114514 * 2) {
+ throw new ArgumentException(nameof(length));
+ }
+ return reader.ReadBytes(length);
+ } catch (Exception e) when (e is IOException or ArgumentException) {
+ AnsiConsole.WriteLine(App.StreamReadDataFail);
+ Environment.Exit(-1);
+ throw new UnreachableException();
+ }
+ }
+
+}
+
+internal static class CodedInputStreamExtensions {
[UnsafeAccessor(UnsafeAccessorKind.Method)]
private static extern byte[] ReadRawBytes(CodedInputStream stream, int size);
diff --git a/YaeAchievement/src/Utilities/GameProcess.cs b/YaeAchievement/src/Utilities/GameProcess.cs
index daf72c9..d925586 100644
--- a/YaeAchievement/src/Utilities/GameProcess.cs
+++ b/YaeAchievement/src/Utilities/GameProcess.cs
@@ -29,7 +29,16 @@ public sealed unsafe class GameProcess {
};
var wd = Path.GetDirectoryName(path)!;
if (!Native.CreateProcess(path, ref cmdLines, null, null, false, flags, null, wd, si, out var pi)) {
- throw new ApplicationException($"CreateProcess fail: {Marshal.GetLastPInvokeErrorMessage()}");
+ var argumentData = new Dictionary {
+ { "path", path },
+ { "workdir", wd },
+ { "file_exists", File.Exists(path) },
+ };
+ throw new ApplicationException($"CreateProcess fail: {Marshal.GetLastPInvokeErrorMessage()}") {
+ Data = {
+ { "sentry:context:Arguments", argumentData }
+ }
+ };
}
Id = pi.dwProcessId;
Handle = pi.hProcess;
diff --git a/YaeAchievement/src/Utilities/SelectionPromptCompat.cs b/YaeAchievement/src/Utilities/SelectionPromptCompat.cs
index c34d773..74a7571 100644
--- a/YaeAchievement/src/Utilities/SelectionPromptCompat.cs
+++ b/YaeAchievement/src/Utilities/SelectionPromptCompat.cs
@@ -1,5 +1,4 @@
using Spectre.Console;
-using YaeAchievement.res;
namespace YaeAchievement.Utilities;
diff --git a/YaeAchievement/src/Utils.cs b/YaeAchievement/src/Utils.cs
index b563caa..1735ae7 100644
--- a/YaeAchievement/src/Utils.cs
+++ b/YaeAchievement/src/Utils.cs
@@ -1,5 +1,4 @@
using System.ComponentModel;
-using System.Diagnostics;
using System.Globalization;
using System.IO.Pipes;
using System.Net;
@@ -11,7 +10,6 @@ using Windows.Win32.Foundation;
using Windows.Win32.System.Console;
using Proto;
using Spectre.Console;
-using YaeAchievement.res;
using YaeAchievement.Utilities;
namespace YaeAchievement;
@@ -109,35 +107,42 @@ public static class Utils {
}
public static async Task CheckUpdate(bool useLocalLib) {
- var versionData = await StartSpinnerAsync(App.UpdateChecking, _ => GetBucketFile("schicksal/version"));
- var versionInfo = UpdateInfo.Parser.ParseFrom(versionData)!;
- if (GlobalVars.AppVersionCode < versionInfo.VersionCode) {
- AnsiConsole.WriteLine(App.UpdateNewVersion, GlobalVars.AppVersionName, versionInfo.VersionName);
- AnsiConsole.WriteLine(App.UpdateDescription, versionInfo.Description);
- if (versionInfo.EnableAutoUpdate) {
- var newBin = await StartSpinnerAsync(App.UpdateDownloading, _ => GetBucketFile(versionInfo.PackageLink));
- var tmpPath = Path.GetTempFileName();
- var updaterPath = Path.Combine(GlobalVars.DataPath, "update.exe");
- await using (var dstStream = File.Open($"{GlobalVars.DataPath}/update.exe", FileMode.Create)) {
- await using var srcStream = typeof(Program).Assembly.GetManifestResourceStream("updater")!;
- await srcStream.CopyToAsync(dstStream);
+ try {
+ var versionData = await StartSpinnerAsync(App.UpdateChecking, _ => GetBucketFile("schicksal/version"));
+ var versionInfo = UpdateInfo.Parser.ParseFrom(versionData)!;
+ if (GlobalVars.AppVersionCode < versionInfo.VersionCode) {
+ AnsiConsole.WriteLine(App.UpdateNewVersion, GlobalVars.AppVersionName, versionInfo.VersionName);
+ AnsiConsole.WriteLine(App.UpdateDescription, versionInfo.Description);
+ if (versionInfo.EnableAutoUpdate) {
+ var newBin = await StartSpinnerAsync(App.UpdateDownloading, _ => GetBucketFile(versionInfo.PackageLink));
+ var tmpPath = Path.GetTempFileName();
+ var updaterPath = Path.Combine(GlobalVars.DataPath, "update.exe");
+ await using (var dstStream = File.Open($"{GlobalVars.DataPath}/update.exe", FileMode.Create)) {
+ await using var srcStream = typeof(Program).Assembly.GetManifestResourceStream("updater")!;
+ await srcStream.CopyToAsync(dstStream);
+ }
+ await File.WriteAllBytesAsync(tmpPath, newBin);
+ ShellOpen(updaterPath, $"{Environment.ProcessId} \"{tmpPath}\"");
+ await StartSpinnerAsync(App.UpdateChecking, _ => Task.Delay(1919810));
+ GlobalVars.PauseOnExit = false;
+ Environment.Exit(0);
+ }
+ AnsiConsole.MarkupLine($"[link]{App.DownloadLink}[/]", versionInfo.PackageLink);
+ if (versionInfo.ForceUpdate) {
+ Environment.Exit(0);
}
- await File.WriteAllBytesAsync(tmpPath, newBin);
- ShellOpen(updaterPath, $"{Environment.ProcessId} \"{tmpPath}\"");
- await StartSpinnerAsync(App.UpdateChecking, _ => Task.Delay(1919810));
- GlobalVars.PauseOnExit = false;
- Environment.Exit(0);
}
- AnsiConsole.MarkupLine($"[link]{App.DownloadLink}[/]", versionInfo.PackageLink);
- if (versionInfo.ForceUpdate) {
- Environment.Exit(0);
+ if (versionInfo.EnableLibDownload && !useLocalLib) {
+ var data = await GetBucketFile("schicksal/lic.dll");
+ await File.WriteAllBytesAsync(GlobalVars.LibFilePath, data); // 要求重启电脑
}
+ _updateInfo = versionInfo;
+ } catch (IOException e) when ((uint) e.HResult == 0x80070020) { // ERROR_SHARING_VIOLATION
+ // IO_SharingViolation_File
+ // The process cannot access the file '{0}' because it is being used by another process.
+ AnsiConsole.WriteLine("文件 {0} 被其它程序占用,请 重启电脑 或 解除文件占用 后重试。", e.Message[36..^46]);
+ Environment.Exit(-1);
}
- if (versionInfo.EnableLibDownload && !useLocalLib) {
- var data = await GetBucketFile("schicksal/lic.dll");
- await File.WriteAllBytesAsync(GlobalVars.LibFilePath, data);
- }
- _updateInfo = versionInfo;
}
// ReSharper disable once UnusedMethodReturnValue.Global