diff --git a/src/Deserializer/BedrockEditionNbtDeserializer.php b/src/Deserializer/BedrockEditionNbtDeserializer.php index 7c5a20b..388a891 100644 --- a/src/Deserializer/BedrockEditionNbtDeserializer.php +++ b/src/Deserializer/BedrockEditionNbtDeserializer.php @@ -4,6 +4,7 @@ use Aternos\Nbt\MachineByteOrder; use Aternos\Nbt\NbtFormat; +use Aternos\Nbt\String\StringDataFormatException; use pocketmine\utils\Binary; class BedrockEditionNbtDeserializer extends NbtDeserializer @@ -88,4 +89,18 @@ public function readDouble(): DeserializerFloatReadResult $raw = $this->getReader()->read(8); return new DeserializerFloatReadResult(Binary::readLDouble($raw), $raw); } + + /** + * @inheritDoc + * @throws StringDataFormatException + */ + public function readString(): DeserializerStringReadResult + { + $length = $this->readStringLengthPrefix(); + $val = $this->getReader()->read($length->getValue()); + if(strlen($val) !== $length->getValue()){ + throw new StringDataFormatException("Failed to read string: expected length " . $length->getValue() . ", got " . strlen($val)); + } + return new DeserializerStringReadResult($val, $length->getRawData() . $val); + } } diff --git a/src/Deserializer/DeserializerStringReadResult.php b/src/Deserializer/DeserializerStringReadResult.php new file mode 100644 index 0000000..4774550 --- /dev/null +++ b/src/Deserializer/DeserializerStringReadResult.php @@ -0,0 +1,35 @@ +value; + } + + /** + * @return int + */ + public function getRawLength(): int + { + return strlen($this->getRawData()); + } + + /** + * @return int + */ + public function getLength(): int + { + return strlen($this->value); + } +} diff --git a/src/Deserializer/JavaEditionNbtDeserializer.php b/src/Deserializer/JavaEditionNbtDeserializer.php index a7e2796..f342d4a 100644 --- a/src/Deserializer/JavaEditionNbtDeserializer.php +++ b/src/Deserializer/JavaEditionNbtDeserializer.php @@ -4,6 +4,8 @@ use Aternos\Nbt\MachineByteOrder; use Aternos\Nbt\NbtFormat; +use Aternos\Nbt\String\JavaEncoding; +use Aternos\Nbt\String\StringDataFormatException; use pocketmine\utils\Binary; class JavaEditionNbtDeserializer extends NbtDeserializer @@ -88,4 +90,18 @@ public function readDouble(): DeserializerFloatReadResult $raw = $this->getReader()->read(8); return new DeserializerFloatReadResult(Binary::readDouble($raw), $raw); } + + /** + * @inheritDoc + * @throws StringDataFormatException + */ + public function readString(): DeserializerStringReadResult + { + $length = $this->readStringLengthPrefix(); + $val = $this->getReader()->read($length->getValue()); + if(strlen($val) !== $length->getValue()){ + throw new StringDataFormatException("Failed to read string: expected length " . $length->getValue() . ", got " . strlen($val)); + } + return new DeserializerStringReadResult(JavaEncoding::getInstance()->decode($val), $length->getRawData() . $val); + } } diff --git a/src/Deserializer/NbtDeserializer.php b/src/Deserializer/NbtDeserializer.php index 980e86c..a01ff27 100644 --- a/src/Deserializer/NbtDeserializer.php +++ b/src/Deserializer/NbtDeserializer.php @@ -54,6 +54,11 @@ abstract public function readFloat(): DeserializerFloatReadResult; */ abstract public function readDouble(): DeserializerFloatReadResult; + /** + * @return DeserializerStringReadResult + */ + abstract public function readString(): DeserializerStringReadResult; + /** * @return int */ diff --git a/src/Serializer/BedrockEditionNbtSerializer.php b/src/Serializer/BedrockEditionNbtSerializer.php index 11e8e3e..44cd685 100644 --- a/src/Serializer/BedrockEditionNbtSerializer.php +++ b/src/Serializer/BedrockEditionNbtSerializer.php @@ -85,7 +85,17 @@ public function writeFloat(float $value): static */ public function writeDouble(float $value): static { - $this->writer->write(Binary::writeLDouble($value)); + $this->getWriter()->write(Binary::writeLDouble($value)); + return $this; + } + + /** + * @inheritDoc + */ + public function writeString(string $value): static + { + $this->writeStringLengthPrefix(strlen($value)); + $this->getWriter()->write($value); return $this; } } diff --git a/src/Serializer/JavaEditionNbtSerializer.php b/src/Serializer/JavaEditionNbtSerializer.php index b08a4ef..5374c12 100644 --- a/src/Serializer/JavaEditionNbtSerializer.php +++ b/src/Serializer/JavaEditionNbtSerializer.php @@ -4,6 +4,7 @@ use Aternos\Nbt\MachineByteOrder; use Aternos\Nbt\NbtFormat; +use Aternos\Nbt\String\JavaEncoding; use pocketmine\utils\Binary; class JavaEditionNbtSerializer extends NbtSerializer @@ -85,7 +86,18 @@ public function writeFloat(float $value): static */ public function writeDouble(float $value): static { - $this->writer->write(Binary::writeDouble($value)); + $this->getWriter()->write(Binary::writeDouble($value)); + return $this; + } + + /** + * @inheritDoc + */ + public function writeString(string $value): static + { + $encoded = JavaEncoding::getInstance()->encode($value); + $this->writeStringLengthPrefix(strlen($encoded)); + $this->getWriter()->write($encoded); return $this; } } diff --git a/src/Serializer/NbtSerializer.php b/src/Serializer/NbtSerializer.php index a499ad1..6f003f1 100644 --- a/src/Serializer/NbtSerializer.php +++ b/src/Serializer/NbtSerializer.php @@ -62,6 +62,12 @@ abstract public function writeFloat(float $value): static; */ abstract public function writeDouble(float $value): static; + /** + * @param string $value + * @return $this + */ + abstract public function writeString(string $value): static; + /** * @return int */ diff --git a/src/String/JavaEncoding.php b/src/String/JavaEncoding.php index 6fcaa5a..1e97270 100644 --- a/src/String/JavaEncoding.php +++ b/src/String/JavaEncoding.php @@ -60,7 +60,7 @@ public function encode(string $string, string $sourceEncoding = "UTF-8"): string } $result .= chr(0xED); - $result .= chr(0xA0 | (($c >> 0x10) & 0x0F)); + $result .= chr(0xA0 | ((($c >> 0x10) & 0x0F) - 1)); $result .= chr(0x80 | (($c >> 0x0A) & 0x3f)); $result .= chr(0xED); $result .= chr(0xb0 | (($c >> 0x06) & 0x0f)); diff --git a/src/Tag/StringTag.php b/src/Tag/StringTag.php index 10bc686..ced3927 100644 --- a/src/Tag/StringTag.php +++ b/src/Tag/StringTag.php @@ -4,8 +4,6 @@ use Aternos\Nbt\IO\Reader\Reader; use Aternos\Nbt\IO\Writer\Writer; -use Aternos\Nbt\String\JavaEncoding; -use Aternos\Nbt\String\StringDataFormatException; use Exception; class StringTag extends Tag @@ -22,16 +20,6 @@ public function getValue(): string return $this->value; } - /** - * @param string $encoding - * @return string - * @throws StringDataFormatException - */ - public function getDecodedValue(string $encoding = "UTF-8"): string - { - return JavaEncoding::getInstance()->decode($this->value, $encoding); - } - /** * @param string $value * @return StringTag @@ -42,17 +30,6 @@ public function setValue(string $value): StringTag return $this; } - /** - * @param string $value - * @param string $encoding - * @return StringTag - */ - public function setDecodedValue(string $value, string $encoding = "UTF-8"): StringTag - { - $this->value = JavaEncoding::getInstance()->encode($value, $encoding); - return $this; - } - /** * @return int */ @@ -71,8 +48,7 @@ public function writeContent(Writer $writer): static if ($length > 0xffff) { throw new Exception("String exceeds maximum length of " . 0xffff . " characters"); } - $writer->getSerializer()->writeStringLengthPrefix($length); - $writer->write($this->value); + $writer->getSerializer()->writeString($this->value); return $this; } @@ -81,8 +57,7 @@ public function writeContent(Writer $writer): static */ protected function readContent(Reader $reader): static { - $length = $reader->getDeserializer()->readStringLengthPrefix()->getValue(); - $this->value = $reader->read($length); + $this->value = $reader->getDeserializer()->readString()->getValue(); return $this; } diff --git a/src/Tag/Tag.php b/src/Tag/Tag.php index 6413302..0d71172 100644 --- a/src/Tag/Tag.php +++ b/src/Tag/Tag.php @@ -159,12 +159,8 @@ public function getType(): int public function read(Reader $reader, bool $named = true): static { if ($named && static::canBeNamed()) { - $nameLength = $reader->getDeserializer()->readStringLengthPrefix()->getValue(); - $name = $reader->read($nameLength); - if (strlen($name) !== $nameLength) { - throw new Exception("Failed to read name of " . static::class); - } - $this->setName($name); + $name = $reader->getDeserializer()->readString(); + $this->setName($name->getValue()); } return $this->readContent($reader); } @@ -180,12 +176,8 @@ public static function readRaw(Reader $reader, TagOptions $options, bool $named { $result = ""; if ($named && static::canBeNamed()) { - $nameLength = $reader->getDeserializer()->readStringLengthPrefix(); - $name = $reader->read($nameLength->getValue()); - if (strlen($name) !== $nameLength->getValue()) { - throw new Exception("Failed to read name of " . static::class); - } - $result .= $nameLength->getRawData() . $name; + $name = $reader->getDeserializer()->readString(); + $result .= $name->getRawData(); } $result .= static::readContentRaw($reader, $options); return $result; @@ -210,8 +202,7 @@ public function writeData(Writer $writer, bool $named = true): static if (is_null($name)) { throw new Exception("Cannot write named tag, because tag does not have a name value"); } - $serializer->writeStringLengthPrefix(strlen($this->getName())); - $writer->write($this->getName()); + $serializer->writeString($this->getName()); } $this->writeContent($writer); $this->isBeingSerialized = false;