diff --git a/src/pocketmine/command/defaults/GiveCommand.php b/src/pocketmine/command/defaults/GiveCommand.php index f26f8b688..a8bcb84e2 100644 --- a/src/pocketmine/command/defaults/GiveCommand.php +++ b/src/pocketmine/command/defaults/GiveCommand.php @@ -25,7 +25,7 @@ use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\event\TranslationContainer; use pocketmine\item\Item; -use pocketmine\nbt\NBT; +use pocketmine\nbt\JsonNBTParser; use pocketmine\nbt\tag\CompoundTag; use pocketmine\Player; use pocketmine\utils\TextFormat; @@ -65,7 +65,7 @@ class GiveCommand extends VanillaCommand{ $tags = $exception = null; $data = implode(" ", array_slice($args, 3)); try{ - $tags = NBT::parseJSON($data); + $tags = JsonNBTParser::parseJSON($data); }catch(\Throwable $ex){ $exception = $ex; } diff --git a/src/pocketmine/nbt/JsonNBTParser.php b/src/pocketmine/nbt/JsonNBTParser.php new file mode 100644 index 000000000..f8c8d6a2b --- /dev/null +++ b/src/pocketmine/nbt/JsonNBTParser.php @@ -0,0 +1,259 @@ +setName($key); + $tag->setValue($value); + $data[$key] = $tag; + } + + $key++; + } + + return $data; + } + + /** + * @param string $str + * @param int $offset + * + * @return NamedTag[] + */ + private static function parseCompound(string $str, int &$offset = 0) : array{ + $len = strlen($str); + + $data = []; + + for(; $offset < $len; ++$offset){ + if($str{$offset - 1} === "}"){ + break; + }elseif($str{$offset} === "}"){ + ++$offset; + break; + } + + $key = self::readKey($str, $offset); + $value = self::readValue($str, $offset, $type); + + $tag = NBT::createTag($type); + if($tag instanceof NamedTag){ + $tag->setName($key); + $tag->setValue($value); + $data[$key] = $tag; + } + } + + return $data; + } + + /** + * @param string $data + * @param int $offset + * @param int|null $type + * + * @return mixed + * @throws \Exception + */ + private static function readValue(string $data, int &$offset, &$type = null){ + $value = ""; + $type = null; + $inQuotes = false; + + $len = strlen($data); + for(; $offset < $len; ++$offset){ + $c = $data{$offset}; + + if(!$inQuotes and ($c === " " or $c === "\r" or $c === "\n" or $c === "\t" or $c === "," or $c === "}" or $c === "]")){ + if($c === "," or $c === "}" or $c === "]"){ + break; + } + }elseif($c === '"'){ + $inQuotes = !$inQuotes; + if($type === null){ + $type = NBT::TAG_String; + }elseif($inQuotes){ + throw new \Exception("Syntax error: invalid quote at offset $offset"); + } + }elseif($c === "\\"){ + $value .= $data{$offset + 1} ?? ""; + ++$offset; + }elseif($c === "{" and !$inQuotes){ + if($value !== ""){ + throw new \Exception("Syntax error: invalid compound start at offset $offset"); + } + ++$offset; + $value = self::parseCompound($data, $offset); + $type = NBT::TAG_Compound; + break; + }elseif($c === "[" and !$inQuotes){ + if($value !== ""){ + throw new \Exception("Syntax error: invalid list start at offset $offset"); + } + ++$offset; + $value = self::parseList($data, $offset); + $type = NBT::TAG_List; + break; + }else{ + $value .= $c; + } + } + + if($value === ""){ + throw new \Exception("Syntax error: invalid empty value at offset $offset"); + } + + if($type === null and strlen($value) > 0){ + $value = trim($value); + $last = strtolower(substr($value, -1)); + $part = substr($value, 0, -1); + + if($last !== "b" and $last !== "s" and $last !== "l" and $last !== "f" and $last !== "d"){ + $part = $value; + $last = null; + } + + if($last !== "f" and $last !== "d" and ((string) ((int) $part)) === $part){ + if($last === "b"){ + $type = NBT::TAG_Byte; + }elseif($last === "s"){ + $type = NBT::TAG_Short; + }elseif($last === "l"){ + $type = NBT::TAG_Long; + }else{ + $type = NBT::TAG_Int; + } + $value = (int) $part; + }elseif(is_numeric($part)){ + if($last === "f" or $last === "d" or strpos($part, ".") !== false){ + if($last === "f"){ + $type = NBT::TAG_Float; + }elseif($last === "d"){ + $type = NBT::TAG_Double; + }else{ + $type = NBT::TAG_Float; + } + $value = (float) $part; + }else{ + if($last === "l"){ + $type = NBT::TAG_Long; + }else{ + $type = NBT::TAG_Int; + } + + $value = $part; + } + }else{ + $type = NBT::TAG_String; + } + } + + return $value; + } + + /** + * @param string $data + * @param int $offset + * + * @return string + * @throws \Exception + */ + private static function readKey(string $data, int &$offset){ + $key = ""; + + $len = strlen($data); + for(; $offset < $len; ++$offset){ + $c = $data{$offset}; + + if($c === ":"){ + ++$offset; + break; + }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t" and $c !== "\""){ + $key .= $c; + } + } + + if($key === ""){ + throw new \Exception("Syntax error: invalid empty key at offset $offset"); + } + + return $key; + } +} \ No newline at end of file diff --git a/src/pocketmine/nbt/NBT.php b/src/pocketmine/nbt/NBT.php index a3cd0df12..b3380c8e4 100644 --- a/src/pocketmine/nbt/NBT.php +++ b/src/pocketmine/nbt/NBT.php @@ -171,199 +171,6 @@ class NBT{ return true; } - public static function parseJSON($data, &$offset = 0){ - $len = strlen($data); - for(; $offset < $len; ++$offset){ - $c = $data{$offset}; - if($c === "{"){ - ++$offset; - $data = self::parseCompound($data, $offset); - return new CompoundTag("", $data); - }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t"){ - throw new \Exception("Syntax error: unexpected '$c' at offset $offset"); - } - } - - return null; - } - - private static function parseList($str, &$offset = 0){ - $len = strlen($str); - - - $key = 0; - $value = null; - - $data = []; - - for(; $offset < $len; ++$offset){ - if($str{$offset - 1} === "]"){ - break; - }elseif($str{$offset} === "]"){ - ++$offset; - break; - } - - $value = self::readValue($str, $offset, $type); - - $tag = self::createTag($type); - if($tag instanceof NamedTag){ - $tag->setName($key); - $tag->setValue($value); - $data[$key] = $tag; - } - - $key++; - } - - return $data; - } - - private static function parseCompound($str, &$offset = 0){ - $len = strlen($str); - - $data = []; - - for(; $offset < $len; ++$offset){ - if($str{$offset - 1} === "}"){ - break; - }elseif($str{$offset} === "}"){ - ++$offset; - break; - } - - $key = self::readKey($str, $offset); - $value = self::readValue($str, $offset, $type); - - $tag = self::createTag($type); - if($tag instanceof NamedTag){ - $tag->setName($key); - $tag->setValue($value); - $data[$key] = $tag; - } - } - - return $data; - } - - private static function readValue($data, &$offset, &$type = null){ - $value = ""; - $type = null; - $inQuotes = false; - - $len = strlen($data); - for(; $offset < $len; ++$offset){ - $c = $data{$offset}; - - if(!$inQuotes and ($c === " " or $c === "\r" or $c === "\n" or $c === "\t" or $c === "," or $c === "}" or $c === "]")){ - if($c === "," or $c === "}" or $c === "]"){ - break; - } - }elseif($c === '"'){ - $inQuotes = !$inQuotes; - if($type === null){ - $type = self::TAG_String; - }elseif($inQuotes){ - throw new \Exception("Syntax error: invalid quote at offset $offset"); - } - }elseif($c === "\\"){ - $value .= $data{$offset + 1} ?? ""; - ++$offset; - }elseif($c === "{" and !$inQuotes){ - if($value !== ""){ - throw new \Exception("Syntax error: invalid compound start at offset $offset"); - } - ++$offset; - $value = self::parseCompound($data, $offset); - $type = self::TAG_Compound; - break; - }elseif($c === "[" and !$inQuotes){ - if($value !== ""){ - throw new \Exception("Syntax error: invalid list start at offset $offset"); - } - ++$offset; - $value = self::parseList($data, $offset); - $type = self::TAG_List; - break; - }else{ - $value .= $c; - } - } - - if($value === ""){ - throw new \Exception("Syntax error: invalid empty value at offset $offset"); - } - - if($type === null and strlen($value) > 0){ - $value = trim($value); - $last = strtolower(substr($value, -1)); - $part = substr($value, 0, -1); - - if($last !== "b" and $last !== "s" and $last !== "l" and $last !== "f" and $last !== "d"){ - $part = $value; - $last = null; - } - - if($last !== "f" and $last !== "d" and ((string) ((int) $part)) === $part){ - if($last === "b"){ - $type = self::TAG_Byte; - }elseif($last === "s"){ - $type = self::TAG_Short; - }elseif($last === "l"){ - $type = self::TAG_Long; - }else{ - $type = self::TAG_Int; - } - $value = (int) $part; - }elseif(is_numeric($part)){ - if($last === "f" or $last === "d" or strpos($part, ".") !== false){ - if($last === "f"){ - $type = self::TAG_Float; - }elseif($last === "d"){ - $type = self::TAG_Double; - }else{ - $type = self::TAG_Float; - } - $value = (float) $part; - }else{ - if($last === "l"){ - $type = self::TAG_Long; - }else{ - $type = self::TAG_Int; - } - - $value = $part; - } - }else{ - $type = self::TAG_String; - } - } - - return $value; - } - - private static function readKey($data, &$offset){ - $key = ""; - - $len = strlen($data); - for(; $offset < $len; ++$offset){ - $c = $data{$offset}; - - if($c === ":"){ - ++$offset; - break; - }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t" and $c !== "\""){ - $key .= $c; - } - } - - if($key === ""){ - throw new \Exception("Syntax error: invalid empty key at offset $offset"); - } - - return $key; - } - public function get($len){ if($len < 0){ $this->offset = strlen($this->buffer) - 1;