• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[Class] Opentibia_Item_Attributes

Mazen

Developer
Joined
Aug 20, 2007
Messages
612
Reaction score
38
Location
Sweden
Here is a script in PHP that reads the binary item attributes from the database.

But since i don't know how floats look like, they are not supported by the script. (Floats aren't used mush anyway.)

PHP:
<?php 
	/* Made by Mazen */
	
	function bytes_split($hex) {
		$bytes = array();

		if (strlen($hex) % 2 == 0) {
			for ($char = 0; $char < strlen($hex); $char = $char + 2) {
				$bytes[] = substr($hex, $char, 2);
			}
		}

		return $bytes;
	}
	
	function bytes_reverse($hex) {
		return implode("", array_reverse(bytes_split($hex)));
	}

	function bytes_to_str($hex) {
		$string = "";

		foreach (bytes_split($hex) as $byte) {
			$string .= chr("0x" . $byte);
		}

		return $string;
	}
	
	function format_bytes($int) {
		if (strlen($int) % 2 == 1) {
			return "0" . $int;
		} else {
			return $int;
		}
	}
	
	function ordstr($str) {
		$string = (string)$str;
		$hex = "";
		
		for ($i = 0; strlen($string) > $i; $i++) {
			$hex .= format_bytes(dechex(ord($string[$i])));
		}
		
		return $hex;
	}

	class Binary_Buffer {
		private $position;
		private $buffer;
		
		public function __construct($buffer = "") {
			$this->position = 0;
			$this->setBuffer($buffer);
		}
		
		public function setBuffer($buffer) {
			$this->buffer = $buffer;
		}
		
		public function getBuffer() {
			return $this->buffer;
		}
		
		public function _format_number($number, $fomrat_len) {
			$value = bytes_reverse(format_bytes(dechex($number)));
			return $value . str_repeat("0", ($fomrat_len * 2) - strlen($value));
		}
		
		public function addChar($char) {
			$this->buffer .= $this->_format_number($char, 1);
		}
		
		public function addInt($int) {
			$this->buffer .= $this->_format_number($int, 4);
		}
		
		public function addShort($short) {
			$this->buffer .= $this->_format_number($short, 2);
		}
		
		public function addLong($long) {
			$this->buffer .= $this->_format_number($long, 8);
		}
		
		public function addString($string, $dynamic = true, $len_format = 4) {
			if ($dynamic) {
				$this->buffer .= $this->_format_number(strlen($string), $len_format);
			}
			
			$this->buffer .= ordstr($string);
		}
		
		public function getSize() {
			return (strlen($this->buffer) / 2);
		}
		
		public function getPosition() {
			return $this->position;
		}
		
		public function getByFormat($format_len) {
			$value = substr($this->buffer, ($this->position * 2), ($format_len * 2));
			$this->position += $format_len;
			return $value;
		}
		
		public function getChar() {
			return hexdec($this->getByFormat(1));
		}
		
		public function getShort() {
			return hexdec(bytes_reverse($this->getByFormat(2)));
		}
		
		public function getint() {
			return hexdec(bytes_reverse($this->getByFormat(4)));
		}
		
		public function getLong() {
			return hexdec(bytes_reverse($this->getByFormat(8)));
		}
		
		public function getString($format_len = 2) {
			$length = hexdec(bytes_reverse($this->getByFormat($format_len)));
			$value = substr($this->buffer, ($this->position * 2), ($length * 2));
			$this->position += $length;
			return $value;
		}
		
		public function __toString() {
			return $this->getBuffer();
		}
	}

	class Opentibia_Binary_Attributes {
		private $value = array();
		
		const VALUE_TYPE_NONE = 0x00;
		const VALUE_TYPE_STRING = 0x01;
		const VALUE_TYPE_INT = 0x02;
		const VALUE_TYPE_FLOAT = 0x03;
		const VALUE_TYPE_TINY = 0x04;
		
		private $attribute_names = array(
			"aid" => self::VALUE_TYPE_INT,
			"uid" => self::VALUE_TYPE_INT,
			"count" => self::VALUE_TYPE_TINY,
			"name" => self::VALUE_TYPE_STRING,
			"text" => self::VALUE_TYPE_STRING,
			"writer" => self::VALUE_TYPE_STRING,
			"date" => self::VALUE_TYPE_INT
		);
		
		const NUM_UNKNOWN = 0;
		const NUM_TINY = 1;
		const NUM_SHORT = 2;
		const NUM_INT = 4;
		const NUM_DOUBLE = 8;
		
		public function __construct($attributes_bin) {
			$match = array();
			
			if (preg_match("#[0-9a-fA-F]*#", $attributes_bin, $match)) {
				$buffer = new Binary_Buffer($attributes_bin);
				$type = $buffer -> getChar();
				
				if ($type == 0x80) {
					$attributes_count = $buffer -> getShort(); // Counts Number of attributes.
					
					while ($buffer -> getPosition() < $buffer -> getSize()) {
						$name = bytes_to_str($buffer -> getString(2));
						$value_type = $buffer -> getChar();
						
						switch ($value_type) {
							case self::VALUE_TYPE_NONE: {
								$buffer -> getInt();
								$value = "";
								break;
							}

							case self::VALUE_TYPE_STRING: {
								$value = bytes_to_str($buffer -> getString(4));
								break;
							}

							case self::VALUE_TYPE_INT: {
								$value = $buffer -> getInt();
								break;
							}

							case self::VALUE_TYPE_FLOAT: {
								// Int has the same size as float. Note: Floats are NOT SUPPORTED, this is just here to avoid any errors.
								$value = $buffer -> getInt();
								break;
							}

							case self::VALUE_TYPE_TINY: {
								$value = $buffer -> getChar();
								break;
							}
							
							default: {
								$value = bytes_to_str($buffer -> getString(4));
								break;
							}
						}
						
						$this->setAttribute($name, $value);
					}
				} elseif ($type == 0x0F) {
					$this->setAttribute("count", $buffer -> getChar());
				}
			}
		}
		
		public function getAttributes() {
			return $this->value;
		}
		
		public function setAttribute($name, $value) {
			$this->value[$name] = $value;
		}
		
		public function getAttributesAsBinary() {
			$buffer = new Binary_Buffer();
			
			if (count($this->value) > 1) {
				$buffer -> addChar(0x80);
				$buffer -> addShort(count($this->value));
				
				foreach ($this->value as $name => $value) {
					$buffer -> addString($name, true, 2);
					
					$type = $this->attribute_names[$name];
					if (!$type) {
						$type = self::VALUE_TYPE_STRING;
					}
					
					$buffer -> addChar($type);

					switch ($type) {
						case self::VALUE_TYPE_NONE: {
							break;
						}

						case self::VALUE_TYPE_STRING: {
							$buffer -> addString($value, true, 4);
							break;
						}

						case self::VALUE_TYPE_INT: {
							$buffer -> addInt($value);
							break;
						}

						case self::VALUE_TYPE_FLOAT: {
							$buffer -> addInt($value);
							break;
						}

						case self::VALUE_TYPE_TINY: {
							$buffer -> addChar($value);
							break;
						}

						default: {
							$buffer -> addString($value, true, 4);
							break;
						}
					}
				}
			} else {
				$buffer -> addChar(0x0F);
				$buffer -> addChar(current($this->value));
			}
			
			return (strlen($buffer) > 0 ? $buffer : "");
		}
	}
