/// <summary> /// Reads bytes until a unescaped closing parenthesis in found. Unescapes escaped parenthesis. /// Leaves reader on the char following the closing parenthesis. /// </summary> /// <param name="r"></param> /// <returns></returns> public static byte[] parseBinary(PsdBinaryReader r) { MemoryStream ms = new MemoryStream(128); //For writing the decoded binary byte b = 0; //Read bytes, keeping a two-byte shifting buffer so we can decode escaped chars. while (r.CanReadByte()) { b = r.ReadByte(); //Escaped parenthesis, skip slash if ((char)b == '\\') { b = r.ReadByte(); ms.WriteByte(b); } else { //We hit the end, an unescaped closng parenthesis. if ((char)b == ')') { return(ms.ToArray()); } ms.WriteByte(b); } } throw new TdTaParseException("Hit end of stream without finding closing parenthesis!"); }
/// <summary> /// Reads bytes until a unescaped closing parenthesis in found. Unescapes escaped parenthesis. /// Leaves reader on the char following the closing parenthesis. /// </summary> /// <param name="r"></param> /// <returns></returns> public static byte[] parseBinary(PsdBinaryReader r) { MemoryStream ms = new MemoryStream(128); //For writing the decoded binary byte b = 0; byte lastb = 0; bool hasLastByte = false; //Read bytes, keeping a two-byte shifting buffer so we can decode escaped chars. while (r.CanReadByte()) { b = r.ReadByte(); //Look for closing parenthesis if ((char)b == ')') { if (hasLastByte && (char)lastb == '\\') { //Escaped parenthesis, skip slash lastb = 0; hasLastByte = false; } else { //Unescaped closing parenthesis! We hit the end! if (hasLastByte) { ms.WriteByte(lastb); //If we still have a byte in the buffer. } return(ms.ToArray()); //We hit the end, an unescaped closng parenthesis. } } //Far as I know, nothing else is escaped. //Write lastb if present. if (hasLastByte) { ms.WriteByte(lastb); } //Shift buffer lastb = b; hasLastByte = true; } throw new TdTaParseException("Hit end of stream without finding closing parenthesis!"); }
/// <summary> /// Parses tdta contents into tokens. Throws a generic exception if unrecognized tokens are encountered /// </summary> /// <param name="r"></param> /// <returns></returns> public static Token nextToken(PsdBinaryReader r) { if (!r.CanReadByte()) { return(null); } char c = r.ReadAsciiChars(1)[0]; switch (c) { case '(': return(new Token(parseBinary(r))); //Binary data. Leading and trailing parens are stripped, and escaped parens are restored to normal. case '[': return(new Token(TokenType.StartList, c.ToString())); // [ opening list char case ']': return(new Token(TokenType.EndList, c.ToString())); //Closing list char } //<< opening dict //>> closing dict if (c == '<' || c == '>') { char c2 = r.ReadAsciiChars(1)[0]; if (c2 != c) { throw new TdTaParseException("Unrecognized token: " + c + c2, r); } if (c == '<') { return(new Token(TokenType.StartDict, "<<")); } else { return(new Token(TokenType.EndDict, ">>")); } } //Dict key (Any non-whitespace, non-[]<> sequence allowed following forward slash) if (c == '/') { StringBuilder sb = new StringBuilder(40); while (r.CanReadByte()) { //Peek at the next char to see if it is whitespace char pc = (char)r.PeekChar(); if (Char.IsWhiteSpace(pc) || pc == '\n' || pc == '[' || pc == ']' || pc == '<' || pc == '>') { //We're done. //Strip the leading slash, create token with name return(new Token(TokenType.MapKey, sb.ToString())); } else { //A valid char, append it. sb.Append(r.ReadChar()); } } } //Integer or double. Ends at whitespace, newline, ], or > if (Char.IsDigit(c) || c == '-' || c == '.') { StringBuilder sb = new StringBuilder(40); sb.Append(c); while (r.CanReadByte()) { //Peek at the next char to see if it is whitespace char pc = (char)r.PeekChar(); if (Char.IsWhiteSpace(pc) || pc == '\n' || pc == ']' || pc == '>') { //We're done. Find out what kind of number it is. string s = sb.ToString(); //If it has a decimal, it's a double if (s.Contains('.')) { return(new Token(TokenType.Double, double.Parse(s, floatingPointStyle, NumberFormatInfo.InvariantInfo))); } else { return(new Token(TokenType.Integer, int.Parse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo))); } } else if (Char.IsDigit(c) || c == '-' || c == '.') { //A valid char, append it. sb.Append(r.ReadChar()); } else { //Unrecognized char in numeric sq throw new TdTaParseException("Unrecognized character in numeric sequence: " + sb.ToString() + pc, r); } } } //Boolean (true|false) parsing if (c == 't' || c == 'T' || c == 'f' || c == 'F') { string s = c.ToString() + r.ReadChar().ToString() + r.ReadChar().ToString() + r.ReadChar().ToString(); char pc = (char)r.PeekChar(); if (s.Equals("true", StringComparison.OrdinalIgnoreCase)) { return(new Token(TokenType.Boolean, true)); } else if (s.Equals("fals", StringComparison.OrdinalIgnoreCase) && (pc == 'e' || pc == 'E')) { r.ReadChar(); //discard 'e' return(new Token(TokenType.Boolean, false)); } else { throw new TdTaParseException("Unrecognized boolean value: " + s + pc, r); } } //Whitespace parsing if (Char.IsWhiteSpace(c) || c == '\n') { StringBuilder sb = new StringBuilder(40); sb.Append(c); while (r.CanReadByte()) { //Peek at the next char to see if it is whitespace char pc = (char)r.PeekChar(); if (Char.IsWhiteSpace(pc) || pc == '\n') { sb.Append(r.ReadChar()); } else { //We done. break; } } return(new Token(TokenType.Whitespace, sb.ToString())); } throw new TdTaParseException("Unrecognized token: " + c, r); }