/// <summary> /// Deserializes the given frame given a known width and height. /// </summary> /// <param name="serializedFrame">the frame data</param> /// <param name="width">the known width of the frame</param> /// <param name="height">the known height of the frame</param> /// <returns>a deserialized frame that's either a raw frame or a diff frame, depending on what was in the serialized string</returns> public ConsoleBitmapFrame DeserializeFrame(string serializedFrame, int width, int height) { var tokens = tokenizer.Tokenize(serializedFrame); var reader = new TokenReader<Token>(tokens); reader.Expect("["); var timestampToken = reader.Advance(); var timestamp = new TimeSpan(long.Parse(timestampToken.Value)); reader.Expect("]"); reader.Expect("["); reader.Advance(); var isDiff = reader.Current.Value == "Diff"; reader.Expect("]"); if (isDiff) { var diffFrame = new ConsoleBitmapDiffFrame() { Timestamp = timestamp, Diffs = new System.Collections.Generic.List<ConsoleBitmapPixelDiff>() }; var lastBackground = ConsoleString.DefaultBackgroundColor; var lastForeground = ConsoleString.DefaultForegroundColor; while (reader.CanAdvance(skipWhitespace: true)) { reader.Expect("[", skipWhiteSpace: true); if (reader.Peek().Value.StartsWith("F=") || reader.Peek().Value.StartsWith("B=")) { reader.Advance(); var match = ColorSpecifierRegex.Match(reader.Current.Value); if (match.Success == false) throw new FormatException($"Unexpected token {reader.Current.Value} at position {reader.Current.Position} "); var isForeground = match.Groups["ForB"].Value == "F"; if (isForeground) { if (Enum.TryParse(match.Groups["color"].Value, out ConsoleColor c)) { lastForeground = (RGB)c; } else if (RGB.TryParse(match.Groups["color"].Value, out lastForeground) == false) { throw new ArgumentException($"Expected a color @ {reader.Position}"); } } else { if (Enum.TryParse(match.Groups["color"].Value, out ConsoleColor c)) { lastBackground = (RGB)c; } else if (RGB.TryParse(match.Groups["color"].Value, out lastBackground) == false) { throw new ArgumentException($"Expected a color @ {reader.Position}"); } } reader.Expect("]"); } else { var match = PixelDiffRegex.Match(reader.Advance().Value); if (match.Success == false) throw new FormatException("Could not parse pixel diff"); var valGroup = match.Groups["val"].Value; char? nextChar = valGroup.Length == 1 ? valGroup[0] : valGroup == "OB" ? '[' : valGroup == "CB" ? ']' : new char?(); if (nextChar.HasValue == false) throw new FormatException($"Unexpected token {nextChar} @ {reader.Position}"); diffFrame.Diffs.Add(new ConsoleBitmapPixelDiff() { X = int.Parse(match.Groups["x"].Value), Y = int.Parse(match.Groups["y"].Value), Value = new ConsoleCharacter(nextChar.Value, lastForeground, lastBackground), }); reader.Expect("]"); } } return diffFrame; } else { var rawFrame = new ConsoleBitmapRawFrame() { Timestamp = timestamp, Pixels = new ConsoleCharacter[width][] }; for (var i = 0; i < width; i++) { rawFrame.Pixels[i] = new ConsoleCharacter[height]; } var x = 0; var y = 0; var lastFg = ConsoleString.DefaultForegroundColor; var lastBg = ConsoleString.DefaultBackgroundColor; while (reader.CanAdvance(skipWhitespace:true)) { reader.Expect("[", skipWhiteSpace:true); var next = reader.Advance(); var match = ColorSpecifierRegex.Match(next.Value); if (match.Success) { var isForeground = match.Groups["ForB"].Value == "F"; if (isForeground) { if(Enum.TryParse<ConsoleColor>(match.Groups["color"].Value, out ConsoleColor c)) { lastFg = c; } else if(RGB.TryParse(match.Groups["color"].Value, out lastFg) == false) { throw new ArgumentException($"Expected a color @ {reader.Position}"); } } else { if (Enum.TryParse<ConsoleColor>(match.Groups["color"].Value, out ConsoleColor c)) { lastBg = c; } else if (RGB.TryParse(match.Groups["color"].Value, out lastBg) == false) { throw new ArgumentException($"Expected a color @ {reader.Position}"); } } } else { char? nextChar = next.Value.Length == 1 ? next.Value[0] : next.Value == "OB" ? '[' : next.Value == "CB" ? ']' : new char?(); if (nextChar.HasValue == false) throw new FormatException($"Unexpected token {nextChar} @ {next.Position}"); rawFrame.Pixels[x][y++] = new ConsoleCharacter(nextChar.Value, lastFg, lastBg); if (y == height) { y = 0; x++; } } reader.Expect("]"); } return rawFrame; } }