|
@@ -0,0 +1,214 @@
|
|
|
+// Sections of this code have been abridged from https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TermInfoReader.cs under the MIT license
|
|
|
+
|
|
|
+using System;
|
|
|
+using System.IO;
|
|
|
+using System.Linq;
|
|
|
+using System.Text;
|
|
|
+
|
|
|
+namespace BepInEx.Unix
|
|
|
+{
|
|
|
+ internal class TtyInfo
|
|
|
+ {
|
|
|
+ public string TerminalType { get; set; } = "default";
|
|
|
+
|
|
|
+ public int MaxColors { get; set; }
|
|
|
+
|
|
|
+ public string[] ForegroundColorStrings { get; set; }
|
|
|
+
|
|
|
+ public static TtyInfo Default { get; } = new TtyInfo
|
|
|
+ {
|
|
|
+ MaxColors = 0
|
|
|
+ };
|
|
|
+
|
|
|
+ public string GetAnsiCode(ConsoleColor color)
|
|
|
+ {
|
|
|
+ if (MaxColors <= 0 || ForegroundColorStrings == null)
|
|
|
+ return string.Empty;
|
|
|
+
|
|
|
+ int index = (int)color % MaxColors;
|
|
|
+ return ForegroundColorStrings[index];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static class TtyHandler
|
|
|
+ {
|
|
|
+ private static readonly string[] ncursesLocations = new[]
|
|
|
+ {
|
|
|
+ "/usr/share/terminfo",
|
|
|
+ "/etc/terminfo",
|
|
|
+ "/usr/lib/terminfo",
|
|
|
+ "/lib/terminfo"
|
|
|
+ };
|
|
|
+
|
|
|
+ private static string TryTermInfoDir(string dir, string term)
|
|
|
+ {
|
|
|
+ string infoFilePath = $"{dir}/{(int)term[0]:x}/{term}";
|
|
|
+
|
|
|
+ if (File.Exists(infoFilePath))
|
|
|
+ return infoFilePath;
|
|
|
+
|
|
|
+ infoFilePath = Utility.CombinePaths(dir, term.Substring(0, 1), term);
|
|
|
+
|
|
|
+ if (File.Exists(infoFilePath))
|
|
|
+ return infoFilePath;
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string FindTermInfoPath(string term)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(term))
|
|
|
+ return null;
|
|
|
+
|
|
|
+ string termInfoVar = Environment.GetEnvironmentVariable("TERMINFO");
|
|
|
+ if (termInfoVar != null && Directory.Exists(termInfoVar))
|
|
|
+ {
|
|
|
+ string text = TryTermInfoDir(termInfoVar, term);
|
|
|
+ if (text != null)
|
|
|
+ {
|
|
|
+ return text;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (string location in ncursesLocations)
|
|
|
+ {
|
|
|
+ if (Directory.Exists(location))
|
|
|
+ {
|
|
|
+ string text = TryTermInfoDir(location, term);
|
|
|
+
|
|
|
+ if (text != null)
|
|
|
+ return text;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static TtyInfo GetTtyInfo(string terminal = null)
|
|
|
+ {
|
|
|
+ terminal = terminal ?? Environment.GetEnvironmentVariable("TERM");
|
|
|
+ var path = FindTermInfoPath(terminal);
|
|
|
+
|
|
|
+ if (path == null)
|
|
|
+ return TtyInfo.Default;
|
|
|
+
|
|
|
+ byte[] buffer = File.ReadAllBytes(path);
|
|
|
+
|
|
|
+ var info = TtyInfoParser.Parse(buffer);
|
|
|
+ info.TerminalType = terminal;
|
|
|
+
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static class TtyInfoParser
|
|
|
+ {
|
|
|
+ private static readonly int[] ansiColorMapping =
|
|
|
+ {
|
|
|
+ 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15
|
|
|
+ };
|
|
|
+
|
|
|
+ public static TtyInfo Parse(byte[] buffer)
|
|
|
+ {
|
|
|
+ int intSize;
|
|
|
+
|
|
|
+
|
|
|
+ int magic = GetInt16(buffer, 0);
|
|
|
+
|
|
|
+ switch (magic)
|
|
|
+ {
|
|
|
+ case 0x11a:
|
|
|
+ intSize = 2;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 0x21E:
|
|
|
+ intSize = 4;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ // Unknown ttyinfo format
|
|
|
+ return TtyInfo.Default;
|
|
|
+ }
|
|
|
+
|
|
|
+ int boolFieldLength = GetInt16(buffer, 4);
|
|
|
+ int intFieldLength = GetInt16(buffer, 6);
|
|
|
+ int strOffsetFieldLength = GetInt16(buffer, 8);
|
|
|
+
|
|
|
+ // Normally i'd put a more complete implementation here, but I only need to parse this info to get the max color count
|
|
|
+ // Feel free to implement the rest of this using these sources:
|
|
|
+ // https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TermInfoReader.cs
|
|
|
+ // https://invisible-island.net/ncurses/man/term.5.html
|
|
|
+ // https://invisible-island.net/ncurses/man/terminfo.5.html
|
|
|
+
|
|
|
+ int baseOffset = 12 + GetString(buffer, 12).Length + 1; // Skip the terminal name
|
|
|
+ baseOffset += boolFieldLength; // Length of bool field section
|
|
|
+ baseOffset += baseOffset % 2; // Correct for boundary
|
|
|
+
|
|
|
+ int colorOffset =
|
|
|
+ baseOffset
|
|
|
+ + (intSize * (int)TermInfoNumbers.MaxColors); // Finally the offset for the max color integer
|
|
|
+
|
|
|
+ //int stringOffset = baseOffset + (intSize * intFieldLength);
|
|
|
+
|
|
|
+ //int foregoundColorOffset =
|
|
|
+ // stringOffset
|
|
|
+ // + (2 * (int)TermInfoStrings.SetAForeground);
|
|
|
+
|
|
|
+ //foregoundColorOffset = stringOffset
|
|
|
+ // + (2 * strOffsetFieldLength)
|
|
|
+ // + GetInt16(buffer, foregoundColorOffset);
|
|
|
+
|
|
|
+ var info = new TtyInfo();
|
|
|
+
|
|
|
+ info.MaxColors = GetInteger(intSize, buffer, colorOffset);
|
|
|
+
|
|
|
+ //string setForegroundTemplate = GetString(buffer, foregoundColorOffset);
|
|
|
+
|
|
|
+ //info.ForegroundColorStrings = ansiColorMapping.Select(x => setForegroundTemplate.Replace("%p1%", x.ToString())).ToArray();
|
|
|
+ info.ForegroundColorStrings = ansiColorMapping.Select(x => $"\u001B[{(x > 7 ? 82 + x : 30 + x)}m").ToArray();
|
|
|
+
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int GetInt32(byte[] buffer, int offset)
|
|
|
+ {
|
|
|
+ return buffer[offset]
|
|
|
+ | (buffer[offset + 1] << 8)
|
|
|
+ | (buffer[offset + 2] << 16)
|
|
|
+ | (buffer[offset + 3] << 24);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static short GetInt16(byte[] buffer, int offset)
|
|
|
+ {
|
|
|
+ return (short)(buffer[offset]
|
|
|
+ | (buffer[offset + 1] << 8));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int GetInteger(int intSize, byte[] buffer, int offset)
|
|
|
+ {
|
|
|
+ return intSize == 2
|
|
|
+ ? GetInt16(buffer, offset)
|
|
|
+ : GetInt32(buffer, offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string GetString(byte[] buffer, int offset)
|
|
|
+ {
|
|
|
+ int length = 0;
|
|
|
+
|
|
|
+ while (buffer[offset + length] != 0x00)
|
|
|
+ length++;
|
|
|
+
|
|
|
+ return Encoding.ASCII.GetString(buffer, offset, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ internal enum TermInfoNumbers
|
|
|
+ {
|
|
|
+ MaxColors = 13
|
|
|
+ }
|
|
|
+
|
|
|
+ internal enum TermInfoStrings
|
|
|
+ {
|
|
|
+ SetAForeground = 359
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|