1use std::{
2 collections::HashMap,
3 error::Error,
4 fmt,
5 io::{self, Read, Write},
6};
7
8use fastnbt::{
9 SerOpts, Tag,
10 stream::{Parser, Value as StreamValue},
11};
12use mcproto_codec::{
13 error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
14 io::write_all_counted,
15};
16
17use crate::{TypeCodec, component::NbtComponent};
18
19pub use fastnbt::Value as NbtValue;
20
21#[derive(Debug, Clone, PartialEq)]
23pub struct TextComponent(pub NbtComponent);
24
25impl TextComponent {
26 const MAX_COMPONENT_DEPTH: usize = 512;
27
28 pub fn text(value: impl Into<String>) -> Self {
29 Self(NbtComponent::text(value))
30 }
31}
32
33impl Default for TextComponent {
34 fn default() -> Self {
35 Self::text("")
36 }
37}
38
39impl From<NbtComponent> for TextComponent {
40 fn from(value: NbtComponent) -> Self {
41 Self(value)
42 }
43}
44
45impl From<String> for TextComponent {
46 fn from(value: String) -> Self {
47 Self::text(value)
48 }
49}
50
51impl From<&str> for TextComponent {
52 fn from(value: &str) -> Self {
53 Self::text(value)
54 }
55}
56
57impl TypeCodec for TextComponent {
58 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
59 self.0
60 .validate_depth(Self::MAX_COMPONENT_DEPTH)
61 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
62 let normalized = self.0.normalized_root_for_nbt();
63 let value = fastnbt::to_value(&normalized)
64 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
65 validate_nbt_strings(&value, CodecOperation::Write, 0)?;
66 encode_root(&value, writer)
67 }
68
69 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
70 let (value, bytes_processed) = decode_root(reader)?;
71 let component: NbtComponent = fastnbt::from_value(&value)
72 .map_err(|source| invalid_nbt(CodecOperation::Read, bytes_processed, source))?;
73 component
74 .validate_depth(Self::MAX_COMPONENT_DEPTH)
75 .map_err(|source| invalid_nbt(CodecOperation::Read, bytes_processed, source))?;
76 Ok(Self(component.normalized_root_for_nbt()))
77 }
78}
79
80fn encode_root(value: &NbtValue, writer: &mut impl Write) -> Result<(), CodecError> {
81 let wrapper = HashMap::from([("", value)]);
82 let encoded = fastnbt::to_bytes_with_opts(&wrapper, SerOpts::network_nbt())
83 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
84
85 if encoded.len() < 5
86 || encoded[0] != Tag::Compound as u8
87 || encoded[2..4] != [0, 0]
88 || encoded.last() != Some(&(Tag::End as u8))
89 || !matches!(encoded[1], 8 | 10)
90 {
91 return Err(CodecError::invalid_encoding_for_operation(
92 CodecKind::TextComponent,
93 CodecOperation::Write,
94 0,
95 InvalidEncodingReason::InvalidNbt,
96 ));
97 }
98
99 write_all_counted(writer, &encoded[1..2], CodecKind::TextComponent, 0)?;
100 write_all_counted(
101 writer,
102 &encoded[4..encoded.len() - 1],
103 CodecKind::TextComponent,
104 1,
105 )
106}
107
108fn decode_root(reader: &mut impl Read) -> Result<(NbtValue, usize), CodecError> {
109 let mut reader = CountedReader::new(reader);
110 let mut root_tag = [0_u8; 1];
111 reader.read_exact(&mut root_tag).map_err(|source| {
112 CodecError::from_read_error(CodecKind::TextComponent, reader.processed, source)
113 })?;
114 if !matches!(root_tag[0], 8 | 10) {
115 return Err(CodecError::invalid_encoding(
116 CodecKind::TextComponent,
117 reader.processed,
118 InvalidEncodingReason::InvalidTextComponentRootTag { tag: root_tag[0] },
119 ));
120 }
121
122 let mut prefixed = PrefixReader {
124 prefix: &[root_tag[0], 0, 0],
125 inner: &mut reader,
126 };
127 let result = (|| {
128 let mut parser = Parser::new(&mut prefixed);
129 let first = parser.next().map_err(NbtTreeError::Parser)?;
130 parse_stream_value(&mut parser, first, 1)
131 })();
132
133 match result {
134 Ok(value) => Ok((value, prefixed.inner.processed)),
135 Err(error) => {
136 let processed = prefixed.inner.processed;
137 match prefixed.inner.failure.take() {
138 Some(source) => Err(CodecError::from_read_error(
139 CodecKind::TextComponent,
140 processed,
141 source,
142 )),
143 None => Err(invalid_nbt(CodecOperation::Read, processed, error)),
144 }
145 }
146 }
147}
148
149fn parse_stream_value<R: Read>(
150 parser: &mut Parser<R>,
151 value: StreamValue,
152 depth: usize,
153) -> Result<NbtValue, NbtTreeError> {
154 if depth > TextComponent::MAX_COMPONENT_DEPTH {
155 return Err(NbtTreeError::TooDeep);
156 }
157 match value {
158 StreamValue::Byte(_, value) => Ok(NbtValue::Byte(value)),
159 StreamValue::Short(_, value) => Ok(NbtValue::Short(value)),
160 StreamValue::Int(_, value) => Ok(NbtValue::Int(value)),
161 StreamValue::Long(_, value) => Ok(NbtValue::Long(value)),
162 StreamValue::Float(_, value) => Ok(NbtValue::Float(value)),
163 StreamValue::Double(_, value) => Ok(NbtValue::Double(value)),
164 StreamValue::ByteArray(_, value) => Ok(NbtValue::ByteArray(fastnbt::ByteArray::new(value))),
165 StreamValue::String(_, value) => Ok(NbtValue::String(value)),
166 StreamValue::IntArray(_, value) => Ok(NbtValue::IntArray(fastnbt::IntArray::new(value))),
167 StreamValue::LongArray(_, value) => Ok(NbtValue::LongArray(fastnbt::LongArray::new(value))),
168 StreamValue::List(_, _, length) => {
169 let length = usize::try_from(length).map_err(|_| NbtTreeError::InvalidStructure)?;
170 let mut values = Vec::with_capacity(length.min(1024));
172 for _ in 0..length {
173 let value = parser.next().map_err(NbtTreeError::Parser)?;
174 values.push(parse_stream_value(parser, value, depth + 1)?);
175 }
176 if !matches!(parser.next(), Ok(StreamValue::ListEnd)) {
177 return Err(NbtTreeError::InvalidStructure);
178 }
179 Ok(NbtValue::List(values))
180 }
181 StreamValue::Compound(_) => {
182 let mut values = HashMap::new();
183 loop {
184 let value = parser.next().map_err(NbtTreeError::Parser)?;
185 if matches!(value, StreamValue::CompoundEnd) {
186 break;
187 }
188 let name = stream_name(&value).ok_or(NbtTreeError::InvalidStructure)?;
189 values.insert(
190 name.to_owned(),
191 parse_stream_value(parser, value, depth + 1)?,
192 );
193 }
194 Ok(NbtValue::Compound(values))
195 }
196 StreamValue::ListEnd | StreamValue::CompoundEnd => Err(NbtTreeError::InvalidStructure),
197 }
198}
199
200fn stream_name(value: &StreamValue) -> Option<&str> {
201 match value {
202 StreamValue::Byte(name, _)
203 | StreamValue::Short(name, _)
204 | StreamValue::Int(name, _)
205 | StreamValue::Long(name, _)
206 | StreamValue::Float(name, _)
207 | StreamValue::Double(name, _)
208 | StreamValue::ByteArray(name, _)
209 | StreamValue::String(name, _)
210 | StreamValue::List(name, _, _)
211 | StreamValue::Compound(name)
212 | StreamValue::IntArray(name, _)
213 | StreamValue::LongArray(name, _) => name.as_deref(),
214 StreamValue::ListEnd | StreamValue::CompoundEnd => None,
215 }
216}
217
218#[derive(Debug)]
219enum NbtTreeError {
220 Parser(fastnbt::stream::Error),
221 InvalidStructure,
222 TooDeep,
223}
224
225impl fmt::Display for NbtTreeError {
226 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
227 match self {
228 Self::Parser(source) => source.fmt(formatter),
229 Self::InvalidStructure => formatter.write_str("invalid NBT tree structure"),
230 Self::TooDeep => formatter.write_str("NBT tree is nested too deeply"),
231 }
232 }
233}
234
235impl Error for NbtTreeError {
236 fn source(&self) -> Option<&(dyn Error + 'static)> {
237 match self {
238 Self::Parser(source) => Some(source),
239 _ => None,
240 }
241 }
242}
243
244fn invalid_nbt(
245 operation: CodecOperation,
246 bytes_processed: usize,
247 source: impl Error + Send + Sync + 'static,
248) -> CodecError {
249 CodecError::invalid_encoding_for_operation_with_source(
250 CodecKind::TextComponent,
251 operation,
252 bytes_processed,
253 InvalidEncodingReason::InvalidNbt,
254 source,
255 )
256}
257
258fn validate_nbt_strings(
259 root: &NbtValue,
260 operation: CodecOperation,
261 bytes_processed: usize,
262) -> Result<(), CodecError> {
263 let mut pending = vec![root];
264 while let Some(value) = pending.pop() {
265 match value {
266 NbtValue::String(value) => validate_nbt_string(value, operation, bytes_processed)?,
267 NbtValue::List(values) => pending.extend(values),
268 NbtValue::Compound(values) => {
269 for (name, value) in values {
270 validate_nbt_string(name, operation, bytes_processed)?;
271 pending.push(value);
272 }
273 }
274 _ => {}
275 }
276 }
277 Ok(())
278}
279
280fn validate_nbt_string(
281 value: &str,
282 operation: CodecOperation,
283 bytes_processed: usize,
284) -> Result<(), CodecError> {
285 let encoded_len = value.chars().try_fold(0_usize, |length, character| {
286 let width = match character as u32 {
287 0 => 2,
288 1..=0x7f => 1,
289 0x80..=0x7ff => 2,
290 0x800..=0xffff => 3,
291 _ => 6,
292 };
293 length.checked_add(width)
294 });
295 if !matches!(encoded_len, Some(length) if length <= u16::MAX as usize) {
296 return Err(CodecError::invalid_encoding_for_operation(
297 CodecKind::TextComponent,
298 operation,
299 bytes_processed,
300 InvalidEncodingReason::StringTooLong {
301 max_bytes: u16::MAX as usize,
302 },
303 ));
304 }
305 Ok(())
306}
307
308struct CountedReader<'a, R: ?Sized> {
309 inner: &'a mut R,
310 processed: usize,
311 failure: Option<io::Error>,
312}
313
314impl<'a, R: ?Sized> CountedReader<'a, R> {
315 fn new(inner: &'a mut R) -> Self {
316 Self {
317 inner,
318 processed: 0,
319 failure: None,
320 }
321 }
322}
323
324impl<R: Read + ?Sized> Read for CountedReader<'_, R> {
325 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
326 if buffer.is_empty() {
327 return Ok(0);
328 }
329 match self.inner.read(buffer) {
330 Ok(0) => {
331 let error = io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected end of NBT");
332 self.failure = Some(error);
333 Err(io::Error::new(
334 io::ErrorKind::UnexpectedEof,
335 "unexpected end of NBT",
336 ))
337 }
338 Ok(read) => {
339 self.processed += read;
340 Ok(read)
341 }
342 Err(error) => {
343 let returned = clone_io_error(&error);
344 self.failure = Some(error);
345 Err(returned)
346 }
347 }
348 }
349}
350
351struct PrefixReader<'a, 'b, R: ?Sized> {
352 prefix: &'a [u8],
353 inner: &'a mut CountedReader<'b, R>,
354}
355
356impl<R: Read + ?Sized> Read for PrefixReader<'_, '_, R> {
357 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
358 if buffer.is_empty() {
359 return Ok(0);
360 }
361 if !self.prefix.is_empty() {
362 let read = buffer.len().min(self.prefix.len());
363 buffer[..read].copy_from_slice(&self.prefix[..read]);
364 self.prefix = &self.prefix[read..];
365 return Ok(read);
366 }
367 self.inner.read(buffer)
368 }
369}
370
371fn clone_io_error(error: &io::Error) -> io::Error {
372 match error.raw_os_error() {
373 Some(code) => io::Error::from_raw_os_error(code),
374 None => io::Error::new(error.kind(), error.to_string()),
375 }
376}