16 Commits

Author SHA1 Message Date
HolographicHat
b14f5b54d7 Merge pull request #148 from Lightczx/master 2025-10-28 11:18:49 +08:00
DismissedLight
8b3dd1bb6c handle CreateToolhelp32Snapshot ERROR_BAD_LENGTH 2025-10-28 11:15:41 +08:00
HolographicHat
6a2115b4de typo 2025-10-23 09:18:04 +08:00
HolographicHat
ccff7a1702 [skip ci] bump lib version 2025-10-23 08:05:27 +08:00
HolographicHat
79122d6ba7 bump version 2025-10-23 08:02:06 +08:00
HolographicHat
1218d1cbc4 fix #146, #147 2025-10-23 08:01:43 +08:00
HolographicHat
19f84bdb86 bump version 2025-10-22 06:23:04 +08:00
HolographicHat
9d22fdf6f9 implement WhenFirstSuccessful method for improved task handling 2025-10-22 06:21:22 +08:00
HolographicHat
27fa0d9c84 move srcgen project 2025-10-22 01:46:58 +08:00
HolographicHat
e9baf8f211 [skip ci] update ci 2025-10-18 20:40:18 +08:00
HolographicHat
b960165b7e Merge pull request #144 from Lightczx/master 2025-10-14 16:17:21 +08:00
DismissedLight
a4c2027ada code style 2025-10-14 13:44:51 +08:00
DismissedLight
f49477c49a Generated MinHook.Attach methods 2025-10-14 11:30:58 +08:00
HolographicHat
67c2fb3bda bump lib version 2025-10-10 18:20:13 +08:00
HolographicHat
be3440695d auto enter gate (close #143) 2025-09-16 11:42:49 +08:00
HolographicHat
f8b8a5a9e1 #142 2025-08-30 11:39:41 +08:00
15 changed files with 365 additions and 83 deletions

View File

@@ -1,6 +1,7 @@
name: .NET Build
on:
workflow_dispatch:
push:
branches: [ "master" ]
pull_request:

View File

@@ -0,0 +1,104 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace YaeAchievement.SourceGeneration;
[Generator(LanguageNames.CSharp)]
public sealed class MinHookAttachGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<AttachInfo>> provider = context.SyntaxProvider.CreateSyntaxProvider(Filter, Transform).Collect();
context.RegisterSourceOutput(provider, Generate);
}
private static bool Filter(SyntaxNode node, CancellationToken token)
{
return node is InvocationExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.Text: "MinHook" },
Name.Identifier.Text: "Attach"
}
};
}
private static AttachInfo Transform(GeneratorSyntaxContext context, CancellationToken token)
{
InvocationExpressionSyntax invocation = (InvocationExpressionSyntax)context.Node;
SeparatedSyntaxList<ArgumentSyntax> args = invocation.ArgumentList.Arguments;
if (args.Count is not 3)
{
return null;
}
string type = context.SemanticModel.GetTypeInfo(args[0].Expression).Type?.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
if (string.IsNullOrEmpty(type))
{
return null;
}
return new()
{
MinimallyQualifiedType = type,
};
}
private static void Generate(SourceProductionContext context, ImmutableArray<AttachInfo> infoArray)
{
CompilationUnitSyntax unit = CompilationUnit()
.WithMembers(List<MemberDeclarationSyntax>(
[
FileScopedNamespaceDeclaration(ParseName("Yae.Utilities")),
ClassDeclaration("MinHook")
.WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.PartialKeyword)))
.WithMembers(List(GenerateMethods(infoArray)))
]));
context.AddSource("MinHook.Attach.g.cs", unit.NormalizeWhitespace().ToFullString());
}
private static IEnumerable<MemberDeclarationSyntax> GenerateMethods(ImmutableArray<AttachInfo> infoArray)
{
foreach (AttachInfo info in infoArray)
{
TypeSyntax type = ParseTypeName(info.MinimallyQualifiedType);
yield return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier("Attach"))
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.UnsafeKeyword)))
.WithParameterList(ParameterList(SeparatedList(
[
Parameter(Identifier("origin")).WithType(type),
Parameter(Identifier("handler")).WithType(type),
Parameter(Identifier("trampoline")).WithType(type).WithModifiers(TokenList(Token(SyntaxKind.OutKeyword)))
])))
.WithBody(Block(List<StatementSyntax>(
[
ExpressionStatement(InvocationExpression(IdentifierName("Attach"))
.WithArgumentList(ArgumentList(SeparatedList(
[
Argument(CastExpression(IdentifierName("nint"), IdentifierName("origin"))),
Argument(CastExpression(IdentifierName("nint"), IdentifierName("handler"))),
Argument(DeclarationExpression(IdentifierName("nint"), SingleVariableDesignation(Identifier("trampoline1"))))
.WithRefKindKeyword(Token(SyntaxKind.OutKeyword))
])))),
ExpressionStatement(AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName("trampoline"),
CastExpression(type, IdentifierName("trampoline1"))))
])));
}
}
private record AttachInfo
{
public required string MinimallyQualifiedType { get; init; }
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageReference Include="PolySharp" Version="1.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
<Solution>
<Project Path="YaeAchievementLib\YaeAchievementLib.csproj" Type="Classic C#" />
<Project Path="YaeAchievement\YaeAchievement.csproj" Type="Classic C#" />
<Project Path="YaeAchievement.SourceGeneration/YaeAchievement.SourceGeneration.csproj" />
<Project Path="YaeAchievementLib\YaeAchievementLib.csproj" />
<Project Path="YaeAchievement\YaeAchievement.csproj" />
</Solution>

View File

@@ -1,28 +1,30 @@
CloseClipboard
CreateProcess
CreateRemoteThread
EmptyClipboard
GetConsoleMode
GetDC
GetDeviceCaps
GetModuleHandle
GetProcAddress
GetStdHandle
// kernel32
GlobalLock
OpenProcess
GetStdHandle
GlobalUnlock
OpenClipboard
ResumeThread
SetClipboardData
Module32Next
Module32First
CreateProcess
LoadLibraryEx
VirtualFreeEx
VirtualAllocEx
GetProcAddress
GetConsoleMode
SetConsoleMode
TerminateProcess
VirtualAllocEx
VirtualFreeEx
WaitForSingleObject
CreateRemoteThread
WriteProcessMemory
WaitForSingleObject
GetCurrentConsoleFontEx
CreateToolhelp32Snapshot
OpenProcess
// psapi
GetModuleFileNameEx
LoadLibraryEx
// user32
OpenClipboard
CloseClipboard
EmptyClipboard
SetClipboardData

View File

@@ -7,8 +7,8 @@
<ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFramework>net9.0-windows</TargetFramework>
<FileVersion>5.7.1</FileVersion>
<AssemblyVersion>5.7.1</AssemblyVersion>
<FileVersion>5.7.2</FileVersion>
<AssemblyVersion>5.7.2</AssemblyVersion>
<ApplicationIcon>res\icon.ico</ApplicationIcon>
<ApplicationManifest>res\app.manifest</ApplicationManifest>
</PropertyGroup>

View File

@@ -19,8 +19,15 @@ message AchievementItem {
message MethodRvaConfig {
uint32 do_cmd = 1;
uint32 to_uint16 = 2;
uint32 update_normal_prop = 3;
uint32 new_string = 4;
uint32 find_game_object = 5;
uint32 event_system_update = 6;
uint32 simulate_pointer_click = 7;
uint32 to_int32 = 8;
uint32 tcp_state_ptr = 9;
uint32 shared_info_ptr = 10;
uint32 decompress = 11;
}
message NativeLibConfig {

View File

@@ -18,8 +18,8 @@ public static class GlobalVars {
public static readonly string CachePath = Path.Combine(DataPath, "cache");
public static readonly string LibFilePath = Path.Combine(DataPath, "YaeAchievement.dll");
public const uint AppVersionCode = 241;
public const string AppVersionName = "5.7.1";
public const uint AppVersionCode = 243;
public const string AppVersionName = "5.7.3";
public const string PipeName = "YaeAchievementPipe";

View File

@@ -7,8 +7,6 @@ using static YaeAchievement.Utils;
namespace YaeAchievement;
// TODO: WndHook
internal static class Program {
public static async Task Main(string[] args) {

View File

@@ -1,7 +1,9 @@
using System.ComponentModel;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Diagnostics.ToolHelp;
using Windows.Win32.System.LibraryLoader;
using Windows.Win32.System.Threading;
using Spectre.Console;
@@ -73,7 +75,39 @@ internal sealed unsafe class GameProcess {
if (Native.WaitForSingleObject(hThread, 2000) == 0) {
Native.VirtualFreeEx(Handle, lpLibPath, 0, MEM_RELEASE);
}
var libHandle = Native.LoadLibraryEx(libPath, LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES);
// Get lib base address in target process
byte* baseAddress = null;
SafeFileHandle hSnap;
do
{
hSnap = Native.CreateToolhelp32Snapshot_SafeHandle(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPMODULE, Id);
if (Marshal.GetLastSystemError() is not (0 or 24)) // ERROR_BAD_LENGTH
{
break;
}
}
while (hSnap.IsInvalid);
if (hSnap.IsInvalid) {
throw new Win32Exception { Data = { { "api", "CreateToolhelp32Snapshot" } } };
}
using (hSnap) {
var moduleEntry = new MODULEENTRY32 {
dwSize = (uint) sizeof(MODULEENTRY32)
};
if (Native.Module32First(hSnap, ref moduleEntry)) {
do {
if (new string((sbyte*) &moduleEntry.szExePath._0) == libPath) {
baseAddress = moduleEntry.modBaseAddr;
break;
}
} while (Native.Module32Next(hSnap, ref moduleEntry));
}
}
if (baseAddress == null) {
throw new InvalidOperationException("No matching module found in target process.");
}
//
using var libHandle = Native.LoadLibraryEx(libPath, LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES);
if (libHandle.IsInvalid) {
throw new Win32Exception { Data = { { "api", "LoadLibraryEx" } } };
}
@@ -81,7 +115,9 @@ internal sealed unsafe class GameProcess {
if (libMainProc.IsNull) {
throw new Win32Exception { Data = { { "api", "GetProcAddress" } } };
}
var lpStartAddress2 = (delegate*unmanaged[Stdcall]<void*, uint>) libMainProc.Value; // THREAD_START_ROUTINE
var libMainProcRVA = libMainProc.Value - libHandle.DangerousGetHandle();
var lpStartAddress2 = (delegate*unmanaged[Stdcall]<void*, uint>) (baseAddress + libMainProcRVA); // THREAD_START_ROUTINE
//
var hThread2 = Native.CreateRemoteThread(Handle, null, 0, lpStartAddress2, null, 0);
if (hThread2.IsNull) {
throw new Win32Exception { Data = { { "api", "CreateRemoteThread2" } } };

View File

@@ -35,18 +35,21 @@ public static class Utils {
//
if (_updateInfo?.CdnFiles.TryGetValue(path, out var cdnFile) == true) {
try {
var tasks = cdnFile.Urls.Select(url => GetFileFromCdn(url, cacheKey, cdnFile.Hash, cdnFile.Size));
var data = await Task.WhenAny(tasks).Unwrap();
var data = await cdnFile.Urls
.Select(url => GetFileFromCdn(url, cacheKey, cdnFile.Hash, cdnFile.Size))
.WhenFirstSuccessful()
.Unwrap();
transaction.Finish();
return data;
} catch (Exception e) when (e is SocketException or TaskCanceledException or HttpRequestException or InvalidDataException) {}
}
//
try {
var data = await Task.WhenAny(
var data = await WhenFirstSuccessful([
GetFileReal($"https://rin.holohat.work/{path}", cacheKey),
GetFileReal($"https://ena-rin.holohat.work//{path}", cacheKey),
GetFileReal($"https://cn-cd-1259389942.file.myqcloud.com/{path}", cacheKey)
).Unwrap();
]).Unwrap();
transaction.Finish();
return data;
} catch (Exception e) when (e is SocketException or TaskCanceledException or HttpRequestException) {
@@ -257,8 +260,15 @@ public static class Utils {
break;
case 0xFD:
writer.Write(methodRva.DoCmd);
writer.Write(methodRva.ToUint16);
writer.Write(methodRva.UpdateNormalProp);
writer.Write(methodRva.NewString);
writer.Write(methodRva.FindGameObject);
writer.Write(methodRva.EventSystemUpdate);
writer.Write(methodRva.SimulatePointerClick);
writer.Write(methodRva.ToInt32);
writer.Write(methodRva.TcpStatePtr);
writer.Write(methodRva.SharedInfoPtr);
writer.Write(methodRva.Decompress);
break;
case 0xFE:
_proc!.ResumeMainThread();
@@ -323,4 +333,26 @@ public static class Utils {
CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US"); // todo: use better way like auto set console font etc.
}
}
// https://stackoverflow.com/a/76953892
private static async Task<Task<TResult>> WhenFirstSuccessful<TResult>(this IEnumerable<Task<TResult>> tasks) {
var cts = new CancellationTokenSource();
Task<TResult>? selectedTask = null;
var continuations = tasks
.TakeWhile(_ => !cts.IsCancellationRequested)
.Select(task => {
return task.ContinueWith(t => {
if (t.IsCompletedSuccessfully) {
if (Interlocked.CompareExchange(ref selectedTask, t, null) is null) {
cts.Cancel();
}
}
}, cts.Token, TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
});
var whenAll = Task.WhenAll(continuations);
try {
await whenAll.ConfigureAwait(false);
} catch when (whenAll.IsCanceled) { /* ignore */ }
return selectedTask!;
}
}

View File

@@ -25,25 +25,25 @@
</ItemGroup>
<ItemGroup>
<DirectPInvoke Include="NTDLL"/>
<DirectPInvoke Include="USER32"/>
<DirectPInvoke Include="KERNEL32"/>
<DirectPInvoke Include="libMinHook.x64"/>
<NativeLibrary Include="lib\libMinHook.x64.lib"/>
<DirectPInvoke Include="NTDLL" />
<DirectPInvoke Include="USER32" />
<DirectPInvoke Include="KERNEL32" />
<DirectPInvoke Include="libMinHook.x64" />
<NativeLibrary Include="lib\libMinHook.x64.lib" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute"/>
<AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Diagnostics"/>
<Using Include="System.Diagnostics.CodeAnalysis"/>
<Using Include="System.Diagnostics" />
<Using Include="System.Diagnostics.CodeAnalysis" />
</ItemGroup>
<PropertyGroup>
<PackageId>Yae.Lib</PackageId>
<Version>5.4.2</Version>
<Version>5.4.4</Version>
<Authors>HoloHat</Authors>
<DevelopmentDependency>true</DevelopmentDependency>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
@@ -54,11 +54,18 @@
</PropertyGroup>
<ItemGroup>
<None Include="$(PublishDir)\$(TargetName)$(NativeBinaryExt)" Pack="true" PackagePath="runtimes\win-x64\native" Visible="false"/>
<None Include="$(PublishDir)\$(TargetName)$(NativeBinaryExt)" Pack="true" PackagePath="runtimes\win-x64\native" Visible="false" />
</ItemGroup>
<ItemGroup>
<ProjectReference OutputItemType="Analyzer" Include="..\YaeAchievement.SourceGeneration\YaeAchievement.SourceGeneration.csproj">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</ProjectReference>
</ItemGroup>
<Target Name="GenerateNuGetPackage" AfterTargets="CopyNativeBinary">
<Exec Command="dotnet pack --no-build --nologo" UseUtf8Encoding="Always" EchoOff="true"/>
<Exec Command="dotnet pack --no-build --nologo" UseUtf8Encoding="Always" EchoOff="true" />
</Target>
</Project>

View File

@@ -2,6 +2,7 @@ using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Yae.Utilities;
using static Yae.GameMethod;
namespace Yae;
@@ -26,9 +27,10 @@ internal static unsafe class Application {
Log.ResetConsole();
//
RecordChecksum();
MinHook.Attach(GameMethod.DoCmd, &OnDoCmd, out _doCmd);
MinHook.Attach(GameMethod.ToUInt16, &OnToUInt16, out _toUInt16);
MinHook.Attach(GameMethod.UpdateNormalProp, &OnUpdateNormalProp, out _updateNormalProp);
MinHook.Attach(DoCmd, &OnDoCmd, out _doCmd);
MinHook.Attach(ToInt32, &OnToInt32, out _toInt32);
MinHook.Attach(UpdateNormalProp, &OnUpdateNormalProp, out _updateNormalProp);
MinHook.Attach(EventSystemUpdate, &OnEventSystemUpdate, out _eventSystemUpdate);
return 0;
}
@@ -40,12 +42,12 @@ internal static unsafe class Application {
#region RecvPacket
private static delegate*unmanaged<byte*, int, ushort> _toUInt16;
private static delegate*unmanaged<byte*, int, int> _toInt32;
[UnmanagedCallersOnly]
private static ushort OnToUInt16(byte* val, int startIndex) {
var ret = _toUInt16(val, startIndex);
if (ret != 0xAB89 || *(ushort*) (val += 0x20) != 0x6745) {
private static int OnToInt32(byte* val, int startIndex) {
var ret = _toInt32(val, startIndex);
if (startIndex != 6 || *(ushort*) (val += 0x20) != 0x6745) {
return ret;
}
var cmdId = BinaryPrimitives.ReverseEndianness(*(ushort*) (val + 2));
@@ -57,9 +59,59 @@ internal static unsafe class Application {
return ret;
static Span<byte> GetData(byte* val) {
var headLen = BinaryPrimitives.ReverseEndianness(*(ushort*) (val + 4));
var headPtr = val + 10;
var dataLen = BinaryPrimitives.ReverseEndianness(*(uint*) (val + 6));
return new Span<byte>(val + 10 + headLen, (int) dataLen);
var dataPtr = val + 10 + headLen;
var unzipLen = GetDecompressedSize(new Span<byte>(headPtr, headLen));
if (unzipLen == 0) {
return new Span<byte>(dataPtr, (int) dataLen);
}
var unzipBuf = NativeMemory.Alloc(unzipLen);
if (!Decompress(*TcpStatePtr, *SharedInfoPtr, dataPtr, dataLen, unzipBuf, unzipLen)) {
throw new InvalidDataException("Decompress failed.");
}
return new Span<byte>(unzipBuf, (int) unzipLen);
}
}
private static uint GetDecompressedSize(Span<byte> header) {
var offset = 0;
ulong tag;
while (offset != header.Length && (tag = ReadRawVarInt64(header, ref offset)) != 0) {
if (tag == 64) {
return (uint) ReadRawVarInt64(header, ref offset);
}
switch (tag & 7) {
case 0:
ReadRawVarInt64(header, ref offset);
break;
case 1:
offset += 8;
break;
case 2:
offset += (int) ReadRawVarInt64(header, ref offset);
break;
case 3:
case 4:
throw new NotSupportedException();
case 5:
offset += 4;
break;
}
}
return 0;
}
private static ulong ReadRawVarInt64(this Span<byte> span, ref int offset) {
ulong result = 0;
for (var i = 0; i < 8; i++) {
var b = span[offset++];
result |= (ulong) (b & 0x7F) << (i * 7);
if (b < 0x80) {
return result;
}
}
throw new InvalidDataException("CodedInputStream encountered a malformed varint.");
}
#endregion
@@ -117,7 +169,7 @@ internal static unsafe class Application {
Buffer = buffer,
Length = 256
};
_ = GameMethod.DoCmd(23, Unsafe.AsPointer(ref data), sizeof(RecordChecksumCmdData));
_ = DoCmd(23, Unsafe.AsPointer(ref data), sizeof(RecordChecksumCmdData));
RecordedChecksum[i] = data;
//REPL//Log.Trace($"nType={i}, value={new string((sbyte*) buffer, 0, data.Length)}");
}
@@ -142,4 +194,24 @@ internal static unsafe class Application {
#endregion
#region EnterGate
private static long _lastTryEnterTime;
private static delegate*unmanaged<nint, void> _eventSystemUpdate;
[UnmanagedCallersOnly]
public static void OnEventSystemUpdate(nint @this) {
_eventSystemUpdate(@this);
if (Environment.TickCount64 - _lastTryEnterTime > 200) {
var obj = FindGameObject(NewString("BtnStart"u8.AsPointer()));
if (obj != 0 && SimulatePointerClick(@this, obj)) {
MinHook.Detach((nint) EventSystemUpdate);
}
_lastTryEnterTime = Environment.TickCount64;
}
}
#endregion
}

View File

@@ -15,10 +15,24 @@ 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; }
public static delegate*unmanaged<nint, nint> NewString { get; set; }
public static delegate*unmanaged<nint, nint> FindGameObject { get; set; }
public static delegate*unmanaged<nint, void> EventSystemUpdate { get; set; }
public static delegate*unmanaged<nint, nint, bool> SimulatePointerClick { get; set; }
public static delegate*unmanaged<byte*, int, int> ToInt32 { get; set; }
public static void** TcpStatePtr { get; set; }
public static void** SharedInfoPtr { get; set; }
public static delegate*unmanaged<void*, void*, void*, uint, void*, uint, bool> Decompress { get; set; }
}
internal static class Goshujin {
@@ -75,8 +89,15 @@ internal static class Goshujin {
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());
GameMethod.NewString = (delegate*unmanaged<nint, nint>) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.FindGameObject = (delegate*unmanaged<nint, nint>) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.EventSystemUpdate = (delegate*unmanaged<nint, void>) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.SimulatePointerClick = (delegate*unmanaged<nint, nint, bool>) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.ToInt32 = (delegate*unmanaged<byte*, int, int>) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.TcpStatePtr = (void**) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.SharedInfoPtr = (void**) Native.RVAToVA(_pipeReader.ReadUInt32());
GameMethod.Decompress = (delegate*unmanaged<void*, void*, void*, uint, void*, uint, bool>) Native.RVAToVA(_pipeReader.ReadUInt32());
}
public static void ResumeMainThread() {

View File

@@ -93,6 +93,9 @@ internal static unsafe class Native {
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nint AsPointer(this ReadOnlySpan<byte> span) => *(nint*) Unsafe.AsPointer(ref span);
}
internal static partial class MinHook {
@@ -146,6 +149,7 @@ internal static partial class MinHook {
/// Uninitialize the MinHook library. You must call this function EXACTLY ONCE at the end of your program.
/// </summary>
[LibraryImport("libMinHook.x64", EntryPoint = "MH_Uninitialize")]
// ReSharper disable once UnusedMember.Local
private static partial uint MinHookUninitialize();
static MinHook() {
@@ -155,30 +159,6 @@ internal static partial class MinHook {
}
}
// 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) {