Created
June 25, 2022 10:37
-
-
Save krypt-lynx/29f3c17cdd283c3f88af515f1c330aee to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PBDiagnostics | |
// Simple tool to manipulate programmable block's Storage variable. | |
ParamsRouter paramsRouter; | |
public Program() | |
{ | |
IsolatedRun(() => | |
{ | |
Log.NewFrame(); | |
paramsRouter = new ParamsRouter(); | |
paramsRouter.Cases = new Dictionary<string, Action<string>> | |
{ | |
{ "read", ReadStorage }, | |
{ "write", WriteStorage }, | |
{ "export", ExportStorage }, | |
{ "import", ImportStorage }, | |
{ "clear", ClearStorage }, | |
{ "pbinfo", PBInfo }, | |
{ "blockinfo", BlockInfo }, | |
}; | |
ShowHelp(); | |
}); | |
} | |
private void ShowRunInfo(string arg, UpdateType ut) | |
{ | |
Log.WriteFormat("Received command \"{0}\"", arg); | |
Log.Write(string.Format("With update types: {0}", ut)); | |
Log.WriteLine(); | |
Log.WriteLine(); | |
} | |
private void ShowInfo() | |
{ | |
Log.Write("Block:"); | |
Log.Write(string.Format("Name: {0}", Me.CustomName)); | |
Log.Write(string.Format("Entity ID: {0}", Me.EntityId)); | |
Log.Write("Grid:"); | |
Log.Write(string.Format("Name: {0}", Me.CubeGrid.CustomName)); | |
Log.Write(string.Format("Entity ID: {0}", Me.CubeGrid.EntityId)); | |
Log.Write(string.Format("Static: {0}", Me.CubeGrid.IsStatic ? "Yes" : " No")); | |
Log.Write(string.Format("Type: {0}", Me.CubeGrid.GridSizeEnum)); | |
} | |
private void ShowHelp() | |
{ | |
Log.Write("Available commands:"); | |
Log.WriteLine(); | |
Log.Write("read - prints Storage variable"); | |
Log.Write("write <value> - writes passed value to Storage"); | |
Log.Write("export - exports Storage variable to Custom Data"); | |
Log.Write("import - imports new Storage value from Custom Data"); | |
Log.Write("clear - clears Storage variable"); | |
Log.Write("pbinfo - show pb block information"); | |
Log.Write("blockinfo <name> - show block information"); | |
} | |
private void PBInfo(string arg) | |
{ | |
ShowInfo(); | |
} | |
private void ImportStorage(string arg) | |
{ | |
Storage = Me.CustomData; | |
Log.Write("import done!"); | |
} | |
private void ExportStorage(string arg) | |
{ | |
if (arg.Equals("--force", StringComparison.InvariantCultureIgnoreCase) || string.IsNullOrWhiteSpace(Me.CustomData)) | |
{ | |
Me.CustomData = Storage; | |
Log.Write("export done!"); | |
} | |
else | |
{ | |
Log.Write("Export failed: CustomData is not empty.\n" + | |
"If you want to export stored data anyway use \"export --force\"\n" + | |
"Current CustomData value will lost"); | |
} | |
} | |
private void WriteStorage(string arg) | |
{ | |
Storage = arg; | |
Log.Write("write done!"); | |
} | |
private void ReadStorage(string arg) | |
{ | |
Log.Write("Storage is:"); | |
Log.Write(Storage); | |
} | |
private void ClearStorage(string arg) | |
{ | |
Storage = ""; | |
Log.Write("clear done!"); | |
} | |
private void BlockInfo(string arg) | |
{ | |
var block = GridTerminalSystem.GetBlockWithName(arg); | |
Log.Write("== Block =="); | |
Log.Write($"DefName:\n \"{block.DefinitionDisplayNameText}\""); | |
Log.Write($"Type Id/Subtype Id:\n \"{block.BlockDefinition.TypeId}\"\n \"{block.BlockDefinition.SubtypeId}\""); | |
Log.Write($"Type: {block.GetType().Name}"); | |
Log.Write($"CustomName:\n \"{block.CustomName}\""); | |
Log.Write($"Entity ID: {block.EntityId}"); | |
Log.Write(""); | |
Log.Write("== Status =="); | |
Log.Write($"OwnerId: {block.OwnerId}"); | |
Log.Write($"IsWorking: {block.IsWorking}"); | |
Log.Write($"IsFunctional: {block.IsFunctional}"); | |
Log.Write($""); | |
Log.Write("== Inventories =="); | |
InvList(block); | |
Log.Write($""); | |
Log.Write("== Text Surfaces =="); | |
SurfaceList(block); | |
Log.Write($""); | |
Log.Write("== Actions =="); | |
Actionlist(block); | |
Log.Write(""); | |
Log.Write("== Properties =="); | |
Proplist(block); | |
} | |
enum PropType | |
{ | |
Boolean, | |
StringBuilder, | |
Single, | |
Int64, | |
Color, | |
String, | |
} | |
private void SurfaceList(IMyTerminalBlock block) | |
{ | |
var provider = block as IMyTextSurfaceProvider; | |
if (provider == null) | |
{ | |
Log.Write($"block is not a surface provider"); | |
return; | |
} | |
Log.Write($"Surfaces count: {provider.SurfaceCount}"); | |
for (int i = 0, imax = provider.SurfaceCount; i < imax; i++) | |
{ | |
var surface = provider.GetSurface(i); | |
Log.Write($"surface {i}"); | |
Log.Write($" texture size: {surface.TextureSize}"); | |
Log.Write($" surface size: {surface.SurfaceSize}"); | |
var texModifier = (float)Math.Min(surface.TextureSize.X, surface.TextureSize.Y) / 512f; | |
Log.Write($" font size magic scale: {texModifier}"); | |
} | |
} | |
private void InvList(IMyTerminalBlock block) | |
{ | |
Log.Write($"Inventories count: {block.InventoryCount}"); | |
for (int i = 0, imax = block.InventoryCount; i < imax; i++) | |
{ | |
var inventory = block.GetInventory(i); | |
Log.Write($" inventory {i}: {inventory.CurrentMass}kg {inventory.CurrentVolume * 1000}/{inventory.MaxVolume * 1000}L"); | |
} | |
} | |
private void Proplist(IMyTerminalBlock block) | |
{ | |
List<ITerminalProperty> props = new List<ITerminalProperty>(); | |
block.GetProperties(props); | |
var allProps = new HashSet<string>(); | |
//var badProps = new HashSet<string>(); // Termimal Properties can have same ids, which makes them unaccessible | |
foreach (var prop in props) | |
{ | |
/*if (allProps.Contains(prop.Id)) | |
{ | |
badProps.Add(prop.Id); | |
} | |
else*/ | |
//{ | |
allProps.Add(prop.Id); | |
//} | |
} | |
foreach (var prop in props) | |
{ | |
// block.GetValue<object>(prop.Id) - Property is not of Type object <...> | |
object value = null; | |
try | |
{ | |
PropType propType; | |
//if (!badProps.Contains(prop.Id) && Enum.TryParse(prop.TypeName, out propType)) | |
if (Enum.TryParse(prop.TypeName, out propType)) | |
{ | |
switch (propType) | |
{ | |
case PropType.Boolean: | |
value = block.GetValueBool(prop.Id); | |
break; | |
case PropType.Single: | |
value = block.GetValueFloat(prop.Id); | |
break; | |
case PropType.Color: | |
value = block.GetValueColor(prop.Id); | |
break; | |
case PropType.StringBuilder: | |
value = block.GetValue<StringBuilder>(prop.Id); | |
break; | |
case PropType.String: | |
value = block.GetValue<string>(prop.Id); | |
break; | |
case PropType.Int64: | |
value = block.GetValue<long>(prop.Id); | |
break; | |
} | |
} | |
} | |
catch { } | |
Log.Write($"\"{prop.Id}\" ({prop.TypeName})\n \"{value}\""); | |
} | |
} | |
private void Actionlist(IMyTerminalBlock block) | |
{ | |
List<ITerminalAction> actions = new List<ITerminalAction>(); | |
block.GetActions(actions); | |
foreach (var action in actions) | |
{ | |
Log.Write($"{action.Id}\n {action.Name}"); | |
} | |
} | |
public void Main(string argument, UpdateType updateSource) | |
{ | |
IsolatedRun(() => | |
{ | |
Log.NewFrame(); | |
Action command = null; | |
if ((updateSource & UpdateType.Terminal) != 0) | |
{ | |
command = paramsRouter.GetCase(argument); | |
} | |
if (updateSource != UpdateType.Terminal) // not contains | |
{ | |
ShowRunInfo(argument, updateSource); | |
} | |
else | |
{ | |
if (command != null) | |
{ | |
command(); | |
} | |
else | |
{ | |
ShowRunInfo(argument, updateSource); | |
ShowHelp(); | |
} | |
} | |
}); | |
} | |
// ---------- | |
// Environment.cs | |
Exception lastException = null; | |
public static MyGridProgram Current; | |
public void IsolatedRun(Action work) | |
{ | |
if (lastException == null) | |
{ | |
try | |
{ | |
Current = this; | |
Log.NewFrame(); | |
work(); | |
Current = null; | |
} | |
catch (Exception e) | |
{ | |
lastException = e; | |
} | |
} | |
if (lastException != null) | |
{ | |
EchoException(); | |
return; | |
} | |
} | |
private void EchoException() | |
{ | |
Echo("Exception handled!"); | |
Echo("Please, make screenshot of this message and report the issue to developer"); | |
Echo("To recover recompile the script"); | |
Echo(lastException.Message); | |
Echo(lastException.StackTrace); | |
Echo(lastException.Message); | |
} | |
} | |
// ---------- | |
// ParamsRouter.cs | |
class ParamsRouter | |
{ | |
public Dictionary<string, Action<string>> Cases = null; | |
public Action NoArgsCase = null; | |
public Action<string, UpdateType> UnknownCase = null; | |
void GetCommand(string arg, out string cmd, out string tail) | |
{ | |
arg = arg.TrimStart(); | |
int firstSpace = arg.IndexOf(' '); | |
cmd = null; | |
tail = null; | |
if (firstSpace != -1) | |
{ | |
cmd = arg.Substring(0, firstSpace); | |
tail = arg.Substring(firstSpace + 1); | |
} | |
else | |
{ | |
cmd = arg; | |
tail = ""; | |
} | |
} | |
public Action GetCase(string arg) | |
{ | |
string command, tail; | |
GetCommand(arg, out command, out tail); | |
if (!string.IsNullOrEmpty(command)) | |
{ | |
if (Cases?.ContainsKey(command) ?? false) | |
{ | |
return () => { Cases[command](tail); }; | |
} | |
} | |
return null; | |
} | |
} | |
// ---------- | |
// Log.cs | |
enum LogLevel | |
{ | |
None, | |
Error, | |
Warning, | |
Verbose, | |
SpamMeToDeath | |
} | |
class Log | |
{ | |
public static Dictionary<string, LogLevel> LogLevels = null; | |
const string NewFrameSeparator = "--------------------------------------"; | |
static bool insertNewFrameSeparator = false; | |
static StringUtils strUtils = new StringUtils(new DebugFontDataProvider()); | |
public static void Write(string logcat, LogLevel level, object anObject) | |
{ | |
if (IsAllowed(logcat, level)) | |
{ | |
string str = string.Format("[{0}] {1}", logcat, anObject); | |
WriteInternal(str); | |
} | |
} | |
private static bool IsAllowed(string logcat, LogLevel level) | |
{ | |
LogLevel currentLevel = LogLevel.None; | |
bool isAllowed = LogLevels == null || ((LogLevels?.TryGetValue(logcat, out currentLevel) ?? false) && level <= currentLevel); | |
return isAllowed; | |
} | |
public static void WriteFormat(string logcat, LogLevel level, string format, params object[] args) | |
{ | |
if (IsAllowed(logcat, level)) | |
{ | |
string str = string.Format("[{0}] {1}", logcat, string.Format(format, args)); | |
WriteInternal(str); | |
} | |
} | |
public static void WriteLine(string logcat, LogLevel level) | |
{ | |
if (IsAllowed(logcat, level)) | |
{ | |
WriteInternal(""); | |
} | |
} | |
public static void Write(object anObject) | |
{ | |
if (anObject == null) | |
{ | |
anObject = "<null>"; | |
} | |
string str = anObject.ToString(); | |
WriteInternal(str); | |
} | |
public static void WriteFormat(string format, params object[] args) | |
{ | |
WriteInternal(string.Format(format, args)); | |
} | |
public static void WriteLine() | |
{ | |
WriteInternal(""); | |
} | |
private static void WriteInternal(string str) | |
{ | |
if (Program.Current != null) | |
{ | |
WrappedEcho(str); | |
var dServer = Program.Current.GridTerminalSystem.GetBlockWithName("DebugSrv") as IMyProgrammableBlock; | |
if (dServer != null) | |
{ | |
if (insertNewFrameSeparator) | |
{ | |
insertNewFrameSeparator = false; | |
dServer.TryRun("L" + NewFrameSeparator); | |
} | |
dServer.TryRun("L" + str); | |
} | |
} | |
else | |
{ | |
throw new InvalidOperationException("Program.Current is not assigned"); | |
} | |
} | |
private static void WrappedEcho(string str) | |
{ | |
foreach (var line in strUtils.Wrap(str, 615)) | |
{ | |
Program.Current.Echo(line); | |
} | |
} | |
internal static void NewFrame() | |
{ | |
insertNewFrameSeparator = true; | |
} | |
} | |
// ---------- | |
// SEStrUtils.cs | |
public enum WordWrapOptions | |
{ | |
None = 0, | |
Overflow = 1, | |
SplitWords = 2, | |
NewLineExtra = 4, | |
Default = None | |
} | |
public interface IFontDataProvider | |
{ | |
int Width(char ch); | |
int Width(string str, char lead = '\0'); | |
int Height(); | |
int LetterSpacing(char left, char right); | |
} | |
public class StringUtils | |
{ | |
public StringUtils(IFontDataProvider dataProvider) | |
{ | |
this.dataProvider = dataProvider; | |
} | |
IFontDataProvider dataProvider = null; | |
public List<string> Wrap(string str, int width, WordWrapOptions options = WordWrapOptions.Default) | |
{ | |
int end; | |
return Wrap(str, x => width, 0, out end, options); | |
} | |
public List<string> Wrap(string str, Func<int, int> width, WordWrapOptions options = WordWrapOptions.Default) | |
{ | |
int end; | |
return Wrap(str, width, 0, out end, options); | |
} | |
public List<string> Wrap(string str, Func<int, int> width, int start, out int end, WordWrapOptions options = WordWrapOptions.Default) | |
{ | |
List<string> lines = new List<string>(); | |
bool allowClipping = (options | WordWrapOptions.Overflow) == options; | |
bool splitWords = (options | WordWrapOptions.SplitWords) == options; | |
bool newLineExtra = (options | WordWrapOptions.NewLineExtra) == options; | |
StringBuilder line = new StringBuilder(); | |
StringBuilder candidate = new StringBuilder(); | |
int lWidth = width(lines.Count); | |
if (lWidth == -1) | |
{ | |
end = start; | |
return lines; | |
} | |
int cWidth = 0; | |
char left = '\0'; | |
char[] chars = str.ToCharArray(); | |
int i = start; | |
int imax = chars.Length; | |
for (; i < imax; i++) | |
{ | |
char ch = chars[i]; | |
int delta = dataProvider.LetterSpacing(left, ch) + dataProvider.Width(ch); | |
if (ch == '\n') | |
{ | |
line.Append(candidate); | |
lines.Add(line.ToString()); | |
if (newLineExtra && line.Length > 0) | |
{ | |
lines.Add(""); | |
} | |
candidate.Clear(); | |
cWidth = 0; | |
line.Clear(); | |
lWidth = width(lines.Count); | |
if (lWidth == -1) | |
{ | |
goto loop_break; | |
} | |
} | |
else if (ch == '\r') | |
{ | |
} | |
else if (splitWords || !char.IsWhiteSpace(ch)) | |
{ | |
if (cWidth + delta > lWidth) | |
{ | |
if (lWidth < width(lines.Count)) | |
{ | |
lines.Add(line.ToString()); | |
line.Clear(); | |
lWidth = width(lines.Count); | |
if (lWidth == -1) | |
{ | |
goto loop_break; | |
} | |
} | |
if ( | |
(cWidth + delta > width(lines.Count)) && | |
!(allowClipping && candidate.Length == 0) | |
) | |
{ | |
if (candidate.Length == 0) | |
{ | |
goto loop_break; | |
} | |
line.Append(candidate); | |
lines.Add(line.ToString()); | |
line.Clear(); | |
lWidth = width(lines.Count); | |
if (lWidth == -1) | |
{ | |
goto loop_break; | |
} | |
candidate.Clear(); | |
candidate.Append(ch); | |
cWidth = delta; | |
} | |
else | |
{ | |
cWidth += delta; | |
candidate.Append(ch); | |
} | |
} | |
else | |
{ | |
cWidth += delta; | |
candidate.Append(ch); | |
} | |
} | |
else | |
{ | |
line.Append(candidate); | |
lWidth -= cWidth; | |
candidate.Clear(); | |
cWidth = 0; | |
if (lWidth <= delta) | |
{ | |
lines.Add(line.ToString()); | |
line.Clear(); | |
lWidth = width(lines.Count); | |
if (lWidth == -1) | |
{ | |
goto loop_break; | |
} | |
} | |
else | |
{ | |
lWidth -= delta; | |
line.Append(ch); | |
} | |
} | |
} | |
if (candidate.Length > 0) | |
{ | |
line.Append(candidate); | |
} | |
if (line.Length > 0 || lines.Count == 0) | |
{ | |
lines.Add(line.ToString()); | |
} | |
loop_break: | |
end = i; | |
return lines; | |
} | |
} | |
class DebugFontDataProvider : IFontDataProvider | |
{ | |
static string cRef = | |
"'|¦ˉ‘’‚\nј\n !I`ijl ¡¨¯´¸ÌÍÎÏìíîïĨĩĪīĮįİıĵĺļľłˆˇ˘˙˚˛˜˝ІЇії‹›∙\n(),.1:;[]ft{" + | |
"}·ţťŧț\n\"-rªºŀŕŗř\n*²³¹\n\\°“”„\nґ\n/ijтэє\nL_vx«»ĹĻĽĿŁГгзлхчҐ–•\n7?Jcz¢¿çć" + | |
"ĉċčĴźżžЃЈЧавийнопсъьѓѕќ\n3FKTabdeghknopqsuy£µÝàáâãäåèéêëðñòóôõöøùúûüýþÿāăąď" + | |
"đēĕėęěĝğġģĥħĶķńņňʼnōŏőśŝşšŢŤŦũūŭůűųŶŷŸșȚЎЗКЛбдекруцяёђћўџ\n+<=>E^~¬±¶ÈÉÊË×÷Ē" + | |
"ĔĖĘĚЄЏЕНЭ−\n#0245689CXZ¤¥ÇßĆĈĊČŹŻŽƒЁЌАБВДИЙПРСТУХЬ€\n$&GHPUVY§ÙÚÛÜÞĀĜĞĠĢĤĦŨ" + | |
"ŪŬŮŰŲОФЦЪЯжы†‡\nABDNOQRSÀÁÂÃÄÅÐÑÒÓÔÕÖØĂĄĎĐŃŅŇŌŎŐŔŖŘŚŜŞŠȘЅЊЖф□\nљ\nю\n%IJЫ\n@" + | |
"©®мшњ\nMМШ\nmw¼ŵЮщ\n¾æœЉ\n½Щ\n™\nWÆŒŴ—…‰\n\n\n\n\n\n\n\n\n" + | |
"\n"; | |
static string kRef = | |
"\nЖв?ж?\nъв>\nьв>\nҐ,?-?.?‚?„?…?–?—?š>œ>à>á>â>ã>ä>å>æ>ç>è>é>ê>ë>ð>ñ?ò>ó>ô>õ" + | |
">ö>ø>ù?ú?û?ü?ž?ā>ă>ą>ć>ĉ>č>ď>đ>ě>ē>ĕ>ę>ĝ>ğ>ń?ň?ō>ŏ>ŕ?ř?ś>ŝ>ş>ș>ũ?ū?ŭ?ů?ų?ŵ?" + | |
"ź?Ц>Ш>Ы?Ю?б>в?ж?н>"; | |
static Dictionary<char, int> cW; | |
static Dictionary<char, Dictionary<char, int>> kP; | |
public DebugFontDataProvider() | |
{ | |
cW = new Dictionary<char, int>(); | |
int w = 6; | |
foreach (char c in cRef) | |
{ | |
if (c == '\n') | |
w++; | |
else | |
cW[c] = w; | |
} | |
kP = new Dictionary<char, Dictionary<char, int>>(); | |
Dictionary<char, int> cur = null; | |
for (var e = ((IList<char>)kRef.ToCharArray()).GetEnumerator(); e.MoveNext();) | |
{ | |
char c1 = e.Current; | |
e.MoveNext(); | |
char c2 = e.Current; | |
if (c1 == '\n') | |
kP[c2] = cur = new Dictionary<char, int>(); | |
else | |
cur[c1] = c2 - 64; | |
} | |
} | |
public int Width(char ch) | |
{ | |
int w; | |
cW.TryGetValue(ch, out w); | |
return w; | |
} | |
public int Width(string str, char lead = '\0') | |
{ | |
char lc = lead; | |
int wd = 0, w; | |
foreach (var c in str) | |
{ | |
cW.TryGetValue(c, out w); | |
wd += w + 1; | |
if (kP.ContainsKey(lc)) | |
{ | |
kP[lc].TryGetValue(c, out w); | |
wd += w; | |
} | |
lc = c; | |
} | |
return wd; | |
} | |
public int LetterSpacing(char left, char right) | |
{ | |
if (kP.ContainsKey(left)) | |
{ | |
int w; | |
kP[left].TryGetValue(right, out w); | |
return w + 1; | |
} | |
return 1; | |
} | |
int SpacingOrZero(char left, char right) | |
{ | |
if (left == '\0' || right == '\0') | |
{ | |
return 0; | |
} | |
else | |
{ | |
return LetterSpacing(left, right); | |
} | |
} | |
public int Height() | |
{ | |
return 37; | |
} | |
} | |
class MonospacedFontDataProvider : IFontDataProvider | |
{ | |
const int charWidth = 24; | |
const int lineHeight = 37; | |
const int letterSpacing = 1; | |
public int LetterSpacing(char left, char right) | |
{ | |
return letterSpacing; | |
} | |
public int Width(char ch) | |
{ | |
return charWidth; | |
} | |
public int Width(string str, char lead = '\0') | |
{ | |
return (charWidth + letterSpacing) * str.Length; | |
} | |
public int Height() | |
{ | |
return lineHeight; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment