diff --git a/YaeAchievement.csproj b/YaeAchievement.csproj
index b769af7..6ad305e 100644
--- a/YaeAchievement.csproj
+++ b/YaeAchievement.csproj
@@ -9,4 +9,8 @@
res\app.manifest
+
+
+
+
diff --git a/src/Events/IEventBase.cs b/src/Events/IEventBase.cs
deleted file mode 100644
index 8bcec9c..0000000
--- a/src/Events/IEventBase.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace YaeAchievement.Events;
-
-public interface IEventBase {
-
-}
\ No newline at end of file
diff --git a/src/Events/PipeRecvEvent.cs b/src/Events/PipeRecvEvent.cs
deleted file mode 100644
index 42dbe6e..0000000
--- a/src/Events/PipeRecvEvent.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace YaeAchievement.Events;
-
-public class PipeRecvEvent : IEventBase {
-
- private readonly byte[] _data;
-
- public PipeRecvEvent(string content) {
- _data = Convert.FromBase64String(content);
- }
-
- public byte[] GetData() {
- return _data;
- }
-}
\ No newline at end of file
diff --git a/src/Program.cs b/src/Program.cs
index 406ebc2..c18b624 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -2,67 +2,13 @@
using System.Diagnostics;
using System.IO.Pipes;
using YaeAchievement;
-using YaeAchievement.Events;
using YaeAchievement.Win32;
-// ReSharper disable once UnusedLocalFunctionReturnValue
-Thread StartAndWaitResult(string exePath, Func onEvent) {
- if (!Injector.CreateProcess(exePath, out var hProcess, out var hThread, out var pid)) {
- Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("ICreateProcess fail"));
- }
- if (Injector.LoadLibraryAndInject(hProcess, GlobalVars.LibName) != 0) {
- if (!Native.TerminateProcess(hProcess, 0)) {
- Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail"));
- }
- }
- if (Native.ResumeThread(hThread) == 0xFFFFFFFF) {
- var e = new Win32Exception();
- if (!Native.TerminateProcess(hProcess, 0)) {
- new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail");
- }
- Environment.Exit(e.PrintMsgAndReturnErrCode("ResumeThread fail"));
- }
- if (!Native.CloseHandle(hProcess)) {
- Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("CloseHandle fail"));
- }
- var proc = Process.GetProcessById(Convert.ToInt32(pid));
- proc.EnableRaisingEvents = true;
- proc.Exited += (_, _) => {
- if (GlobalVars.UnexpectedExit) {
- Console.WriteLine($"Game process exit at {proc.ExitTime:HH:mm:ss}");
- Environment.Exit(114514);
- }
- };
- var ts = new ThreadStart(() => {
- var server = new NamedPipeServerStream(GlobalVars.PipeName);
- server.WaitForConnection();
- var reader = new StreamReader(server);
- while (true) {
- var line = reader.ReadLine();
- if (line?.Length > 0) {
- if (onEvent(new PipeRecvEvent(line))) {
- break;
- }
- server.Disconnect();
- server.WaitForConnection();
- }
- }
- });
- var th = new Thread(ts);
- th.Start();
- return th;
-}
-StartAndWaitResult(@"D:\Genshin Impact Dev\2.8\YuanShen.exe", evt => {
- switch (evt) {
- case PipeRecvEvent @event: {
- var bytes = @event.GetData();
- GlobalVars.UnexpectedExit = false;
- Console.WriteLine("On PRE");
- Console.WriteLine(Convert.ToBase64String(bytes));
- return false;
- }
- default:
- return false;
- }
+Utils.StartAndWaitResult(@"D:\Genshin Impact Dev\2.8\YuanShen.exe", str => {
+ GlobalVars.UnexpectedExit = false;
+ //AchievementAllDataNotify.Parser.ParseFrom(Convert.FromBase64String(str)).List;
+ //var notify = Serializer.Deserialize(stream)!;
+ //notify.list;
+ return false;
});
diff --git a/src/Proto/AchievementAllDataNotify.cs b/src/Proto/AchievementAllDataNotify.cs
new file mode 100644
index 0000000..6337a5f
--- /dev/null
+++ b/src/Proto/AchievementAllDataNotify.cs
@@ -0,0 +1,572 @@
+//
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: AchievementAllDataNotify.proto
+//
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+/// Holder for reflection information generated from AchievementAllDataNotify.proto
+public static partial class AchievementAllDataNotifyReflection {
+
+ #region Descriptor
+ /// File descriptor for AchievementAllDataNotify.proto
+ public static pbr::FileDescriptor Descriptor {
+ get { return descriptor; }
+ }
+ private static pbr::FileDescriptor descriptor;
+
+ static AchievementAllDataNotifyReflection() {
+ byte[] descriptorData = global::System.Convert.FromBase64String(
+ string.Concat(
+ "Ch5BY2hpZXZlbWVudEFsbERhdGFOb3RpZnkucHJvdG8iugEKC0FjaGlldmVt",
+ "ZW50EgoKAmlkGAEgASgNEiMKBnN0YXR1cxgCIAEoDjITLkFjaGlldmVtZW50",
+ "LlN0YXR1cxIPCgdjdXJyZW50GAMgASgNEg8KB3JlcXVpcmUYBCABKA0SEQoJ",
+ "dGltZXN0YW1wGAUgASgNIkUKBlN0YXR1cxILCgdJTlZBTElEEAASDgoKVU5G",
+ "SU5JU0hFRBABEgwKCEZJTklTSEVEEAISEAoMUkVXQVJEX1RBS0VOEAMiNgoY",
+ "QWNoaWV2ZW1lbnRBbGxEYXRhTm90aWZ5EhoKBGxpc3QYBSADKAsyDC5BY2hp",
+ "ZXZlbWVudGIGcHJvdG8z"));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+ new pbr::FileDescriptor[] { },
+ new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Achievement), global::Achievement.Parser, new[]{ "ID", "Status", "Current", "Require", "Timestamp" }, null, new[]{ typeof(global::Achievement.Types.Status) }, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::AchievementAllDataNotify), global::AchievementAllDataNotify.Parser, new[]{ "List" }, null, null, null, null)
+ }));
+ }
+ #endregion
+
+}
+#region Messages
+public sealed partial class Achievement : pb::IMessage
+#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+#endif
+{
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Achievement());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::AchievementAllDataNotifyReflection.Descriptor.MessageTypes[0]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public Achievement() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public Achievement(Achievement other) : this() {
+ id_ = other.id_;
+ status_ = other.status_;
+ current_ = other.current_;
+ require_ = other.require_;
+ timestamp_ = other.timestamp_;
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public Achievement Clone() {
+ return new Achievement(this);
+ }
+
+ /// Field number for the "id" field.
+ public const int IDFieldNumber = 1;
+ private uint id_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public uint ID {
+ get { return id_; }
+ set {
+ id_ = value;
+ }
+ }
+
+ /// Field number for the "status" field.
+ public const int StatusFieldNumber = 2;
+ private global::Achievement.Types.Status status_ = global::Achievement.Types.Status.Invalid;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public global::Achievement.Types.Status Status {
+ get { return status_; }
+ set {
+ status_ = value;
+ }
+ }
+
+ /// Field number for the "current" field.
+ public const int CurrentFieldNumber = 3;
+ private uint current_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public uint Current {
+ get { return current_; }
+ set {
+ current_ = value;
+ }
+ }
+
+ /// Field number for the "require" field.
+ public const int RequireFieldNumber = 4;
+ private uint require_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public uint Require {
+ get { return require_; }
+ set {
+ require_ = value;
+ }
+ }
+
+ /// Field number for the "timestamp" field.
+ public const int TimestampFieldNumber = 5;
+ private uint timestamp_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public uint Timestamp {
+ get { return timestamp_; }
+ set {
+ timestamp_ = value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as Achievement);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(Achievement other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (ID != other.ID) return false;
+ if (Status != other.Status) return false;
+ if (Current != other.Current) return false;
+ if (Require != other.Require) return false;
+ if (Timestamp != other.Timestamp) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (ID != 0) hash ^= ID.GetHashCode();
+ if (Status != global::Achievement.Types.Status.Invalid) hash ^= Status.GetHashCode();
+ if (Current != 0) hash ^= Current.GetHashCode();
+ if (Require != 0) hash ^= Require.GetHashCode();
+ if (Timestamp != 0) hash ^= Timestamp.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ if (ID != 0) {
+ output.WriteRawTag(8);
+ output.WriteUInt32(ID);
+ }
+ if (Status != global::Achievement.Types.Status.Invalid) {
+ output.WriteRawTag(16);
+ output.WriteEnum((int) Status);
+ }
+ if (Current != 0) {
+ output.WriteRawTag(24);
+ output.WriteUInt32(Current);
+ }
+ if (Require != 0) {
+ output.WriteRawTag(32);
+ output.WriteUInt32(Require);
+ }
+ if (Timestamp != 0) {
+ output.WriteRawTag(40);
+ output.WriteUInt32(Timestamp);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ if (ID != 0) {
+ output.WriteRawTag(8);
+ output.WriteUInt32(ID);
+ }
+ if (Status != global::Achievement.Types.Status.Invalid) {
+ output.WriteRawTag(16);
+ output.WriteEnum((int) Status);
+ }
+ if (Current != 0) {
+ output.WriteRawTag(24);
+ output.WriteUInt32(Current);
+ }
+ if (Require != 0) {
+ output.WriteRawTag(32);
+ output.WriteUInt32(Require);
+ }
+ if (Timestamp != 0) {
+ output.WriteRawTag(40);
+ output.WriteUInt32(Timestamp);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ if (ID != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ID);
+ }
+ if (Status != global::Achievement.Types.Status.Invalid) {
+ size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status);
+ }
+ if (Current != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Current);
+ }
+ if (Require != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Require);
+ }
+ if (Timestamp != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Timestamp);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(Achievement other) {
+ if (other == null) {
+ return;
+ }
+ if (other.ID != 0) {
+ ID = other.ID;
+ }
+ if (other.Status != global::Achievement.Types.Status.Invalid) {
+ Status = other.Status;
+ }
+ if (other.Current != 0) {
+ Current = other.Current;
+ }
+ if (other.Require != 0) {
+ Require = other.Require;
+ }
+ if (other.Timestamp != 0) {
+ Timestamp = other.Timestamp;
+ }
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 8: {
+ ID = input.ReadUInt32();
+ break;
+ }
+ case 16: {
+ Status = (global::Achievement.Types.Status) input.ReadEnum();
+ break;
+ }
+ case 24: {
+ Current = input.ReadUInt32();
+ break;
+ }
+ case 32: {
+ Require = input.ReadUInt32();
+ break;
+ }
+ case 40: {
+ Timestamp = input.ReadUInt32();
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 8: {
+ ID = input.ReadUInt32();
+ break;
+ }
+ case 16: {
+ Status = (global::Achievement.Types.Status) input.ReadEnum();
+ break;
+ }
+ case 24: {
+ Current = input.ReadUInt32();
+ break;
+ }
+ case 32: {
+ Require = input.ReadUInt32();
+ break;
+ }
+ case 40: {
+ Timestamp = input.ReadUInt32();
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
+ #region Nested types
+ /// Container for nested types declared in the Achievement message type.
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static partial class Types {
+ public enum Status {
+ [pbr::OriginalName("INVALID")] Invalid = 0,
+ [pbr::OriginalName("UNFINISHED")] Unfinished = 1,
+ [pbr::OriginalName("FINISHED")] Finished = 2,
+ [pbr::OriginalName("REWARD_TAKEN")] RewardTaken = 3,
+ }
+
+ }
+ #endregion
+
+}
+
+public sealed partial class AchievementAllDataNotify : pb::IMessage
+#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ , pb::IBufferMessage
+#endif
+{
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new AchievementAllDataNotify());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::AchievementAllDataNotifyReflection.Descriptor.MessageTypes[1]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public AchievementAllDataNotify() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public AchievementAllDataNotify(AchievementAllDataNotify other) : this() {
+ list_ = other.list_.Clone();
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public AchievementAllDataNotify Clone() {
+ return new AchievementAllDataNotify(this);
+ }
+
+ /// Field number for the "list" field.
+ public const int ListFieldNumber = 5;
+ private static readonly pb::FieldCodec _repeated_list_codec
+ = pb::FieldCodec.ForMessage(42, global::Achievement.Parser);
+ private readonly pbc::RepeatedField list_ = new pbc::RepeatedField();
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public pbc::RepeatedField List {
+ get { return list_; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override bool Equals(object other) {
+ return Equals(other as AchievementAllDataNotify);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public bool Equals(AchievementAllDataNotify other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if(!list_.Equals(other.list_)) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override int GetHashCode() {
+ int hash = 1;
+ hash ^= list_.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void WriteTo(pb::CodedOutputStream output) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ output.WriteRawMessage(this);
+ #else
+ list_.WriteTo(output, _repeated_list_codec);
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+ list_.WriteTo(ref output, _repeated_list_codec);
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(ref output);
+ }
+ }
+ #endif
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public int CalculateSize() {
+ int size = 0;
+ size += list_.CalculateSize(_repeated_list_codec);
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(AchievementAllDataNotify other) {
+ if (other == null) {
+ return;
+ }
+ list_.Add(other.list_);
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ public void MergeFrom(pb::CodedInputStream input) {
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ input.ReadRawMessage(this);
+ #else
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 42: {
+ list_.AddEntriesFrom(input, _repeated_list_codec);
+ break;
+ }
+ }
+ }
+ #endif
+ }
+
+ #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+ break;
+ case 42: {
+ list_.AddEntriesFrom(ref input, _repeated_list_codec);
+ break;
+ }
+ }
+ }
+ }
+ #endif
+}
+#endregion
+#endregion Designer generated code
diff --git a/src/Utils.cs b/src/Utils.cs
new file mode 100644
index 0000000..555279f
--- /dev/null
+++ b/src/Utils.cs
@@ -0,0 +1,55 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO.Pipes;
+using YaeAchievement.Win32;
+
+namespace YaeAchievement;
+
+public static class Utils {
+
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Thread StartAndWaitResult(string exePath, Func onReceive) {
+ if (!Injector.CreateProcess(exePath, out var hProcess, out var hThread, out var pid)) {
+ Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("ICreateProcess fail"));
+ }
+ if (Injector.LoadLibraryAndInject(hProcess, GlobalVars.LibName) != 0) {
+ if (!Native.TerminateProcess(hProcess, 0)) {
+ Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail"));
+ }
+ }
+ if (Native.ResumeThread(hThread) == 0xFFFFFFFF) {
+ var e = new Win32Exception();
+ if (!Native.TerminateProcess(hProcess, 0)) {
+ new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail");
+ }
+ Environment.Exit(e.PrintMsgAndReturnErrCode("ResumeThread fail"));
+ }
+ if (!Native.CloseHandle(hProcess)) {
+ Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("CloseHandle fail"));
+ }
+ var proc = Process.GetProcessById(Convert.ToInt32(pid));
+ proc.EnableRaisingEvents = true;
+ proc.Exited += (_, _) => {
+ if (GlobalVars.UnexpectedExit) {
+ Console.WriteLine($"Game process exit at {proc.ExitTime:HH:mm:ss}");
+ Environment.Exit(114514);
+ }
+ };
+ var ts = new ThreadStart(() => {
+ var server = new NamedPipeServerStream(GlobalVars.PipeName);
+ server.WaitForConnection();
+ using var reader = new StreamReader(server);
+ while (!proc.HasExited) {
+ var line = reader.ReadLine();
+ if (line?.Length > 0) {
+ if (onReceive(line)) break;
+ server.Disconnect();
+ server.WaitForConnection();
+ }
+ }
+ });
+ var th = new Thread(ts);
+ th.Start();
+ return th;
+ }
+}
\ No newline at end of file