?>

Example:
PHP:
	$test = new Opentibia_Item_Attributes("800300040064617465024614e14d04007465787401040000006869696906007772697465720108000000474d205275667573");
	print_r($test -> getAttributes());

Output:
Code:
Array ( [date] => 1306596422 [text] => hiii [writer] => GM Rufus )

scrolln.png


You can find these binaries in for example database_name.player_items. You cannot read these binary directly from phpmyadmin, but if you implement this script into your website you'll be able to decode what's inside them. :)

Example use:
PHP:
    $results = mysql_query("SELECT `attributes` FROM `player_items` LIMIT 1"); // Get one item
    
    $row = mysql_fetch_assoc($results);
    
    $attributes_hex = ordstr($row["attributes"]); // Convert string to hex.
    
    $ot_item_attribute = new Opentibia_Item_Attribute($attributes_hex); // Create object
    
    echo $ot_item_attribute -> getAttribute("name"); // Get attribute

Since I'm allowed to edit the page, I'll add a version number for the script in case you guys find any bugs.

Version Information
Code:
1.0 - The script is released. Converts binary attributes into a PHP array.
1.1 - Removed the "0x" before the HEX value. So do [B]not[/B] include it when you use the class. See the example.
2.0 - Fully rescripted. Using the buffer method wish is far better and easier to use. All previous bugs should be fixed by now. (Floats still not supported.)

Enjoy.
 
Last edited:
Approved, nice :).
But AFAIK, you can read attributes using converting in mysql, here is an example:

SELECT `player_id`,`pid`,`sid`,CONVERT( `attributes` USING latin1 ) FROM `player_items` WHERE CONVERT( `attributes` USING latin1 ) LIKE '%description%'
Correct me if I'm wrong, but anyway, keep it up:). And if I'm right, feel free to use above query to rebuild your code ;).
 
Last edited:
Approved, nice :).
But AFAIK, you can read attributes using converting in mysql, here is an example:

Correct me if I'm wrong, but anyway, keep it up:). And if I'm right, feel free to use above query to rebuild your code ;).
That wont do any good. You will be able to read through the string and with your own eyes see the information between all the false letters. But that wont be useful in case you wish to have your website understand and make use of it. The script knows the format well and will give you clean information on what's inside the binary.
 
Updated to version 2.0. Enjoy. It'll work mush better than before.

Note that Opentibia_Binary_Attributes:: $attribute_names might not be a complete list of attribute types. If you know other types from the otserv source code, just add them with the right name and format.
 
Last edited:
You seriously didn't get enough thanks for this.

So once again, rep++
 
;o didnt know this relic was released you sir deserve an internet
 
Back
Top Bottom