using System; using System.Text; namespace LibAC.NBT.API { public class StringReader { private readonly string _buffer; private int _position = 0; private int _lastPosition = 0; private bool _quotedString = false; public StringReader(string buffer) { _buffer = buffer; } // Check if there's more to read public bool CanRead => _CanRead(); private bool _CanRead() { SkipWhitespace(); return _position < _buffer.Length; } // Get the number of chars seeked public int Seeked => _lastPosition - _position; // Read the next character public char Next() { if (CanRead) { SkipWhitespace(); return _buffer[_position++]; } else { throw new Exception("End of buffer reached"); } } // Generates a snapshot of the text location if applicable public string GetSnapshot() { if (CanRead) { if (_position + 64 < _buffer.Length) { return _buffer.Substring(_position, 64); } else { return _buffer.Substring(_position); } } else { return string.Empty; } } // Peek the next character without advancing the position public char Peek() { SkipWhitespace(); if (CanRead) { return _buffer[_position]; } else { throw new Exception("End of buffer reached"); } } // Skip any whitespace characters public void SkipWhitespace() { if (_quotedString) return; // We need whitespace for strings while (_position < _buffer.Length && IsWhitespace(_buffer[_position])) { _position++; } } // Check if a character is a whitespace private bool IsWhitespace(char character) { return char.IsWhiteSpace(character); } // Read until a specific character is found public string ReadUntil(char stopChar, int maxChars) { var result = new StringBuilder(); int index = 0; while (CanRead && Peek() != stopChar && index < maxChars) { result.Append(Next().ToString()); index++; } return result.ToString(); } // Read a string enclosed in double quotes public string ReadQuotedString() { _quotedString = true; if (Next() != '"') { throw new Exception("Expected double quotes at the start of a string"); } var result = new StringBuilder(); while (CanRead) { char character = Next(); if (character == '"') { break; } result.Append(character.ToString()); } _quotedString = false; return result.ToString(); } // Read a number (int or double) public string ReadNumber() { var result = new StringBuilder(); while (CanRead && (IsDigit(Peek()) || Peek() == '.' || Peek() == '-')) { result.Append(Next().ToString()); } return result.ToString(); } // Check if a character is a digit private bool IsDigit(char character) { return char.IsDigit(character); } // Read an unquoted string (used for keys in SNBT) public string ReadUnquotedString() { var result = new StringBuilder(); while (CanRead && !IsWhitespace(Peek()) && Peek() != ':' && Peek() != ',' && Peek() != '{' && Peek() != '}' && Peek() != '[' && Peek() != ']') { result.Append(Next().ToString()); } return result.ToString(); } public string ReadString() { if (Peek() == '"') { return ReadQuotedString(); } else { return ReadUnquotedString(); } } // Read a specific character and throw an exception if it's not found public void Expect(char expectedChar) { if (char.ToLower(Next()) != char.ToLower(expectedChar)) { throw new Exception($"Expected {expectedChar}"); } } public void StartSeek() { _lastPosition = _position; } public void EndSeek() { _position = _lastPosition; _lastPosition = 0; } } }