/// <summary> /// Builds an Answer String from the Specified FsdStatus Object /// </summary> /// <param name="fsdStatus"></param> /// <returns></returns> public ReadOnlySpan <byte> BuildAnswerString(FsdStatus fsdStatus) { using var result = new MemoryStream(fsdStatus.Fields.Sum(field => field.Name.Length + field.Value.Length + 4)); foreach (var field in fsdStatus.Fields) { result.Write(Encoding.ASCII.GetBytes($"{field.Name}={field.Value}\0")); } result.WriteByte(0); return(result.ToArray()); }
/// <summary> /// Parses through the template looking for leading ANSI formatting on field specifications /// </summary> /// <param name="template"></param> /// <param name="status"></param> public void GetFieldAnsi(ReadOnlySpan <byte> template, FsdStatus status) { var currentField = 0; var foundFieldCharacter = 0xFF; for (var i = 0; i < template.Length; i++) { var c = template[i]; //If it's the character we previously found, keep going if (c == foundFieldCharacter) { continue; } //We're past the field now, reset the character foundFieldCharacter = '\xFF'; //If it's a new line, increment Y and set X back to -1 if (c == '\r') { continue; } //If we're not in a field definition, keep moving if (c != '?' && c != '$' && c != '!') { continue; } //Found a field definition character, check to see if the next character is the same if (template[i + 1] != c) { continue; } //Assume the ANSI format specification is within the first 10 characters var extractedAnsi = ExtractFieldAnsi(template.Slice(i - 10, 10)); //Error Fields are their own special little snowflakes if (c == '!') { status.ErrorField.FieldAnsi = extractedAnsi; } else { status.Fields[currentField].FieldAnsi = extractedAnsi; currentField++; } //Set our Position foundFieldCharacter = c; } }
/// <summary> /// Determines the X,Y coordinates of specified template fields by stripping ANSI from the field template /// and parsing each character, each new line increments Y and sets X back to 1. /// /// Note: X,Y for ANSI starts at 1,1 being the top left of the terminal (not 0,0) /// </summary> /// <param name="template"></param> /// <param name="status"></param> public void GetFieldPositions(ReadOnlySpan <byte> template, FsdStatus status) { //Get Template and Strip out ANSI Characters var templateString = Encoding.ASCII.GetString(StripAnsi(template)); var currentY = 1; //ANSI x/y positions are 1 based, so first loop these will be 1,1 var currentX = 0; var currentField = 0; var currentFieldLength = 0; var foundFieldCharacter = '\xFF'; for (var i = 0; i < templateString.Length; i++) { var c = templateString[i]; //Increment the X Position currentX++; //If it's the character we previously found, keep going if (c == foundFieldCharacter) { currentFieldLength++; continue; } //If we're past it, and the previous field Character wasn't the default, mark the length if (foundFieldCharacter != '\xFF') { //Special Case for Error Field if (foundFieldCharacter == '!') { status.ErrorField.FieldLength = currentFieldLength + 1; } else { //Standard Fields status.Fields[currentField - 1].FieldLength = currentFieldLength + 1; } currentFieldLength = 0; } //We're past the field now, reset the character foundFieldCharacter = '\xFF'; //If it's a new line, increment Y and set X back to -1 if (c == '\n') { currentY++; currentX = 0; continue; } if (c != '?' && c != '$' && c != '!') { continue; } //If the next character is a known control character, then we're in a field definition switch (templateString[i + 1]) { case '?': case '$': case '!': break; default: //Otherwise keep searching continue; } //Define the field based on the field specification character used switch (c) { case '!': //Error Message Field //Add a new field for the Error, as it's not included in the Field Spec status.ErrorField = new FsdFieldSpec { FsdFieldType = EnumFsdFieldType.Error, X = currentX, Y = currentY }; foundFieldCharacter = c; continue; case '$': //Numeric Field status.Fields[currentField].FsdFieldType = EnumFsdFieldType.Numeric; status.Fields[currentField].X = currentX; status.Fields[currentField].Y = currentY; break; default: //Text or Multiple Choice status.Fields[currentField].X = currentX; status.Fields[currentField].Y = currentY; break; } currentField++; foundFieldCharacter = c; } }