XTermFix.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection.Emit;
  5. using HarmonyLib;
  6. using MonoMod.Utils;
  7. namespace BepInEx.Preloader.RuntimeFixes
  8. {
  9. internal static class XTermFix
  10. {
  11. public static void Apply()
  12. {
  13. if (Utility.CurrentOs == Platform.Windows)
  14. return;
  15. if (typeof(Console).Assembly.GetType("System.ConsoleDriver") == null)
  16. {
  17. // Mono version is too old, use our own TTY implementation instead
  18. return;
  19. }
  20. if (AccessTools.Method("System.TermInfoReader:DetermineVersion") != null)
  21. {
  22. // Fix has been applied officially
  23. return;
  24. }
  25. var harmony = new HarmonyLib.Harmony("com.bepinex.xtermfix");
  26. harmony.Patch(AccessTools.Method("System.TermInfoReader:ReadHeader"),
  27. prefix: new HarmonyMethod(typeof(XTermFix), nameof(ReadHeaderPrefix)));
  28. harmony.Patch(AccessTools.Method("System.TermInfoReader:Get", new []{ AccessTools.TypeByName("System.TermInfoNumbers") }),
  29. transpiler: new HarmonyMethod(typeof(XTermFix), nameof(GetTermInfoNumbersTranspiler)));
  30. harmony.Patch(AccessTools.Method("System.TermInfoReader:Get", new []{ AccessTools.TypeByName("System.TermInfoStrings") }),
  31. transpiler: new HarmonyMethod(typeof(XTermFix), nameof(GetTermInfoStringsTranspiler)));
  32. harmony.Patch(AccessTools.Method("System.TermInfoReader:GetStringBytes", new []{ AccessTools.TypeByName("System.TermInfoStrings") }),
  33. transpiler: new HarmonyMethod(typeof(XTermFix), nameof(GetTermInfoStringsTranspiler)));
  34. }
  35. public static int intOffset;
  36. public static int GetInt32(byte[] buffer, int offset)
  37. {
  38. int b1 = buffer[offset];
  39. int b2 = buffer[offset + 1];
  40. int b3 = buffer[offset + 2];
  41. int b4 = buffer[offset + 3];
  42. return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
  43. }
  44. public static short GetInt16(byte[] buffer, int offset)
  45. {
  46. int b1 = buffer[offset];
  47. int b2 = buffer[offset + 1];
  48. return (short)(b1 | (b2 << 8));
  49. }
  50. public static int GetInteger(byte[] buffer, int offset)
  51. {
  52. return intOffset == 2
  53. ? GetInt16(buffer, offset)
  54. : GetInt32(buffer, offset);
  55. }
  56. public static void DetermineVersion(short magic)
  57. {
  58. if (magic == 0x11a)
  59. intOffset = 2;
  60. else if (magic == 0x21e)
  61. intOffset = 4;
  62. else
  63. throw new Exception($"Unknown xterm header format: {magic}");
  64. }
  65. public static bool ReadHeaderPrefix(byte[] buffer, ref int position, ref short ___boolSize, ref short ___numSize, ref short ___strOffsets)
  66. {
  67. short magic = GetInt16(buffer, position);
  68. position += 2;
  69. DetermineVersion(magic);
  70. // nameSize = GetInt16(buffer, position);
  71. position += 2;
  72. ___boolSize = GetInt16(buffer, position);
  73. position += 2;
  74. ___numSize = GetInt16(buffer, position);
  75. position += 2;
  76. ___strOffsets = GetInt16(buffer, position);
  77. position += 2;
  78. // strSize = GetInt16(buffer, position);
  79. position += 2;
  80. return false;
  81. }
  82. public static IEnumerable<CodeInstruction> GetTermInfoNumbersTranspiler(IEnumerable<CodeInstruction> instructions)
  83. {
  84. // This implementation does not seem to have changed so I will be using indexes like the lazy fuck I am
  85. var list = instructions.ToList();
  86. list[31] = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(XTermFix), nameof(intOffset)));
  87. list[36] = new CodeInstruction(OpCodes.Nop);
  88. list[39] = new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(XTermFix), nameof(GetInteger)));
  89. return list;
  90. }
  91. public static IEnumerable<CodeInstruction> GetTermInfoStringsTranspiler(IEnumerable<CodeInstruction> instructions)
  92. {
  93. // This implementation does not seem to have changed so I will be using indexes like the lazy fuck I am
  94. var list = instructions.ToList();
  95. list[32] = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(XTermFix), nameof(intOffset)));
  96. return list;
  97. }
  98. }
  99. }