Skip to content

Commit 6a2683a

Browse files
committed
Supporting new Binary NBTs (heads currently disabled)
1 parent 4c4d493 commit 6a2683a

5 files changed

Lines changed: 267 additions & 11 deletions

File tree

head.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
$default_config = include("default-config.php");
33

4-
define("MP_PROTOCOL", "1");
4+
define("MP_PROTOCOL", "2");
55

66
if(file_exists("config.php")){
77
$loaded_config = include("config.php");
@@ -17,6 +17,7 @@
1717
}
1818
}
1919

20+
include("src/nbt.class.php");
2021
include("src/utils.php");
2122
include("src/db_manager.php");
2223
include("src/accounts.php");

js/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ function find_image(namespace, durability = 0){
2929
}
3030
}
3131
}else{
32-
console.log("invalid")
32+
console.log("Didn't found item", namespace);
33+
return {
34+
icon: "not-found",
35+
data: 0,
36+
}
3337
}
3438
}
3539

listing.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
header("Location: ".get_main_path());
2828
}else{
2929
$listing = $listing[0];
30-
if(getname($listing["item_nbt"])==""){ ?>
30+
if($listing["item_name"]==""){ ?>
3131
<span class="title bukkit2name color-f"><?php echo $listing["item_type"]; ?></span>
3232
<?php }else{ ?>
33-
<span class="title colorize"><?php echo htmlspecialchars(getname($listing["item_nbt"])); ?></span>
33+
<span class="title colorize"><?php echo htmlspecialchars($listing["item_name"]); ?></span>
3434
<?php } ?>
3535
<?php } ?>
3636
<?php

src/listings.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,20 @@ function pending_to_claim(){
141141
}
142142

143143
function get_item($query){
144-
$nbt = $query["item_nbt"];
145-
$lore = getlore($nbt);
146-
$lore_data = array();
147-
$head = ConvertTextureData($nbt);
144+
$bin_nbt = $query["item_bin_nbt"];
148145

149-
foreach($lore as $idx=>$line){
150-
array_push($lore_data, 'data-lore-'.$idx.'="'.htmlspecialchars($line).'"');
146+
$lore_data = array();
147+
if($query["item_lore"]!=NULL){
148+
foreach(json_decode($query["item_lore"]) as $idx=>$line){
149+
array_push($lore_data, 'data-lore-'.$idx.'="'.htmlspecialchars($line).'"');
150+
}
151151
}
152152

153+
$head = "";
154+
153155
$tax = raw_purchase_tax();
154156

155-
return str_replace("\n", "", str_replace(" ", "", '<a href="'.get_path("listing").'?id='.$query["id"].'" class="invslot" onmouseenter="showTooltip(event)" onmouseleave="hideTooltip(event)" onmousemove="handleTooltip(event)" onload="item_loaded"><span class="invslot-item"><span class="inv-sprite" data-bukkit="'.$query["item_type"].'" data-id="'.$query["id"].'" data-durability="'.$query["item_durability"].'" data-amount="'.$query["item_amount"].'" data-head="'.$head.'" data-name="'.htmlspecialchars(getname($nbt)).'" data-lore="'.count($lore).'" '.implode(" ", $lore_data).'" data-seller="'.$query["seller_name"].'" data-date="'.$query["publish_date"].'" data-total="'.price_format($query["price"] + ($query["price"]*$tax)).'"><br></span></span></a>'));
157+
return str_replace("\n", "", str_replace(" ", "", '<a href="'.get_path("listing").'?id='.$query["id"].'" class="invslot" onmouseenter="showTooltip(event)" onmouseleave="hideTooltip(event)" onmousemove="handleTooltip(event)" onload="item_loaded"><span class="invslot-item"><span class="inv-sprite" data-bukkit="'.$query["item_type"].'" data-id="'.$query["id"].'" data-durability="'.$query["item_durability"].'" data-amount="'.$query["item_amount"].'" data-head="'.$head.'" data-name="'.htmlspecialchars($query["item_name"]).'" data-lore="'.count($lore_data).'" '.implode(" ", $lore_data).' data-seller="'.$query["seller_name"].'" data-date="'.$query["publish_date"].'" data-total="'.price_format($query["price"] + ($query["price"]*$tax)).'"><br></span></span></a>'));
156158
}
157159

158160
function fetch_main($current_page){

src/nbt.class.php

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
<?php
2+
/**
3+
* Class for reading in NBT-format files.
4+
*
5+
* @author Justin Martin <frozenfire@thefrozenfire.com>
6+
* @version 1.0
7+
*
8+
* Dependencies:
9+
* PHP 4.3+ (5.3+ recommended)
10+
* GMP Extension
11+
*/
12+
13+
if(PHP_INT_SIZE < 8) {
14+
/*
15+
* GMP isn't required for 64-bit machines as we're handling signed ints. We can use native math instead.
16+
* We still need to use GMP for 32-bit builds of PHP though.
17+
*/
18+
extension_loaded("gmp") or trigger_error (
19+
"The NBT class requires the GMP extension for 64-bit number handling on 32-bit PHP builds. ".
20+
"Execution will continue, but will halt if a 64-bit number is handled.", E_USER_NOTICE
21+
);
22+
}
23+
24+
class NBT {
25+
public $root = array();
26+
27+
public $verbose = false;
28+
29+
const TAG_END = 0;
30+
const TAG_BYTE = 1;
31+
const TAG_SHORT = 2;
32+
const TAG_INT = 3;
33+
const TAG_LONG = 4;
34+
const TAG_FLOAT = 5;
35+
const TAG_DOUBLE = 6;
36+
const TAG_BYTE_ARRAY = 7;
37+
const TAG_STRING = 8;
38+
const TAG_LIST = 9;
39+
const TAG_COMPOUND = 10;
40+
const TAG_INT_ARRAY = 11;
41+
const TAG_LONG_ARRAY = 12;
42+
43+
public function __construct($filename = null, $wrapper = "compress.zlib://") {
44+
// PHP 5 constructor (just in case PHP 4-style constructors are ever deprecated)
45+
if(!is_null($filename)) $this->loadFile($filename, $wrapper);
46+
}
47+
48+
public function NBT($filename = null, $wrapper = "compress.zlib://") {
49+
// PHP 4 constructor
50+
if(!is_null($filename)) $this->loadFile($filename, $wrapper);
51+
}
52+
53+
public function loadFile($filename, $wrapper = "compress.zlib://") {
54+
if(is_string($wrapper) && is_file($filename)) {
55+
if($this->verbose) trigger_error("Loading file \"{$filename}\" with stream wrapper \"{$wrapper}\".", E_USER_NOTICE);
56+
$fp = fopen("{$wrapper}{$filename}", "rb");
57+
} elseif(is_null($wrapper) && is_resource($filename)) {
58+
if($this->verbose) trigger_error("Loading file from existing resource.", E_USER_NOTICE);
59+
$fp = $filename;
60+
} else {
61+
trigger_error("First parameter must be a filename or a resource.", E_USER_WARNING);
62+
return false;
63+
}
64+
if($this->verbose) trigger_error("Traversing first tag in file.", E_USER_NOTICE);
65+
$this->traverseTag($fp, $this->root);
66+
if($this->verbose) trigger_error("Encountered end tag for first tag; finished.", E_USER_NOTICE);
67+
return end($this->root);
68+
}
69+
70+
public function writeFile($filename, $wrapper = "compress.zlib://") {
71+
if(is_string($wrapper)) {
72+
if($this->verbose) trigger_error("Writing file \"{$filename}\" with stream wrapper \"{$wrapper}\".", E_USER_NOTICE);
73+
$fp = fopen("{$wrapper}{$filename}", "wb");
74+
} elseif(is_null($wrapper) && is_resource($fp)) {
75+
if($this->verbose) trigger_error("Writing file to existing resource.", E_USER_NOTICE);
76+
$fp = $filename;
77+
} else {
78+
trigger_error("First parameter must be a filename or a resource.", E_USER_WARNING);
79+
return false;
80+
}
81+
if($this->verbose) trigger_error("Writing ".count($this->root)." root tag(s) to file/resource.", E_USER_NOTICE);
82+
foreach($this->root as $rootNum => $rootTag) if(!$this->writeTag($fp, $rootTag)) trigger_error("Failed to write root tag #{$rootNum} to file/resource.", E_USER_WARNING);
83+
return true;
84+
}
85+
86+
public function purge() {
87+
if($this->verbose) trigger_error("Purging all loaded data", E_USER_ERROR);
88+
$this->root = array();
89+
}
90+
91+
public function traverseTag($fp, &$tree) {
92+
if(feof($fp)) {
93+
if($this->verbose) trigger_error("Reached end of file/resource.", E_USER_NOTICE);
94+
return false;
95+
}
96+
$tagType = $this->readType($fp, self::TAG_BYTE); // Read type byte.
97+
if($tagType == self::TAG_END) {
98+
return false;
99+
} else {
100+
if($this->verbose) $position = ftell($fp);
101+
$tagName = $this->readType($fp, self::TAG_STRING);
102+
if($this->verbose) trigger_error("Reading tag \"{$tagName}\" at offset {$position}.", E_USER_NOTICE);
103+
$tagData = $this->readType($fp, $tagType);
104+
// $tree[] = array("type"=>$tagType, "name"=>$tagName, "value"=>$tagData);
105+
$tree[$tagName] = $tagData;
106+
return true;
107+
}
108+
}
109+
110+
public function writeTag($fp, $tag) {
111+
if($this->verbose) {
112+
$position = ftell($fp);
113+
trigger_error("Writing tag \"{$tag["name"]}\" of type {$tag["type"]} at offset {$position}.", E_USER_NOTICE);
114+
}
115+
return $this->writeType($fp, self::TAG_BYTE, $tag["type"]) && $this->writeType($fp, self::TAG_STRING, $tag["name"]) && $this->writeType($fp, $tag["type"], $tag["value"]);
116+
}
117+
118+
public function readType($fp, $tagType) {
119+
switch($tagType) {
120+
case self::TAG_BYTE: // Signed byte (8 bit)
121+
list(,$unpacked) = unpack("c", fread($fp, 1));
122+
return $unpacked;
123+
case self::TAG_SHORT: // Signed short (16 bit, big endian)
124+
list(,$unpacked) = unpack("n", fread($fp, 2));
125+
if($unpacked >= pow(2, 15)) $unpacked -= pow(2, 16); // Convert unsigned short to signed short.
126+
return $unpacked;
127+
case self::TAG_INT: // Signed integer (32 bit, big endian)
128+
list(,$unpacked) = unpack("N", fread($fp, 4));
129+
if($unpacked >= pow(2, 31)) $unpacked -= pow(2, 32); // Convert unsigned int to signed int
130+
return $unpacked;
131+
case self::TAG_LONG: // Signed long (64 bit, big endian)
132+
list(,$firstHalf, $secondHalf) = unpack("N*", fread($fp, 8));
133+
if(PHP_INT_SIZE>=8) {
134+
// Workaround for PHP bug #47564 in 64-bit PHP<=5.2.9
135+
$firstHalf &= 0xFFFFFFFF; $secondHalf &= 0xFFFFFFFF;
136+
137+
$value = ($firstHalf << 32) | $secondHalf;
138+
if($value>pow(2,63)) $value -= pow(2,64);
139+
} else {
140+
extension_loaded("gmp") or trigger_error (
141+
"This file contains a 64-bit number and execution cannot continue. ".
142+
"Please install the GMP extension for 64-bit number handling.", E_USER_ERROR
143+
);
144+
145+
// Fix values >= 2^31 (same fix as above, but this time because it's > PHP_INT_MAX)
146+
$firstHalf = gmp_and($firstHalf, "0xFFFFFFFF");
147+
$secondHalf = gmp_and($secondHalf, "0xFFFFFFFF");
148+
149+
$value = gmp_add($secondHalf, gmp_mul($firstHalf, "4294967296"));
150+
if(gmp_cmp($value, gmp_pow(2, 63)) >= 0) $value = gmp_sub($value, gmp_pow(2, 64));
151+
$value=gmp_strval($value);
152+
}
153+
return $value;
154+
case self::TAG_FLOAT: // Floating point value (32 bit, big endian, IEEE 754-2008)
155+
list(,$value) = (pack('d', 1) == "\77\360\0\0\0\0\0\0")?unpack('f', fread($fp, 4)):unpack('f', strrev(fread($fp, 4)));
156+
return $value;
157+
case self::TAG_DOUBLE: // Double value (64 bit, big endian, IEEE 754-2008)
158+
list(,$value) = (pack('d', 1) == "\77\360\0\0\0\0\0\0")?unpack('d', fread($fp, 8)):unpack('d', strrev(fread($fp, 8)));
159+
return $value;
160+
case self::TAG_BYTE_ARRAY: // Byte array
161+
$arrayLength = $this->readType($fp, self::TAG_INT);
162+
$array = array_values(unpack("c*", fread($fp, $arrayLength)));
163+
return $array;
164+
case self::TAG_STRING: // String
165+
if(!$stringLength = $this->readType($fp, self::TAG_SHORT)) return "";
166+
$string = utf8_decode(fread($fp, $stringLength)); // Read in number of bytes specified by string length, and decode from utf8.
167+
return $string;
168+
case self::TAG_LIST: // List
169+
$tagID = $this->readType($fp, self::TAG_BYTE);
170+
$listLength = $this->readType($fp, self::TAG_INT);
171+
if($this->verbose) trigger_error("Reading in list of {$listLength} tags of type {$tagID}.", E_USER_NOTICE);
172+
$list = array("type"=>$tagID, "value"=>array());
173+
for($i = 0; $i < $listLength; $i++) {
174+
if(feof($fp)) break;
175+
$list["value"][] = $this->readType($fp, $tagID);
176+
}
177+
return $list;
178+
case self::TAG_COMPOUND: // Compound
179+
$tree = array();
180+
while($this->traverseTag($fp, $tree));
181+
return $tree;
182+
case self::TAG_INT_ARRAY:
183+
$arrayLength = $this->readType($fp, self::TAG_INT);
184+
$array = array_values(unpack("N*", fread($fp, $arrayLength * 4)));
185+
return $array;
186+
case self::TAG_LONG_ARRAY:
187+
$arrayLength = $this->readType($fp, self::TAG_INT);
188+
$array = array_values(unpack("J*", fread($fp, $arrayLength * 8)));
189+
return $array;
190+
}
191+
}
192+
193+
public function writeType($fp, $tagType, $value) {
194+
switch($tagType) {
195+
case self::TAG_BYTE: // Signed byte (8 bit)
196+
return is_int(fwrite($fp, pack("c", $value)));
197+
case self::TAG_SHORT: // Signed short (16 bit, big endian)
198+
if($value < 0) $value += pow(2, 16); // Convert signed short to unsigned short
199+
return is_int(fwrite($fp, pack("n", $value)));
200+
case self::TAG_INT: // Signed integer (32 bit, big endian)
201+
if($value < 0) $value += pow(2, 32); // Convert signed int to unsigned int
202+
return is_int(fwrite($fp, pack("N", $value)));
203+
case self::TAG_LONG: // Signed long (64 bit, big endian)
204+
if(PHP_INT_SIZE>=8) {
205+
$firstHalf = ($value & 0xFFFFFFFF00000000) >> 32;
206+
$secondHalf = $value & 0xFFFFFFFF;
207+
208+
$wResult = is_int(fwrite($fp, pack("NN", $firstHalf, $secondHalf)));
209+
} else {
210+
extension_loaded("gmp") or trigger_error (
211+
"This file contains a 64-bit number and execution cannot continue. ".
212+
"Please install the GMP extension for 64-bit number handling.", E_USER_ERROR
213+
);
214+
215+
// 32-bit values seem to be too long for pack() on 32-bit machines. Split into 4x16-bit instead.
216+
$quarters[0] = gmp_div(gmp_and($value, "0xFFFF000000000000"), gmp_pow(2,48));
217+
$quarters[1] = gmp_div(gmp_and($value, "0x0000FFFF00000000"), gmp_pow(2,32));
218+
$quarters[2] = gmp_div(gmp_and($value, "0x00000000FFFF0000"), gmp_pow(2,16));
219+
$quarters[3] = gmp_and($value, "0xFFFF");
220+
221+
$wResult = is_int(fwrite($fp, pack("nnnn", gmp_intval($quarters[0]), gmp_intval($quarters[1]), gmp_intval($quarters[2]), gmp_intval($quarters[3]))));
222+
}
223+
return $wResult;
224+
case self::TAG_FLOAT: // Floating point value (32 bit, big endian, IEEE 754-2008)
225+
return is_int(fwrite($fp, (pack('d', 1) == "\77\360\0\0\0\0\0\0")?pack('f', $value):strrev(pack('f', $value))));
226+
case self::TAG_DOUBLE: // Double value (64 bit, big endian, IEEE 754-2008)
227+
return is_int(fwrite($fp, (pack('d', 1) == "\77\360\0\0\0\0\0\0")?pack('d', $value):strrev(pack('d', $value))));
228+
case self::TAG_BYTE_ARRAY: // Byte array
229+
return $this->writeType($fp, self::TAG_INT, count($value)) && is_int(fwrite($fp, call_user_func_array("pack", array_merge(array("c".count($value)), $value))));
230+
case self::TAG_STRING: // String
231+
$value = utf8_encode($value);
232+
return $this->writeType($fp, self::TAG_SHORT, strlen($value)) && is_int(fwrite($fp, $value));
233+
case self::TAG_LIST: // List
234+
if($this->verbose) trigger_error("Writing list of ".count($value["value"])." tags of type {$value["type"]}.", E_USER_NOTICE);
235+
if(!($this->writeType($fp, self::TAG_BYTE, $value["type"]) && $this->writeType($fp, self::TAG_INT, count($value["value"])))) return false;
236+
foreach($value["value"] as $listItem) if(!$this->writeType($fp, $value["type"], $listItem)) return false;
237+
return true;
238+
case self::TAG_COMPOUND: // Compound
239+
foreach($value as $listItem) if(!$this->writeTag($fp, $listItem)) return false;
240+
if(!is_int(fwrite($fp, "\0"))) return false;
241+
return true;
242+
case self::TAG_INT_ARRAY: // Byte array
243+
return $this->writeType($fp, self::TAG_INT, count($value)) && is_int(fwrite($fp, call_user_func_array("pack", array_merge(array("N".count($value)), $value))));
244+
case self::TAG_LONG_ARRAY: // Byte array
245+
return $this->writeType($fp, self::TAG_LONG, count($value)) && is_long(fwrite($fp, call_user_func_array("pack", array_merge(array("J".count($value)), $value))));
246+
}
247+
}
248+
}
249+
?>

0 commit comments

Comments
 (0)