mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-13 01:48:13 +08:00
refactor native lib
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
<Solution>
|
<Solution>
|
||||||
|
<Project Path="YaeAchievementLib\YaeAchievementLib.csproj" Type="Classic C#" />
|
||||||
<Project Path="YaeAchievement\YaeAchievement.csproj" Type="Classic C#" />
|
<Project Path="YaeAchievement\YaeAchievement.csproj" Type="Classic C#" />
|
||||||
</Solution>
|
</Solution>
|
||||||
2
YaeAchievement/res/App.Designer.cs
generated
2
YaeAchievement/res/App.Designer.cs
generated
@@ -222,7 +222,7 @@ namespace YaeAchievement.res {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Fail, please contact developer to get help information.
|
/// Looks up a localized string similar to Fail, please contact developer to get help information (CG_{0}).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ExportToCocogoatFail {
|
internal static string ExportToCocogoatFail {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ExportToCocogoatFail" xml:space="preserve">
|
<data name="ExportToCocogoatFail" xml:space="preserve">
|
||||||
<value>Fail, please contact developer to get help information</value>
|
<value>Fail, please contact developer to get help information (CG_{0})</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllAchievement" xml:space="preserve">
|
<data name="AllAchievement" xml:space="preserve">
|
||||||
<value>all achievement</value>
|
<value>all achievement</value>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ExportToCocogoatFail" xml:space="preserve">
|
<data name="ExportToCocogoatFail" xml:space="preserve">
|
||||||
<value>导出失败, 请联系开发者以获取帮助</value>
|
<value>导出失败, 请联系开发者以获取帮助(CG_{0})</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllAchievement" xml:space="preserve">
|
<data name="AllAchievement" xml:space="preserve">
|
||||||
<value>全部成就</value>
|
<value>全部成就</value>
|
||||||
|
|||||||
@@ -17,9 +17,22 @@ message AchievementItem {
|
|||||||
string description = 4;
|
string description = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MethodRvaConfig {
|
||||||
|
uint32 do_cmd = 1;
|
||||||
|
uint32 to_uint16 = 2;
|
||||||
|
uint32 update_normal_prop = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NativeLibConfig {
|
||||||
|
uint32 store_cmd_id = 1;
|
||||||
|
uint32 achievement_cmd_id = 2;
|
||||||
|
map<uint32, MethodRvaConfig> method_rva = 10;
|
||||||
|
}
|
||||||
|
|
||||||
message AchievementInfo {
|
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;
|
AchievementProtoFieldInfo pb_info = 4;
|
||||||
|
NativeLibConfig native_config = 5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,4 @@ message UpdateInfo {
|
|||||||
bool force_update = 5;
|
bool force_update = 5;
|
||||||
bool enable_lib_download = 6;
|
bool enable_lib_download = 6;
|
||||||
bool enable_auto_update = 7;
|
bool enable_auto_update = 7;
|
||||||
string current_cn_hash = 8;
|
|
||||||
string current_os_hash = 9;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public static class Export {
|
|||||||
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
|
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
|
||||||
using var response = Utils.CHttpClient.Send(request);
|
using var response = Utils.CHttpClient.Send(request);
|
||||||
if (response.StatusCode != HttpStatusCode.Created) {
|
if (response.StatusCode != HttpStatusCode.Created) {
|
||||||
AnsiConsole.WriteLine(App.ExportToCocogoatFail);
|
AnsiConsole.WriteLine(App.ExportToCocogoatFail, response.StatusCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ internal static class Program {
|
|||||||
}
|
}
|
||||||
} catch (Exception) { /* ignored */ }
|
} catch (Exception) { /* ignored */ }
|
||||||
|
|
||||||
if (CacheFile.GetLastWriteTime("achievement_data").AddMinutes(60) > DateTime.UtcNow && data != null) {
|
if (data != null && CacheFile.GetLastWriteTime("achievement_data").AddMinutes(60) > DateTime.UtcNow) {
|
||||||
var prompt = new SelectionPromptCompat<string>()
|
var prompt = new SelectionPromptCompat<string>()
|
||||||
.Title(App.UsePreviousData)
|
.Title(App.UsePreviousData)
|
||||||
.AddChoices(App.CommonYes, App.CommonNo);
|
.AddChoices(App.CommonYes, App.CommonNo);
|
||||||
@@ -72,7 +72,7 @@ internal static class Program {
|
|||||||
StartAndWaitResult(AppConfig.GamePath, new Dictionary<int, Func<BinaryReader, bool>> {
|
StartAndWaitResult(AppConfig.GamePath, new Dictionary<int, Func<BinaryReader, bool>> {
|
||||||
{ 1, AchievementAllDataNotify.OnReceive },
|
{ 1, AchievementAllDataNotify.OnReceive },
|
||||||
{ 2, PlayerStoreNotify.OnReceive },
|
{ 2, PlayerStoreNotify.OnReceive },
|
||||||
{ 100, PlayerPropNotify.OnReceive },
|
{ 3, PlayerPropNotify.OnReceive },
|
||||||
}, () => {
|
}, () => {
|
||||||
#if DEBUG_EX
|
#if DEBUG_EX
|
||||||
PlayerPropNotify.OnFinish();
|
PlayerPropNotify.OnFinish();
|
||||||
|
|||||||
26
YaeAchievement/src/Utilities/Crc32.cs
Normal file
26
YaeAchievement/src/Utilities/Crc32.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace YaeAchievement.Utilities;
|
||||||
|
|
||||||
|
// CRC-32-IEEE 802.3
|
||||||
|
public static class Crc32 {
|
||||||
|
|
||||||
|
private const uint Polynomial = 0xEDB88320;
|
||||||
|
private static readonly uint[] Crc32Table = new uint[256];
|
||||||
|
|
||||||
|
static Crc32() {
|
||||||
|
for (uint i = 0; i < Crc32Table.Length; i++) {
|
||||||
|
var v = i;
|
||||||
|
for (var j = 0; j < 8; j++) {
|
||||||
|
v = (v >> 1) ^ ((v & 1) * Polynomial);
|
||||||
|
}
|
||||||
|
Crc32Table[i] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint Compute(Span<byte> buf) {
|
||||||
|
var checksum = 0xFFFFFFFF;
|
||||||
|
foreach (var b in buf) {
|
||||||
|
checksum = (checksum >> 8) ^ Crc32Table[(b ^ checksum) & 0xFF];
|
||||||
|
}
|
||||||
|
return ~checksum;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -203,30 +203,40 @@ public static class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool _isUnexpectedExit = true;
|
private static bool _isUnexpectedExit = true;
|
||||||
|
|
||||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||||
public static void StartAndWaitResult(string exePath, Dictionary<int, Func<BinaryReader, bool>> handlers, Action onFinish) {
|
public static void StartAndWaitResult(string exePath, Dictionary<int, Func<BinaryReader, bool>> handlers, Action onFinish) {
|
||||||
_proc = new GameProcess(exePath);
|
var hash = GetGameHash(exePath);
|
||||||
_proc.LoadLibrary(GlobalVars.LibFilePath);
|
var nativeConf = GlobalVars.AchievementInfo.NativeConfig;
|
||||||
_proc.ResumeMainThread();
|
if (!nativeConf.MethodRva.TryGetValue(hash, out var methodRva)) {
|
||||||
_proc.OnExit += () => {
|
AnsiConsole.WriteLine($"No match config {exePath} {hash:X8}");
|
||||||
if (_isUnexpectedExit) {
|
Environment.Exit(0);
|
||||||
_proc = null;
|
return;
|
||||||
AnsiConsole.WriteLine(App.GameProcessExit);
|
}
|
||||||
Environment.Exit(114514);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AnsiConsole.WriteLine(App.GameLoading, _proc.Id);
|
|
||||||
Task.Run(() => {
|
Task.Run(() => {
|
||||||
using var stream = new NamedPipeServerStream(GlobalVars.PipeName);
|
using var stream = new NamedPipeServerStream(GlobalVars.PipeName);
|
||||||
using var reader = new BinaryReader(stream);
|
using var reader = new BinaryReader(stream, System.Text.Encoding.UTF8, true);
|
||||||
|
using var writer = new BinaryWriter(stream, System.Text.Encoding.UTF8, true);
|
||||||
stream.WaitForConnection();
|
stream.WaitForConnection();
|
||||||
int type;
|
int type;
|
||||||
while ((type = stream.ReadByte()) != -1) {
|
while ((type = stream.ReadByte()) != -1) {
|
||||||
if (type == 0xFF) {
|
switch (type) {
|
||||||
_isUnexpectedExit = false;
|
case 0xFC:
|
||||||
onFinish();
|
writer.Write(nativeConf.AchievementCmdId);
|
||||||
break;
|
writer.Write(nativeConf.StoreCmdId);
|
||||||
|
break;
|
||||||
|
case 0xFD:
|
||||||
|
writer.Write(methodRva.DoCmd);
|
||||||
|
writer.Write(methodRva.ToUint16);
|
||||||
|
writer.Write(methodRva.UpdateNormalProp);
|
||||||
|
break;
|
||||||
|
case 0xFE:
|
||||||
|
_proc!.ResumeMainThread();
|
||||||
|
break;
|
||||||
|
case 0xFF:
|
||||||
|
_isUnexpectedExit = false;
|
||||||
|
onFinish();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (handlers.TryGetValue(type, out var handler)) {
|
if (handlers.TryGetValue(type, out var handler)) {
|
||||||
if (handler(reader)) {
|
if (handler(reader)) {
|
||||||
@@ -235,6 +245,23 @@ public static class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).ContinueWith(task => { if (task.IsFaulted) OnUnhandledException(task.Exception!); });
|
}).ContinueWith(task => { if (task.IsFaulted) OnUnhandledException(task.Exception!); });
|
||||||
|
_proc = new GameProcess(exePath);
|
||||||
|
_proc.LoadLibrary(GlobalVars.LibFilePath);
|
||||||
|
_proc.OnExit += () => {
|
||||||
|
if (_isUnexpectedExit) {
|
||||||
|
_proc = null;
|
||||||
|
AnsiConsole.WriteLine(App.GameProcessExit);
|
||||||
|
Environment.Exit(114514);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AnsiConsole.WriteLine(App.GameLoading, _proc.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint GetGameHash(string exePath) {
|
||||||
|
Span<byte> buffer = stackalloc byte[0x10000];
|
||||||
|
using var stream = File.OpenRead(exePath);
|
||||||
|
_ = stream.Read(buffer);
|
||||||
|
return Crc32.Compute(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static unsafe void SetQuickEditMode(bool enable) {
|
internal static unsafe void SetQuickEditMode(bool enable) {
|
||||||
|
|||||||
50
YaeAchievementLib/YaeAchievementLib.csproj
Normal file
50
YaeAchievementLib/YaeAchievementLib.csproj
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>preview</LangVersion>
|
||||||
|
<RootNamespace>Yae</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<TargetFramework>net9.0-windows</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<OptimizationPreference>Speed</OptimizationPreference>
|
||||||
|
<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Elysia.Bootstrap" Version="1.0.14"/>
|
||||||
|
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.183">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<DirectPInvoke Include="NTDLL"/>
|
||||||
|
<DirectPInvoke Include="USER32"/>
|
||||||
|
<DirectPInvoke Include="KERNEL32"/>
|
||||||
|
<DirectPInvoke Include="libMinHook.x64"/>
|
||||||
|
<NativeLibrary Include="lib\libMinHook.x64.lib"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<LibraryEntrypoint Include="YaeMain"/>
|
||||||
|
<AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="System.Diagnostics"/>
|
||||||
|
<Using Include="System.Diagnostics.CodeAnalysis"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="obj\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
BIN
YaeAchievementLib/lib/libMinHook.x64.lib
Normal file
BIN
YaeAchievementLib/lib/libMinHook.x64.lib
Normal file
Binary file not shown.
134
YaeAchievementLib/src/Application.cs
Normal file
134
YaeAchievementLib/src/Application.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Yae.Utilities;
|
||||||
|
|
||||||
|
namespace Yae;
|
||||||
|
|
||||||
|
internal static unsafe class Application {
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "YaeMain")]
|
||||||
|
private static uint Awake(nint hModule) {
|
||||||
|
Native.RegisterUnhandledExceptionHandler();
|
||||||
|
Log.UseConsoleOutput();
|
||||||
|
Log.Trace("~");
|
||||||
|
Goshujin.Init();
|
||||||
|
Goshujin.LoadCmdTable();
|
||||||
|
Goshujin.LoadMethodTable();
|
||||||
|
Goshujin.ResumeMainThread();
|
||||||
|
//
|
||||||
|
Native.WaitMainWindow();
|
||||||
|
Log.ResetConsole();
|
||||||
|
//
|
||||||
|
RecordChecksum();
|
||||||
|
MinHook.Attach(GameMethod.DoCmd, &OnDoCmd, out _doCmd);
|
||||||
|
MinHook.Attach(GameMethod.ToUInt16, &OnToUInt16, out _toUInt16);
|
||||||
|
MinHook.Attach(GameMethod.UpdateNormalProp, &OnUpdateNormalProp, out _updateNormalProp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region RecvPacket
|
||||||
|
|
||||||
|
private static delegate*unmanaged<byte*, int, ushort> _toUInt16;
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
private static ushort OnToUInt16(byte* val, int startIndex) {
|
||||||
|
var ret = _toUInt16(val, startIndex);
|
||||||
|
if (ret != 0xAB89 || *(ushort*) (val += 0x20) != 0x6745) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
var cmdId = BinaryPrimitives.ReverseEndianness(*(ushort*) (val + 2));
|
||||||
|
if (cmdId == CmdId.PlayerStoreNotify) {
|
||||||
|
Goshujin.PushStoreData(GetData(val));
|
||||||
|
} else if (cmdId == CmdId.AchievementAllDataNotify) {
|
||||||
|
Goshujin.PushAchievementData(GetData(val));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
static Span<byte> GetData(byte* val) {
|
||||||
|
var headLen = BinaryPrimitives.ReverseEndianness(*(ushort*) (val + 4));
|
||||||
|
var dataLen = BinaryPrimitives.ReverseEndianness(*(uint*) (val + 6));
|
||||||
|
return new Span<byte>(val + 10 + headLen, (int) dataLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Prop
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PROP_PLAYER_HCOIN = 10015,
|
||||||
|
* PROP_PLAYER_WAIT_SUB_HCOIN = 10022,
|
||||||
|
* PROP_PLAYER_SCOIN = 10016,
|
||||||
|
* PROP_PLAYER_WAIT_SUB_SCOIN = 10023,
|
||||||
|
* PROP_PLAYER_MCOIN = 10025,
|
||||||
|
* PROP_PLAYER_WAIT_SUB_MCOIN = 10026,
|
||||||
|
* PROP_PLAYER_HOME_COIN = 10042,
|
||||||
|
* PROP_PLAYER_WAIT_SUB_HOME_COIN = 10043,
|
||||||
|
* PROP_PLAYER_ROLE_COMBAT_COIN = 10053,
|
||||||
|
* PROP_PLAYER_MUSIC_GAME_BOOK_COIN = 10058,
|
||||||
|
*/
|
||||||
|
public static HashSet<int> RequiredPlayerProperties { get; } = [
|
||||||
|
10015, 10022, 10016, 10023, 10025, 10026, 10042, 10043, 10053, 10058
|
||||||
|
];
|
||||||
|
|
||||||
|
private static delegate*unmanaged<nint, int, double, double, int, void> _updateNormalProp;
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
private static void OnUpdateNormalProp(nint @this, int type, double value, double lastValue, int state) {
|
||||||
|
_updateNormalProp(@this, type, value, lastValue, state);
|
||||||
|
if (RequiredPlayerProperties.Remove(type)) {
|
||||||
|
Goshujin.PushPlayerProp(type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Checksum
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct RecordChecksumCmdData {
|
||||||
|
|
||||||
|
public int Type;
|
||||||
|
|
||||||
|
public void* Buffer;
|
||||||
|
|
||||||
|
public int Length;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RecordChecksumCmdData[] RecordedChecksum = new RecordChecksumCmdData[3];
|
||||||
|
|
||||||
|
private static void RecordChecksum() {
|
||||||
|
for (var i = 0; i < 3; i++) {
|
||||||
|
var buffer = NativeMemory.AllocZeroed(256);
|
||||||
|
var data = new RecordChecksumCmdData {
|
||||||
|
Type = i,
|
||||||
|
Buffer = buffer,
|
||||||
|
Length = 256
|
||||||
|
};
|
||||||
|
_ = GameMethod.DoCmd(23, Unsafe.AsPointer(ref data), sizeof(RecordChecksumCmdData));
|
||||||
|
RecordedChecksum[i] = data;
|
||||||
|
//REPL//Log.Trace($"nType={i}, value={new string((sbyte*) buffer, 0, data.Length)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static delegate*unmanaged<int, void*, int, int> _doCmd;
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
public static int OnDoCmd(int cmdType, void* data, int size) {
|
||||||
|
var result = _doCmd(cmdType, data, size);
|
||||||
|
if (cmdType == 23) {
|
||||||
|
var cmdData = (RecordChecksumCmdData*) data;
|
||||||
|
if (cmdData->Type < 3) {
|
||||||
|
var recordedData = RecordedChecksum[cmdData->Type];
|
||||||
|
cmdData->Length = recordedData.Length;
|
||||||
|
Buffer.MemoryCopy(recordedData.Buffer, cmdData->Buffer, recordedData.Length, recordedData.Length);
|
||||||
|
//REPL//Log.Trace($"Override type {cmdData->Type} result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
88
YaeAchievementLib/src/Goshujin.cs
Normal file
88
YaeAchievementLib/src/Goshujin.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System.IO.Pipes;
|
||||||
|
using Yae.Utilities;
|
||||||
|
|
||||||
|
namespace Yae;
|
||||||
|
|
||||||
|
internal static class CmdId {
|
||||||
|
|
||||||
|
public static uint AchievementAllDataNotify { get; set; }
|
||||||
|
|
||||||
|
public static uint PlayerStoreNotify { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static unsafe class GameMethod {
|
||||||
|
|
||||||
|
public static delegate*unmanaged<int, void*, int, int> DoCmd { get; set; }
|
||||||
|
|
||||||
|
public static delegate*unmanaged<byte*, int, ushort> ToUInt16 { get; set; }
|
||||||
|
|
||||||
|
public static delegate*unmanaged<nint, int, double, double, int, void> UpdateNormalProp { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Goshujin {
|
||||||
|
|
||||||
|
private static NamedPipeClientStream _pipeStream = null!;
|
||||||
|
private static BinaryReader _pipeReader = null!;
|
||||||
|
private static BinaryWriter _pipeWriter = null!;
|
||||||
|
|
||||||
|
public static void Init(string pipeName = "YaeAchievementPipe") {
|
||||||
|
_pipeStream = new NamedPipeClientStream(pipeName);
|
||||||
|
_pipeReader = new BinaryReader(_pipeStream);
|
||||||
|
_pipeWriter = new BinaryWriter(_pipeStream);
|
||||||
|
_pipeStream.Connect();
|
||||||
|
Log.Trace("Pipe server connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PushAchievementData(Span<byte> data) {
|
||||||
|
_pipeWriter.Write((byte) 1);
|
||||||
|
_pipeWriter.Write(data.Length);
|
||||||
|
_pipeWriter.Write(data);
|
||||||
|
_achievementDataPushed = true;
|
||||||
|
ExitIfFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PushStoreData(Span<byte> data) {
|
||||||
|
_pipeWriter.Write((byte) 2);
|
||||||
|
_pipeWriter.Write(data.Length);
|
||||||
|
_pipeWriter.Write(data);
|
||||||
|
_storeDataPushed = true;
|
||||||
|
ExitIfFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PushPlayerProp(int type, double value) {
|
||||||
|
_pipeWriter.Write((byte) 3);
|
||||||
|
_pipeWriter.Write(type);
|
||||||
|
_pipeWriter.Write(value);
|
||||||
|
ExitIfFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadCmdTable() {
|
||||||
|
_pipeWriter.Write((byte) 0xFC);
|
||||||
|
CmdId.AchievementAllDataNotify = _pipeReader.ReadUInt32();
|
||||||
|
CmdId.PlayerStoreNotify = _pipeReader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe void LoadMethodTable() {
|
||||||
|
_pipeWriter.Write((byte) 0xFD);
|
||||||
|
GameMethod.DoCmd = (delegate*unmanaged<int, void*, int, int>) Native.RVAToVA(_pipeReader.ReadUInt32());
|
||||||
|
GameMethod.ToUInt16 = (delegate*unmanaged<byte*, int, ushort>) Native.RVAToVA(_pipeReader.ReadUInt32());
|
||||||
|
GameMethod.UpdateNormalProp = (delegate*unmanaged<nint, int, double, double, int, void>) Native.RVAToVA(_pipeReader.ReadUInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ResumeMainThread() {
|
||||||
|
_pipeWriter.Write((byte) 0xFE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool _storeDataPushed;
|
||||||
|
|
||||||
|
private static bool _achievementDataPushed;
|
||||||
|
|
||||||
|
private static void ExitIfFinished() {
|
||||||
|
if (_storeDataPushed && _achievementDataPushed && Application.RequiredPlayerProperties.Count == 0) {
|
||||||
|
_pipeWriter.Write((byte) 0xFF);
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
YaeAchievementLib/src/Utilities/Log.cs
Normal file
111
YaeAchievementLib/src/Utilities/Log.cs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
|
||||||
|
namespace Yae.Utilities;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
internal enum LogLevel : byte {
|
||||||
|
Trace = 0x00,
|
||||||
|
Debug = 0x01,
|
||||||
|
Info = 0x02,
|
||||||
|
Warn = 0x03,
|
||||||
|
Error = 0x04,
|
||||||
|
Fatal = 0x05,
|
||||||
|
Time = 0x06,
|
||||||
|
LevelMask = 0x0F,
|
||||||
|
FileOnly = 0x10,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Log {
|
||||||
|
|
||||||
|
#region ConsoleWriter
|
||||||
|
|
||||||
|
private static TextWriter? _consoleWriter;
|
||||||
|
|
||||||
|
[Conditional("EnableLogging")]
|
||||||
|
public static void UseConsoleOutput() {
|
||||||
|
InitializeConsole();
|
||||||
|
_consoleWriter = Console.Out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("EnableLogging")]
|
||||||
|
public static void ResetConsole() {
|
||||||
|
Kernel32.FreeConsole();
|
||||||
|
InitializeConsole();
|
||||||
|
var sw = new StreamWriter(Console.OpenStandardOutput(), _consoleWriter!.Encoding, 256, true) {
|
||||||
|
AutoFlush = true
|
||||||
|
};
|
||||||
|
_consoleWriter = TextWriter.Synchronized(sw);
|
||||||
|
Console.SetOut(_consoleWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void InitializeConsole() {
|
||||||
|
Kernel32.AllocConsole();
|
||||||
|
uint mode;
|
||||||
|
var cHandle = Kernel32.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE);
|
||||||
|
if (!Kernel32.GetConsoleMode(cHandle, &mode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Kernel32.SetConsoleMode(cHandle, mode | Kernel32.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||||
|
Console.OutputEncoding = Console.InputEncoding = System.Text.Encoding.UTF8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[DoesNotReturn]
|
||||||
|
public static void ErrorAndExit(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Fatal);
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Error(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Warn(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Info(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Debug(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Trace(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Time(string value, [CallerMemberName] string callerName = "") {
|
||||||
|
WriteLog(value, callerName, LogLevel.Time);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("EnableLogging")]
|
||||||
|
public static void WriteLog(string message, string tag, LogLevel level) {
|
||||||
|
var time = DateTimeOffset.Now.ToString("HH:mm:ss.fff");
|
||||||
|
if (_consoleWriter != null) {
|
||||||
|
var color = level switch {
|
||||||
|
LogLevel.Error or LogLevel.Fatal => "244;67;54",
|
||||||
|
LogLevel.Warn => "255;235;59",
|
||||||
|
LogLevel.Info => "153;255;153",
|
||||||
|
LogLevel.Debug => "91;206;250",
|
||||||
|
LogLevel.Trace => "246;168;184",
|
||||||
|
LogLevel.Time => "19;161;14",
|
||||||
|
_ => throw new ArgumentException($"Invalid log level: {level}")
|
||||||
|
};
|
||||||
|
_consoleWriter.Write($"[{time}][\e[38;2;{color}m{level,5}\e[0m] {tag} : ");
|
||||||
|
_consoleWriter.WriteLine(message);
|
||||||
|
}
|
||||||
|
if (level == LogLevel.Fatal) {
|
||||||
|
if (_consoleWriter != null) {
|
||||||
|
WriteLog("Error occurred, press enter key to exit", tag, LogLevel.Error);
|
||||||
|
Console.ReadLine();
|
||||||
|
} else {
|
||||||
|
User32.MessageBoxW(0, "An critical error occurred.", "Error", 0x10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
201
YaeAchievementLib/src/Utilities/Native.cs
Normal file
201
YaeAchievementLib/src/Utilities/Native.cs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
|
||||||
|
namespace Yae.Utilities;
|
||||||
|
|
||||||
|
internal static unsafe class Native {
|
||||||
|
|
||||||
|
#region WaitMainWindow
|
||||||
|
|
||||||
|
private static nint _hwnd;
|
||||||
|
private static readonly uint ProcessId = Kernel32.GetCurrentProcessId();
|
||||||
|
|
||||||
|
public static void WaitMainWindow() {
|
||||||
|
_hwnd = 0;
|
||||||
|
do {
|
||||||
|
Thread.Sleep(100);
|
||||||
|
_ = User32.EnumWindows(&EnumWindowsCallback, 0);
|
||||||
|
} while (_hwnd == 0);
|
||||||
|
return;
|
||||||
|
[UnmanagedCallersOnly(CallConvs = [ typeof(CallConvStdcall) ])]
|
||||||
|
static int EnumWindowsCallback(nint handle, nint extraParameter) {
|
||||||
|
uint wProcessId = 0; // Avoid uninitialized variable if the window got closed in the meantime
|
||||||
|
_ = User32.GetWindowThreadProcessId(handle, &wProcessId);
|
||||||
|
var cName = (char*) NativeMemory.Alloc(256);
|
||||||
|
if (User32.GetClassNameW(handle, cName, 256) != 0) {
|
||||||
|
if (wProcessId == ProcessId && User32.IsWindowVisible(handle) && new string(cName) == "UnityWndClass") {
|
||||||
|
_hwnd = handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeMemory.Free(cName);
|
||||||
|
return _hwnd == 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RestoreVirtualProtect
|
||||||
|
|
||||||
|
public static bool RestoreVirtualProtect() {
|
||||||
|
// NtProtectVirtualMemoryImpl
|
||||||
|
// _ = stackalloc byte[] { 0x4C, 0x8B, 0xD1, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x05, 0xC3 };
|
||||||
|
if (!NativeLibrary.TryLoad("ntdll.dll", out var hPtr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!NativeLibrary.TryGetExport(hPtr, "NtProtectVirtualMemory", out var mPtr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 4C 8B D1 mov r10, rcx
|
||||||
|
// B8 mov eax, $imm32
|
||||||
|
if (*(uint*) (mPtr - 0x20) != 0xB8D18B4C) { // previous
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var syscallNumber = (ulong) *(uint*) (mPtr - 0x1C) + 1;
|
||||||
|
var old = 0u;
|
||||||
|
if (!Kernel32.VirtualProtect(mPtr, 1, Kernel32.PAGE_EXECUTE_READWRITE, &old)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*(ulong*) mPtr = 0xB8D18B4C | syscallNumber << 32;
|
||||||
|
return Kernel32.VirtualProtect(mPtr, 1, old, &old);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GetModuleHandle
|
||||||
|
|
||||||
|
public static string GetModulePath(nint hModule) {
|
||||||
|
var buffer = stackalloc char[256];
|
||||||
|
_ = Kernel32.GetModuleFileNameW(hModule, buffer, 256);
|
||||||
|
return new string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static nint GetModuleHandle(string? moduleName = null) {
|
||||||
|
fixed (char* pName = moduleName ?? Path.GetFileName(GetModulePath(0))) {
|
||||||
|
return Kernel32.GetModuleHandleW(pName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static readonly nint ModuleBase = GetModuleHandle();
|
||||||
|
|
||||||
|
public static nint RVAToVA(uint addr) => ModuleBase + (nint) addr;
|
||||||
|
|
||||||
|
public static void RegisterUnhandledExceptionHandler() {
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||||
|
return;
|
||||||
|
static void OnUnhandledException(object? sender, UnhandledExceptionEventArgs e) {
|
||||||
|
var ex = e.ExceptionObject as Exception;
|
||||||
|
User32.MessageBoxW(0, ex?.ToString() ?? "null", "Unhandled Exception", 0x10);
|
||||||
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static partial class MinHook {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the MinHook library. You must call this function EXACTLY ONCE at the beginning of your program.
|
||||||
|
/// </summary>
|
||||||
|
[LibraryImport("libMinHook.x64", EntryPoint = "MH_Initialize")]
|
||||||
|
private static partial uint MinHookInitialize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a hook for the specified target function, in disabled state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pTarget">A pointer to the target function, which will be overridden by the detour function.</param>
|
||||||
|
/// <param name="pDetour">A pointer to the detour function, which will override the target function.</param>
|
||||||
|
/// <param name="ppOriginal">
|
||||||
|
/// A pointer to the trampoline function, which will be used to call the original target function.
|
||||||
|
/// This parameter can be NULL.
|
||||||
|
/// </param>
|
||||||
|
[LibraryImport("libMinHook.x64", EntryPoint = "MH_CreateHook")]
|
||||||
|
private static partial uint MinHookCreate(nint pTarget, nint pDetour, out nint ppOriginal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables an already created hook.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pTarget">
|
||||||
|
/// A pointer to the target function.
|
||||||
|
/// If this parameter is MH_ALL_HOOKS, all created hooks are enabled in one go.
|
||||||
|
/// </param>
|
||||||
|
[LibraryImport("libMinHook.x64", EntryPoint = "MH_EnableHook")]
|
||||||
|
private static partial uint MinHookEnable(nint pTarget);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables an already created hook.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pTarget">
|
||||||
|
/// A pointer to the target function.
|
||||||
|
/// If this parameter is MH_ALL_HOOKS, all created hooks are enabled in one go.
|
||||||
|
/// </param>
|
||||||
|
[LibraryImport("libMinHook.x64", EntryPoint = "MH_DisableHook")]
|
||||||
|
private static partial uint MinHookDisable(nint pTarget);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an already created hook.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pTarget">A pointer to the target function.</param>
|
||||||
|
[LibraryImport("libMinHook.x64", EntryPoint = "MH_RemoveHook")]
|
||||||
|
private static partial uint MinHookRemove(nint pTarget);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uninitialize the MinHook library. You must call this function EXACTLY ONCE at the end of your program.
|
||||||
|
/// </summary>
|
||||||
|
[LibraryImport("libMinHook.x64", EntryPoint = "MH_Uninitialize")]
|
||||||
|
private static partial uint MinHookUninitialize();
|
||||||
|
|
||||||
|
static MinHook() {
|
||||||
|
var result = MinHookInitialize();
|
||||||
|
if (result != 0) {
|
||||||
|
throw new InvalidOperationException($"Failed to initialize MinHook: {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: auto gen
|
||||||
|
public static unsafe void Attach(delegate*unmanaged<byte*, int, ushort> origin, delegate*unmanaged<byte*, int, ushort> handler, out delegate*unmanaged<byte*, int, ushort> trampoline) {
|
||||||
|
Attach((nint) origin, (nint) handler, out var trampoline1);
|
||||||
|
trampoline = (delegate*unmanaged<byte*, int, ushort>) trampoline1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: auto gen
|
||||||
|
public static unsafe void Attach(delegate*unmanaged<nint, int, double, double, int, void> origin, delegate*unmanaged<nint, int, double, double, int, void> handler, out delegate*unmanaged<nint, int, double, double, int, void> trampoline) {
|
||||||
|
Attach((nint) origin, (nint) handler, out var trampoline1);
|
||||||
|
trampoline = (delegate*unmanaged<nint, int, double, double, int, void>) trampoline1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: auto gen
|
||||||
|
public static unsafe void Attach(delegate*unmanaged<nint, nint, uint, void> origin, delegate*unmanaged<nint, nint, uint, void> handler, out delegate*unmanaged<nint, nint, uint, void> trampoline) {
|
||||||
|
Attach((nint) origin, (nint) handler, out var trampoline1);
|
||||||
|
trampoline = (delegate*unmanaged<nint, nint, uint, void>) trampoline1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: auto gen
|
||||||
|
public static unsafe void Attach(delegate*unmanaged<int, void*, int, int> origin, delegate*unmanaged<int, void*, int, int> handler, out delegate*unmanaged<int, void*, int, int> trampoline) {
|
||||||
|
Attach((nint) origin, (nint) handler, out var trampoline1);
|
||||||
|
trampoline = (delegate*unmanaged<int, void*, int, int>) trampoline1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Attach(nint origin, nint handler, out nint trampoline) {
|
||||||
|
uint result;
|
||||||
|
if ((result = MinHookCreate(origin, handler, out trampoline)) != 0) {
|
||||||
|
throw new InvalidOperationException($"Failed to create hook: {result}");
|
||||||
|
}
|
||||||
|
if ((result = MinHookEnable(origin)) != 0) {
|
||||||
|
throw new InvalidOperationException($"Failed to enable hook: {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Detach(nint origin) {
|
||||||
|
uint result;
|
||||||
|
if ((result = MinHookDisable(origin)) != 0) {
|
||||||
|
throw new InvalidOperationException($"Failed to create hook: {result}");
|
||||||
|
}
|
||||||
|
if ((result = MinHookRemove(origin)) != 0) {
|
||||||
|
throw new InvalidOperationException($"Failed to enable hook: {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
YaeAchievementLib/src/Utilities/WinApi.cs
Normal file
68
YaeAchievementLib/src/Utilities/WinApi.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Yae.Utilities;
|
||||||
|
|
||||||
|
#pragma warning disable CS0649, CA1069 // ReSharper disable IdentifierTypo, InconsistentNaming, UnassignedField.Global
|
||||||
|
|
||||||
|
internal static unsafe partial class Kernel32 {
|
||||||
|
|
||||||
|
[LibraryImport("KERNEL32.dll")]
|
||||||
|
internal static partial uint GetCurrentProcessId();
|
||||||
|
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial nint GetModuleHandleW(char* lpModuleName);
|
||||||
|
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial uint GetModuleFileNameW(nint hModule, char* lpFilename, uint nSize);
|
||||||
|
|
||||||
|
internal const uint PAGE_EXECUTE_READWRITE = 0x00000040;
|
||||||
|
|
||||||
|
[return:MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial bool VirtualProtect(nint lpAddress, nuint dwSize, uint flNewProtect, uint* lpflOldProtect);
|
||||||
|
|
||||||
|
internal const uint STD_OUTPUT_HANDLE = 0xFFFFFFF5;
|
||||||
|
|
||||||
|
internal const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x00000004;
|
||||||
|
|
||||||
|
[return:MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial bool AllocConsole();
|
||||||
|
|
||||||
|
[return:MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial bool FreeConsole();
|
||||||
|
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial nint GetStdHandle(uint nStdHandle);
|
||||||
|
|
||||||
|
[return:MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial bool GetConsoleMode(nint hConsoleHandle, uint* lpMode);
|
||||||
|
|
||||||
|
[return:MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("KERNEL32.dll", SetLastError = true)]
|
||||||
|
internal static partial bool SetConsoleMode(nint hConsoleHandle, uint dwMode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static unsafe partial class User32 {
|
||||||
|
|
||||||
|
[LibraryImport("USER32.dll", SetLastError = true)]
|
||||||
|
internal static partial uint GetWindowThreadProcessId(nint hWnd, uint* lpdwProcessId);
|
||||||
|
|
||||||
|
[LibraryImport("USER32.dll", SetLastError = true)]
|
||||||
|
internal static partial int GetClassNameW(nint hWnd, char* lpClassName, int nMaxCount);
|
||||||
|
|
||||||
|
[return: MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("USER32.dll")]
|
||||||
|
internal static partial bool IsWindowVisible(nint hWnd);
|
||||||
|
|
||||||
|
[return: MarshalAs(UnmanagedType.I4)]
|
||||||
|
[LibraryImport("USER32.dll", SetLastError = true)]
|
||||||
|
internal static partial bool EnumWindows(delegate *unmanaged[Stdcall]<nint, nint, int> lpEnumFunc, nint lParam);
|
||||||
|
|
||||||
|
[LibraryImport("USER32.dll", StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
|
||||||
|
internal static partial int MessageBoxW(nint hWnd, string text, string caption, uint uType);
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user