mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-20 11:39:43 +00:00
137 lines
5.3 KiB
C#
137 lines
5.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection.Emit;
|
|
using HarmonyLib;
|
|
using MonoMod.RuntimeDetour;
|
|
using MonoMod.RuntimeDetour.Platforms;
|
|
using MonoMod.Utils;
|
|
|
|
namespace BepInEx.Preloader.RuntimeFixes
|
|
{
|
|
internal static class XTermFix
|
|
{
|
|
public static int intOffset;
|
|
|
|
public static void Apply()
|
|
{
|
|
if (PlatformHelper.Is(Platform.Windows))
|
|
return;
|
|
|
|
if (typeof(Console).Assembly.GetType("System.ConsoleDriver") == null)
|
|
// Mono version is too old, use our own TTY implementation instead
|
|
return;
|
|
|
|
if (AccessTools.Method("System.TermInfoReader:DetermineVersion") != null)
|
|
// Fix has been applied officially
|
|
return;
|
|
|
|
// Apparently on older Unity versions (4.x), using Process.Start can run Console..cctor
|
|
// And since MonoMod's PlatformHelper (used by DetourHelper.Native) runs Process.Start to determine ARM/x86 platform,
|
|
// this causes a crash owing to TermInfoReader running before it can be patched and fixed
|
|
// Because Doorstop does not support ARM at the moment, we can get away with just forcing x86 detour platform.
|
|
// TODO: Figure out a way to detect ARM on Unix without running Process.Start
|
|
DetourHelper.Native = new DetourNativeX86Platform();
|
|
|
|
var harmony = new Harmony("com.bepinex.xtermfix");
|
|
|
|
harmony.Patch(AccessTools.Method("System.TermInfoReader:ReadHeader"),
|
|
new HarmonyMethod(typeof(XTermFix), nameof(ReadHeaderPrefix)));
|
|
|
|
harmony.Patch(AccessTools.Method("System.TermInfoReader:Get", new[] { AccessTools.TypeByName("System.TermInfoNumbers") }),
|
|
transpiler: new HarmonyMethod(typeof(XTermFix), nameof(GetTermInfoNumbersTranspiler)));
|
|
|
|
harmony.Patch(AccessTools.Method("System.TermInfoReader:Get", new[] { AccessTools.TypeByName("System.TermInfoStrings") }),
|
|
transpiler: new HarmonyMethod(typeof(XTermFix), nameof(GetTermInfoStringsTranspiler)));
|
|
|
|
harmony.Patch(AccessTools.Method("System.TermInfoReader:GetStringBytes", new[] { AccessTools.TypeByName("System.TermInfoStrings") }),
|
|
transpiler: new HarmonyMethod(typeof(XTermFix), nameof(GetTermInfoStringsTranspiler)));
|
|
|
|
DetourHelper.Native = null;
|
|
}
|
|
|
|
public static int GetInt32(byte[] buffer, int offset)
|
|
{
|
|
int b1 = buffer[offset];
|
|
int b2 = buffer[offset + 1];
|
|
int b3 = buffer[offset + 2];
|
|
int b4 = buffer[offset + 3];
|
|
|
|
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
|
|
}
|
|
|
|
public static short GetInt16(byte[] buffer, int offset)
|
|
{
|
|
int b1 = buffer[offset];
|
|
int b2 = buffer[offset + 1];
|
|
|
|
return (short) (b1 | (b2 << 8));
|
|
}
|
|
|
|
public static int GetInteger(byte[] buffer, int offset) =>
|
|
intOffset == 2
|
|
? GetInt16(buffer, offset)
|
|
: GetInt32(buffer, offset);
|
|
|
|
public static void DetermineVersion(short magic)
|
|
{
|
|
if (magic == 0x11a)
|
|
intOffset = 2;
|
|
else if (magic == 0x21e)
|
|
intOffset = 4;
|
|
else
|
|
throw new Exception($"Unknown xterm header format: {magic}");
|
|
}
|
|
|
|
public static bool ReadHeaderPrefix(byte[] buffer,
|
|
ref int position,
|
|
ref short ___boolSize,
|
|
ref short ___numSize,
|
|
ref short ___strOffsets)
|
|
{
|
|
var magic = GetInt16(buffer, position);
|
|
position += 2;
|
|
DetermineVersion(magic);
|
|
|
|
// nameSize = GetInt16(buffer, position);
|
|
position += 2;
|
|
___boolSize = GetInt16(buffer, position);
|
|
position += 2;
|
|
___numSize = GetInt16(buffer, position);
|
|
position += 2;
|
|
___strOffsets = GetInt16(buffer, position);
|
|
position += 2;
|
|
// strSize = GetInt16(buffer, position);
|
|
position += 2;
|
|
|
|
return false;
|
|
}
|
|
|
|
public static IEnumerable<CodeInstruction> GetTermInfoNumbersTranspiler(
|
|
IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
// This implementation does not seem to have changed so I will be using indexes like the lazy fuck I am
|
|
|
|
var list = instructions.ToList();
|
|
|
|
list[31] = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(XTermFix), nameof(intOffset)));
|
|
list[36] = new CodeInstruction(OpCodes.Nop);
|
|
list[39] = new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(XTermFix), nameof(GetInteger)));
|
|
|
|
return list;
|
|
}
|
|
|
|
public static IEnumerable<CodeInstruction> GetTermInfoStringsTranspiler(
|
|
IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
// This implementation does not seem to have changed so I will be using indexes like the lazy fuck I am
|
|
|
|
var list = instructions.ToList();
|
|
|
|
list[32] = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(XTermFix), nameof(intOffset)));
|
|
|
|
return list;
|
|
}
|
|
}
|
|
}
|