Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildWin.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cd $PSScriptRoot/sources
dotnet build -c=Release ./ModCore

echo "Building Shell"
dotnet publish -c=Release -a x86 ./DeadCellsModding
dotnet publish -c=Release -r win-x86 ./DeadCellsModding

echo "Building Native"
cd native
Expand Down
22 changes: 18 additions & 4 deletions sources/DeadCellsCoreModding.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ci", "ci", "{D82BFBA7-3D6C-
..\.github\workflows\build.yml = ..\.github\workflows\build.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModCore.Common", "ModCore.Common\ModCore.Common.csproj", "{5666766A-4901-4F75-8F1E-0BF433A9F55D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -55,8 +57,8 @@ Global
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|x64.ActiveCfg = Debug|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|x64.Build.0 = Debug|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|x86.ActiveCfg = Debug|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|x86.Build.0 = Debug|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|x86.ActiveCfg = Debug|x86
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Debug|x86.Build.0 = Debug|x86
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Release|Any CPU.Build.0 = Release|Any CPU
{798EC443-C229-4F22-86B4-8BDE18266DBA}.Release|x64.ActiveCfg = Release|Any CPU
Expand All @@ -67,14 +69,26 @@ Global
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|Any CPU.Build.0 = Debug|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|x64.ActiveCfg = Debug|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|x64.Build.0 = Debug|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|x86.ActiveCfg = Debug|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|x86.Build.0 = Debug|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|x86.ActiveCfg = Debug|x86
{148A63DD-1249-49B7-80D8-601BABA6F236}.Debug|x86.Build.0 = Debug|x86
{148A63DD-1249-49B7-80D8-601BABA6F236}.Release|Any CPU.ActiveCfg = Release|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Release|Any CPU.Build.0 = Release|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Release|x64.ActiveCfg = Release|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Release|x64.Build.0 = Release|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Release|x86.ActiveCfg = Release|Any CPU
{148A63DD-1249-49B7-80D8-601BABA6F236}.Release|x86.Build.0 = Release|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Debug|x64.ActiveCfg = Debug|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Debug|x64.Build.0 = Debug|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Debug|x86.ActiveCfg = Debug|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Debug|x86.Build.0 = Debug|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Release|Any CPU.Build.0 = Release|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Release|x64.ActiveCfg = Release|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Release|x64.Build.0 = Release|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Release|x86.ActiveCfg = Release|Any CPU
{5666766A-4901-4F75-8F1E-0BF433A9F55D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

[assembly: SuppressMessage("Interoperability", "CA1401:P/Invokes 应该是不可见的")]

[assembly: InternalsVisibleTo("ModCore")]
66 changes: 66 additions & 0 deletions sources/HashlinkSharp/Brigde/MethodWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Hashlink.Marshaling;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hashlink.Brigde
{
public sealed unsafe class MethodWrapper : IDisposable
{
private bool disposedValue;

private static readonly ConcurrentDictionary<Type, Func<object[], object>> targetCaller = [];
private readonly Delegate target;
private MethodWrapperFactory.EntryItem* entry;

public object? Data { get; set; }
public nint EntryPointer => (nint)entry->table.entryPtr;
public nint RedirectTarget
{
get
{
return entry->table.origFuncPtr;
}
set
{
entry->table.origFuncPtr = value;
}
}

internal MethodWrapperFactory.EntryItem* EntryHandle => entry;

public MethodWrapper(Delegate target,
HL_type.TypeKind retType,
IEnumerable<HL_type.TypeKind> argTypes)
{
this.target = target;
entry = MethodWrapperFactory.CreateWrapper(this, argTypes, retType);
}

internal void Entry(MethodWrapperFactory.NativeInfoTable* table, void* retVal, long* argPtr)
{
var args = new object?[table->argsCount];
for (int i = 0; i < table->argsCount; i++)
{
var at = (HL_type.TypeKind) table->targs[i];
args[i] = HashlinkMarshal.ReadData(argPtr + i, at);
}
}

public void Dispose()
{
if (disposedValue)
{
return;
}
disposedValue = true;

MethodWrapperFactory.FreeWrapper(this);

entry = null;
}
}
}
206 changes: 206 additions & 0 deletions sources/HashlinkSharp/Brigde/MethodWrapperFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using Hashlink.Marshaling;
using Hashlink.Track;
using ModCore;
using MonoMod.Core.Platforms;
using MonoMod.RuntimeDetour;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Hashlink.Brigde
{
internal unsafe static class MethodWrapperFactory
{
private readonly static int PAGE_ALLOC_SIZE = 8192;
private static readonly byte[] call_code_x64 = [
0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //mov rax, 0xffffffffffffffff
0xFF, 0xD0 //call rax
];
private static readonly byte[] call_code_x86 = [
0xB8, 0XFF, 0xFF, 0xFF, 0xFF, //mov eax, 0xffffffff
0xFF, 0xD0 //call eax
];

private static readonly void* csentry_no_orig_ptr = (delegate* unmanaged[Cdecl]<void>)&CSEntry_NoOriginal;
private static readonly void* csentry_ptr = (delegate* unmanaged[Cdecl]<NativeInfoTable*, void*, long*, void>)&CSEntry;
private static readonly Queue<nint> freeEntries = [];

[MethodImpl(MethodImplOptions.Synchronized)]
public static EntryItem* CreateWrapper(MethodWrapper wrapper,
IEnumerable<HL_type.TypeKind> argTypes, HL_type.TypeKind retType)
{
if (freeEntries.Count == 0)
{
//Allocate New Page
void* page = hl_alloc_executable_memory(PAGE_ALLOC_SIZE);
for(int i = 0; i < PAGE_ALLOC_SIZE; i+= sizeof(EntryItem))
{
freeEntries.Enqueue((nint)page + i);
}
}
EntryItem* entry = (EntryItem*)freeEntries.Dequeue();

InitEntryItem(entry);
var table = &entry->table;
table->wrapperHandle = (nint)GCHandle.Alloc(wrapper, GCHandleType.Normal);

//

table->callback = (nint)csentry_ptr;

table->tret = retType;

if (retType == HL_type.TypeKind.HF32 ||
retType == HL_type.TypeKind.HF64)
{
table->retType = 1;
}
else if (!Environment.Is64BitProcess && retType == HL_type.TypeKind.HI64)
{
table->retType = 2;
}
else
{
table->retType = 0;
}

//

foreach (var at in argTypes)
{
table->targs[table->argsCount] = (int)at;
table->argsCount++;

if(at == HL_type.TypeKind.HF32 ||
at == HL_type.TypeKind.HF64)
{
table->argFloatMarks |= 1;
if(at == HL_type.TypeKind.HF64)
{
table->argSizeBitMarks |= 1;
}
}
else
{
if(at.IsPointer() && Environment.Is64BitProcess)
{
table->argSizeBitMarks |= 1;
}
else if(at == HL_type.TypeKind.HI64)
{
table->argSizeBitMarks |= 1;
}
}

table->argSizeBitMarks <<= 1;
table->argFloatMarks <<= 1;
}

//

table->argSizeBitMarks = Utils.ReverseBits(table->argSizeBitMarks) >> (32 - table->argsCount);
table->argFloatMarks = Utils.ReverseBits(table->argFloatMarks) >> (32 - table->argsCount);


return entry;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public static void FreeWrapper(MethodWrapper wrapper)
{
if(wrapper.EntryHandle == null)
{
return;
}
var table = &wrapper.EntryHandle->table;
GCHandle.FromIntPtr(table->wrapperHandle).Free();

table->wrapperHandle = 0;
freeEntries.Enqueue((nint)wrapper.EntryHandle);
}

private static void* InitEntryItem(EntryItem* item)
{
item->table = new();
if (Environment.Is64BitProcess)
{
fixed (byte* code = call_code_x64)
{
Buffer.MemoryCopy(code, item->call_code_x64, call_code_x64.LongLength, call_code_x64.LongLength);
}
*(void**)(item->call_code_x64 + 2) = Native.get_asm_call_bridge_hl_to_cs();
return item->table.entryPtr = item->call_code_x64;
}
else
{
fixed (byte* code = call_code_x86)
{
Buffer.MemoryCopy(code, item->call_code_x86, call_code_x86.LongLength, call_code_x86.LongLength);
}
*(void**)(item->call_code_x86 + 1) = Native.get_asm_call_bridge_hl_to_cs();
return item->table.entryPtr = item->call_code_x86;
}
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
[CallFromHLOnly]
[StackTraceHidden]
private static void CSEntry_NoOriginal()
{
throw new InvalidOperationException("Trying to call an empty CSharp Method Wrapper");
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
[CallFromHLOnly]
[StackTraceHidden]
private static void CSEntry(NativeInfoTable* table, void* retVal, long* args)
{
if(table->wrapperHandle == 0 ||
table->enabled == 0)
{
throw new InvalidOperationException();
}
var gch = GCHandle.FromIntPtr(table->wrapperHandle);
var wrapper = (MethodWrapper?)gch.Target ?? throw new InvalidOperationException();
wrapper.Entry(table, retVal, args);
}


[StructLayout(LayoutKind.Explicit)]
public struct EntryItem
{
[FieldOffset(0)]
public fixed byte call_code_x64[1];
[FieldOffset(5)]
public fixed byte call_code_x86[1];
[FieldOffset(12)]
public NativeInfoTable table;

}

[StructLayout(LayoutKind.Sequential)]
public struct NativeInfoTable
{
//The following fields should be consistent with the hl2c_table in modcorenative.h
public int retType; /* 2: return int64 in x86, 1: return float/double, 0: return ptr/int32/int16/byte/others */
public nint origFuncPtr;
public int enabled; //Invalid, reserved
public int argsCount;
public uint argSizeBitMarks; /* bit marks : 1 means the parameter is 8 bytes long, otherwise it is 4 bytes long */
public uint argFloatMarks; /* bit marks : 1 means the parameter is float/double, otherwise it is int/ptr */
public nint callback;

//The following fields are not visible to the native layer

public fixed int targs[32];
public HL_type.TypeKind tret;

public void* entryPtr;
public nint wrapperHandle;
}
}
}
21 changes: 0 additions & 21 deletions sources/HashlinkSharp/HashlinkException.cs

This file was deleted.

Loading