mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-15 02:48:12 +08:00
auto identify field ids
This commit is contained in:
9
res/App.Designer.cs
generated
9
res/App.Designer.cs
generated
@@ -393,5 +393,14 @@ namespace YaeAchievement.res {
|
|||||||
return ResourceManager.GetString("VcRuntimeInstalling", resourceCulture);
|
return ResourceManager.GetString("VcRuntimeInstalling", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Please update game and retry..
|
||||||
|
/// </summary>
|
||||||
|
internal static string WaitMetadataUpdate {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("WaitMetadataUpdate", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,4 +137,7 @@ Input a number (0-8): </value>
|
|||||||
<data name="ExportToTauriFail" xml:space="preserve">
|
<data name="ExportToTauriFail" xml:space="preserve">
|
||||||
<value>Please launch/update Teyvat Guide and retry.</value>
|
<value>Please launch/update Teyvat Guide and retry.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="WaitMetadataUpdate" xml:space="preserve">
|
||||||
|
<value>Please update game and retry.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -127,4 +127,7 @@
|
|||||||
<data name="ExportToTauriSuccess" xml:space="preserve">
|
<data name="ExportToTauriSuccess" xml:space="preserve">
|
||||||
<value>在 Teyvat Guide 进行下一步操作</value>
|
<value>在 Teyvat Guide 进行下一步操作</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="WaitMetadataUpdate" xml:space="preserve">
|
||||||
|
<value>当前元数据版本不匹配,请更新原神至最新版本或等待元数据更新后重试。</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
option csharp_namespace = "Proto";
|
|
||||||
|
|
||||||
message Achievement {
|
|
||||||
enum Status {
|
|
||||||
INVALID = 0;
|
|
||||||
UNFINISHED = 1;
|
|
||||||
FINISHED = 2;
|
|
||||||
REWARD_TAKEN = 3;
|
|
||||||
}
|
|
||||||
uint32 timestamp = 11;
|
|
||||||
uint32 current = 5;
|
|
||||||
uint32 total = 15;
|
|
||||||
uint32 id = 2;
|
|
||||||
Status status = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AchievementAllDataNotify {
|
|
||||||
repeated Achievement list = 4;
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,14 @@ syntax = "proto3";
|
|||||||
|
|
||||||
option csharp_namespace = "Proto";
|
option csharp_namespace = "Proto";
|
||||||
|
|
||||||
|
message AchievementProtoFieldInfo {
|
||||||
|
uint32 id = 1;
|
||||||
|
uint32 status = 2;
|
||||||
|
uint32 total_progress = 3;
|
||||||
|
uint32 current_progress = 4;
|
||||||
|
uint32 finish_timestamp = 5;
|
||||||
|
}
|
||||||
|
|
||||||
message AchievementItem {
|
message AchievementItem {
|
||||||
uint32 pre = 1;
|
uint32 pre = 1;
|
||||||
uint32 group = 2;
|
uint32 group = 2;
|
||||||
@@ -13,4 +21,5 @@ message AchievementInfo {
|
|||||||
string version = 1;
|
string version = 1;
|
||||||
map<uint32, string> group = 2;
|
map<uint32, string> group = 2;
|
||||||
map<uint32, AchievementItem> items = 3;
|
map<uint32, AchievementItem> items = 3;
|
||||||
|
AchievementProtoFieldInfo pb_info = 4;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ syntax = "proto3";
|
|||||||
option csharp_namespace = "Proto";
|
option csharp_namespace = "Proto";
|
||||||
|
|
||||||
message UpdateInfo {
|
message UpdateInfo {
|
||||||
uint32 versionCode = 1;
|
uint32 version_code = 1;
|
||||||
string versionName = 2;
|
string version_name = 2;
|
||||||
string description = 3;
|
string description = 3;
|
||||||
string packageLink = 4;
|
string package_link = 4;
|
||||||
bool forceUpdate = 5;
|
bool force_update = 5;
|
||||||
bool enableLibDownload = 6;
|
bool enable_lib_download = 6;
|
||||||
bool enableAutoDownload = 7;
|
bool enable_auto_update = 7;
|
||||||
string currentCNGameHash = 8;
|
string current_cn_hash = 8;
|
||||||
string currentOSGameHash = 9;
|
string current_os_hash = 9;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ using System.Text;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Proto;
|
using YaeAchievement.Parsers;
|
||||||
using YaeAchievement.res;
|
using YaeAchievement.res;
|
||||||
using static Proto.Achievement.Types;
|
|
||||||
|
|
||||||
namespace YaeAchievement;
|
namespace YaeAchievement;
|
||||||
|
|
||||||
@@ -120,10 +119,10 @@ public static class Export {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void ToPaimon(AchievementAllDataNotify data) {
|
private static void ToPaimon(AchievementAllDataNotify data) {
|
||||||
var info = LoadAchievementInfo().Items.ToDictionary(pair => pair.Key, pair => pair.Value.Group);
|
var info = GlobalVars.AchievementInfo.Items.ToDictionary(pair => pair.Key, pair => pair.Value.Group);
|
||||||
var final = new Dictionary<string, Dictionary<uint, Dictionary<uint, bool>>> {
|
var final = new Dictionary<string, Dictionary<uint, Dictionary<uint, bool>>> {
|
||||||
["achievement"] = data.List
|
["achievement"] = data.AchievementList
|
||||||
.Where(achievement => achievement.Status is Status.Finished or Status.RewardTaken)
|
.Where(achievement => achievement.Status is AchievementStatus.Finished or AchievementStatus.RewardTaken)
|
||||||
.Where(achievement => info.ContainsKey(achievement.Id))
|
.Where(achievement => info.ContainsKey(achievement.Id))
|
||||||
.GroupBy(achievement => info[achievement.Id], achievement => achievement.Id)
|
.GroupBy(achievement => info[achievement.Id], achievement => achievement.Id)
|
||||||
.OrderBy(group => group.Key)
|
.OrderBy(group => group.Key)
|
||||||
@@ -137,7 +136,7 @@ public static class Export {
|
|||||||
|
|
||||||
private static void ToSeelie(AchievementAllDataNotify data) {
|
private static void ToSeelie(AchievementAllDataNotify data) {
|
||||||
var output = new Dictionary<uint, Dictionary<string, bool>>();
|
var output = new Dictionary<uint, Dictionary<string, bool>>();
|
||||||
foreach (var ach in data.List.Where(a => a.Status is Status.Finished or Status.RewardTaken)) {
|
foreach (var ach in data.AchievementList.Where(a => a.Status is AchievementStatus.Finished or AchievementStatus.RewardTaken)) {
|
||||||
output[ach.Id] = new Dictionary<string, bool> {
|
output[ach.Id] = new Dictionary<string, bool> {
|
||||||
["done"] = true
|
["done"] = true
|
||||||
};
|
};
|
||||||
@@ -153,23 +152,25 @@ public static class Export {
|
|||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
private static void ToCSV(AchievementAllDataNotify data) {
|
private static void ToCSV(AchievementAllDataNotify data) {
|
||||||
var info = LoadAchievementInfo();
|
var info = GlobalVars.AchievementInfo;
|
||||||
var outList = new List<List<object>>();
|
var outList = new List<List<object>>();
|
||||||
foreach (var ach in data.List.OrderBy(a => a.Id)) {
|
foreach (var ach in data.AchievementList.OrderBy(a => a.Id)) {
|
||||||
if (UnusedAchievement.Contains(ach.Id)) continue;
|
if (UnusedAchievement.Contains(ach.Id)) continue;
|
||||||
if (!info.Items.TryGetValue(ach.Id, out var achInfo) || achInfo == null) {
|
if (!info.Items.TryGetValue(ach.Id, out var achInfo) || achInfo == null) {
|
||||||
Console.WriteLine($@"Unable to find {ach.Id} in metadata.");
|
Console.WriteLine($@"Unable to find {ach.Id} in metadata.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var finishAt = "";
|
var finishAt = "";
|
||||||
if (ach.Timestamp != 0) {
|
if (ach.FinishTimestamp != 0) {
|
||||||
var ts = Convert.ToInt64(ach.Timestamp);
|
var ts = Convert.ToInt64(ach.FinishTimestamp);
|
||||||
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||||
}
|
}
|
||||||
var current = ach.Status != Status.Unfinished ? ach.Current == 0 ? ach.Total : ach.Current : ach.Current;
|
var current = ach.Status != AchievementStatus.Unfinished
|
||||||
|
? ach.CurrentProgress == 0 ? ach.TotalProgress : ach.CurrentProgress
|
||||||
|
: ach.CurrentProgress;
|
||||||
outList.Add([
|
outList.Add([
|
||||||
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
|
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
|
||||||
achInfo.Description, current, ach.Total, finishAt
|
achInfo.Description, current, ach.TotalProgress, finishAt
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
|
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
|
||||||
@@ -194,13 +195,13 @@ public static class Export {
|
|||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
private static Dictionary<string, object> ExportToUIAFApp(AchievementAllDataNotify data) {
|
private static Dictionary<string, object> ExportToUIAFApp(AchievementAllDataNotify data) {
|
||||||
var output = data.List
|
var output = data.AchievementList
|
||||||
.Where(a => (uint)a.Status > 1 || a.Current > 0)
|
.Where(a => (uint)a.Status > 1 || a.CurrentProgress > 0)
|
||||||
.Select(ach => new Dictionary<string, uint> {
|
.Select(ach => new Dictionary<string, uint> {
|
||||||
["id"] = ach.Id,
|
["id"] = ach.Id,
|
||||||
["status"] = (uint) ach.Status,
|
["status"] = (uint) ach.Status,
|
||||||
["current"] = ach.Current,
|
["current"] = ach.CurrentProgress,
|
||||||
["timestamp"] = ach.Timestamp
|
["timestamp"] = ach.FinishTimestamp
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
@@ -225,21 +226,16 @@ public static class Export {
|
|||||||
|
|
||||||
private static readonly List<uint> UnusedAchievement = [ 84517 ];
|
private static readonly List<uint> UnusedAchievement = [ 84517 ];
|
||||||
|
|
||||||
private static string ToDesc(this Status status) {
|
private static string ToDesc(this AchievementStatus status) {
|
||||||
return status switch {
|
return status switch {
|
||||||
Status.Invalid => App.StatusInvalid,
|
AchievementStatus.Invalid => App.StatusInvalid,
|
||||||
Status.Finished => App.StatusFinished,
|
AchievementStatus.Finished => App.StatusFinished,
|
||||||
Status.Unfinished => App.StatusUnfinished,
|
AchievementStatus.Unfinished => App.StatusUnfinished,
|
||||||
Status.RewardTaken => App.StatusRewardTaken,
|
AchievementStatus.RewardTaken => App.StatusRewardTaken,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AchievementInfo LoadAchievementInfo() {
|
|
||||||
var b = Utils.GetBucketFileAsByteArray("schicksal/metadata");
|
|
||||||
return AchievementInfo.Parser.ParseFrom(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
||||||
// ReSharper disable once LocalizableElement
|
// ReSharper disable once LocalizableElement
|
||||||
Console.WriteLine($"{msg}: {ex.Message}");
|
Console.WriteLine($"{msg}: {ex.Message}");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Proto;
|
||||||
|
|
||||||
namespace YaeAchievement;
|
namespace YaeAchievement;
|
||||||
|
|
||||||
@@ -26,6 +27,9 @@ public static class GlobalVars {
|
|||||||
public const string PipeName = "YaeAchievementPipe";
|
public const string PipeName = "YaeAchievementPipe";
|
||||||
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
||||||
|
|
||||||
|
public static AchievementInfo AchievementInfo { get; }
|
||||||
|
= AchievementInfo.Parser.ParseFrom(Utils.GetBucketFileAsByteArray("schicksal/metadata"));
|
||||||
|
|
||||||
static GlobalVars() {
|
static GlobalVars() {
|
||||||
Directory.CreateDirectory(DataPath);
|
Directory.CreateDirectory(DataPath);
|
||||||
Directory.CreateDirectory(CachePath);
|
Directory.CreateDirectory(CachePath);
|
||||||
|
|||||||
0
src/Outputs/.gitkeep
Normal file
0
src/Outputs/.gitkeep
Normal file
97
src/Parsers/AchievementAllDataNotify.cs
Normal file
97
src/Parsers/AchievementAllDataNotify.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using YaeAchievement.res;
|
||||||
|
|
||||||
|
namespace YaeAchievement.Parsers;
|
||||||
|
|
||||||
|
public enum AchievementStatus {
|
||||||
|
Invalid,
|
||||||
|
Unfinished,
|
||||||
|
Finished,
|
||||||
|
RewardTaken,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AchievementItem {
|
||||||
|
|
||||||
|
public uint Id { get; init; }
|
||||||
|
public uint TotalProgress { get; init; }
|
||||||
|
public uint CurrentProgress { get; init; }
|
||||||
|
public uint FinishTimestamp { get; init; }
|
||||||
|
public AchievementStatus Status { get; init; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AchievementAllDataNotify {
|
||||||
|
|
||||||
|
public List<AchievementItem> AchievementList { get; private init; } = [];
|
||||||
|
|
||||||
|
public static AchievementAllDataNotify ParseFrom(byte[] bytes) {
|
||||||
|
using var stream = new CodedInputStream(bytes);
|
||||||
|
uint tag;
|
||||||
|
var data = new List<Dictionary<uint, uint>>();
|
||||||
|
while ((tag = stream.ReadTag()) != 0) {
|
||||||
|
if ((tag & 7) == 2) { // is LengthDelimited
|
||||||
|
var dict = new Dictionary<uint, uint>();
|
||||||
|
using var eStream = new CodedInputStream(ReadRawBytes(stream, stream.ReadLength()));
|
||||||
|
while ((tag = eStream.ReadTag()) != 0) {
|
||||||
|
if ((tag & 7) != 0) { // not VarInt
|
||||||
|
dict = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dict[tag >> 3] = eStream.ReadUInt32();
|
||||||
|
}
|
||||||
|
if (dict != null) {
|
||||||
|
data.Add(dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.Count == 0) {
|
||||||
|
return new AchievementAllDataNotify();
|
||||||
|
}
|
||||||
|
uint tId, sId, iId, currentId, totalId;
|
||||||
|
if (data.Count > 20) { /* uwu */
|
||||||
|
(tId, var cnt) = data // ↓ 2020-09-15 04:15:14
|
||||||
|
.GroupKeys(value => value > 1600114514).Select(g => (g.Key, g.Count())).MaxBy(p => p.Item2);
|
||||||
|
sId = data // FINISHED ↓ ↓ REWARD_TAKEN
|
||||||
|
.GroupKeys(value => value is 2 or 3).First(g => g.Count() == cnt).Key;
|
||||||
|
iId = data // ↓ id: 8xxxx
|
||||||
|
.GroupKeys(value => value / 10000 % 10 == 8).MaxBy(g => g.Count())!.Key;
|
||||||
|
(currentId, totalId) = data
|
||||||
|
.Where(d => d[sId] is 2 or 3)
|
||||||
|
.Select(d => d.ToDictionary().RemoveValues(tId, sId, iId).ToArray())
|
||||||
|
.Where(d => d.Length == 2 && d[0].Value != d[1].Value)
|
||||||
|
.GroupBy(a => a[0].Value > a[1].Value ? (a[0].Key, a[1].Key) : (a[1].Key, a[0].Key))
|
||||||
|
.Select(g => (FieldIds: g.Key, Count: g.Count()))
|
||||||
|
.MaxBy(p => p.Count)
|
||||||
|
.FieldIds;
|
||||||
|
#if DEBUG
|
||||||
|
// ReSharper disable once LocalizableElement
|
||||||
|
Console.WriteLine($"Id={iId}, Status={sId}, Total={totalId}, Current={currentId}, Timestamp={tId}");
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
var info = GlobalVars.AchievementInfo.PbInfo; // ...
|
||||||
|
iId = info.Id;
|
||||||
|
tId = info.FinishTimestamp;
|
||||||
|
sId = info.Status;
|
||||||
|
totalId = info.TotalProgress;
|
||||||
|
currentId = info.CurrentProgress;
|
||||||
|
if (data.Any(dict => !dict.ContainsKey(iId) || !dict.ContainsKey(sId) || !dict.ContainsKey(totalId))) {
|
||||||
|
Console.WriteLine(App.WaitMetadataUpdate);
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new AchievementAllDataNotify {
|
||||||
|
AchievementList = data.Select(dict => new AchievementItem {
|
||||||
|
Id = dict[iId],
|
||||||
|
Status = (AchievementStatus) dict[sId],
|
||||||
|
TotalProgress = dict[totalId],
|
||||||
|
CurrentProgress = dict.GetValueOrDefault(currentId),
|
||||||
|
FinishTimestamp = dict.GetValueOrDefault(tId),
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnsafeAccessor(UnsafeAccessorKind.Method)]
|
||||||
|
private static extern byte[] ReadRawBytes(CodedInputStream stream, int size);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Proto;
|
|
||||||
using YaeAchievement;
|
using YaeAchievement;
|
||||||
|
using YaeAchievement.Parsers;
|
||||||
using YaeAchievement.res;
|
using YaeAchievement.res;
|
||||||
using static YaeAchievement.Utils;
|
using static YaeAchievement.Utils;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ var historyCache = new CacheFile("ExportData");
|
|||||||
|
|
||||||
AchievementAllDataNotify? data = null;
|
AchievementAllDataNotify? data = null;
|
||||||
try {
|
try {
|
||||||
data = AchievementAllDataNotify.Parser.ParseFrom(historyCache.Read().Content);
|
data = AchievementAllDataNotify.ParseFrom(historyCache.Read().Content.ToByteArray());
|
||||||
} catch (Exception) { /* ignored */ }
|
} catch (Exception) { /* ignored */ }
|
||||||
|
|
||||||
if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null) {
|
if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null) {
|
||||||
@@ -43,7 +43,7 @@ if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null)
|
|||||||
StartAndWaitResult(AppConfig.GamePath, str => {
|
StartAndWaitResult(AppConfig.GamePath, str => {
|
||||||
GlobalVars.UnexpectedExit = false;
|
GlobalVars.UnexpectedExit = false;
|
||||||
var bytes = Convert.FromBase64String(str);
|
var bytes = Convert.FromBase64String(str);
|
||||||
var list = AchievementAllDataNotify.Parser.ParseFrom(bytes);
|
var list = AchievementAllDataNotify.ParseFrom(bytes);
|
||||||
historyCache.Write(bytes);
|
historyCache.Write(bytes);
|
||||||
Export.Choose(list);
|
Export.Choose(list);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
16
src/Utilities/Extensions/Collection.cs
Normal file
16
src/Utilities/Extensions/Collection.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace System.Collections.Generic;
|
||||||
|
|
||||||
|
public static class Collection {
|
||||||
|
|
||||||
|
public static IDictionary<TKey, TValue> RemoveValues<TKey, TValue>(
|
||||||
|
this IDictionary<TKey, TValue> dictionary,
|
||||||
|
params TKey[] keys
|
||||||
|
) {
|
||||||
|
foreach (var key in keys) {
|
||||||
|
dictionary.Remove(key);
|
||||||
|
}
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
src/Utilities/Extensions/Enumerable.cs
Normal file
14
src/Utilities/Extensions/Enumerable.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
|
||||||
|
namespace System.Linq;
|
||||||
|
|
||||||
|
public static class Enumerable {
|
||||||
|
|
||||||
|
public static IEnumerable<IGrouping<TKey, TKey>> GroupKeys<TKey, TValue>(
|
||||||
|
this IEnumerable<Dictionary<TKey, TValue>> source,
|
||||||
|
Func<TValue, bool> condition
|
||||||
|
) where TKey : notnull {
|
||||||
|
return source.SelectMany(dict => dict.Where(pair => condition(pair.Value)).Select(pair => pair.Key)).GroupBy(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
src/Utils.cs
16
src/Utils.cs
@@ -93,7 +93,7 @@ public static class Utils {
|
|||||||
if (GlobalVars.AppVersionCode < info.VersionCode) {
|
if (GlobalVars.AppVersionCode < info.VersionCode) {
|
||||||
Console.WriteLine(App.UpdateNewVersion, GlobalVars.AppVersionName, info.VersionName);
|
Console.WriteLine(App.UpdateNewVersion, GlobalVars.AppVersionName, info.VersionName);
|
||||||
Console.WriteLine(App.UpdateDescription, info.Description);
|
Console.WriteLine(App.UpdateDescription, info.Description);
|
||||||
if (info.EnableAutoDownload) {
|
if (info.EnableAutoUpdate) {
|
||||||
Console.WriteLine(App.UpdateDownloading);
|
Console.WriteLine(App.UpdateDownloading);
|
||||||
var tmpPath = Path.GetTempFileName();
|
var tmpPath = Path.GetTempFileName();
|
||||||
File.WriteAllBytes(tmpPath, GetBucketFileAsByteArray(info.PackageLink));
|
File.WriteAllBytes(tmpPath, GetBucketFileAsByteArray(info.PackageLink));
|
||||||
@@ -205,22 +205,8 @@ public static class Utils {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckGenshinIsLatestVersion(string path) {
|
|
||||||
#if DEBUG
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
if (!File.Exists(path)) return false;
|
|
||||||
var hash = File.ReadAllBytes(path).MD5Hash();
|
|
||||||
return hash == _updateInfo.CurrentCNGameHash || hash == _updateInfo.CurrentOSGameHash;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||||
public static Thread StartAndWaitResult(string exePath, Func<string, bool> onReceive) {
|
public static Thread StartAndWaitResult(string exePath, Func<string, bool> onReceive) {
|
||||||
if (!CheckGenshinIsLatestVersion(exePath)) {
|
|
||||||
Console.WriteLine(App.GenshinHashError);
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
|
||||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||||
try {
|
try {
|
||||||
File.Delete(GlobalVars.LibFilePath);
|
File.Delete(GlobalVars.LibFilePath);
|
||||||
|
|||||||
Reference in New Issue
Block a user