testing more item parsing properties, need to test on actual items

This commit is contained in:
Hash Borgir 2023-06-14 22:19:29 -06:00
parent 505d1e9d89
commit a74628c063
2 changed files with 206 additions and 102 deletions

View File

@ -155,7 +155,7 @@ WHERE sk.charclass = '$class' AND sk.skilldesc != 'dummy'";
// readh(offset, numbytes), convert toBits, feed into D2Item
$_items = [];
foreach ($itemOffsets as $offset => $bytes) {
$this->items[] = new D2Item($this->ByteReader->toBits($this->ByteReader->readh($offset, $bytes))); // Create a new D2Item object and add it to the items array
$this->items[] = new D2Item($this->ByteReader->toBits($this->ByteReader->readh($offset, $bytes)), $this->cData['CharacterName']); // Create a new D2Item object and add it to the items array
}
}
@ -239,15 +239,18 @@ WHERE sk.charclass = '$class' AND sk.skilldesc != 'dummy'";
$cData['NPCIntroductions'] = $this->bData[714];
$cData['filePath'] = $this->filePath;
// returns an array of items,
// each item is an array of item details
$this->parseItems(); // parse items will populate $this->items
$cData['items'] = $this->items; // cData[items] will be $this->items
$this->cData = $cData;
// parse stats
$this->parseStats();
$this->cData['skills'] = $this->parseSkills();
// returns an array of items,
// each item is an array of item details
$this->parseItems(); // parse items will populate $this->items
$cData['items'] = $this->items; // cData[items] will be $this->items
unset($this->items);
//unset($this->bData);
unset($this->sData);

View File

@ -13,6 +13,7 @@ class D2Item {
* @var null
*/
private $bits = null;
private $charName;
/**
* @var null
@ -22,10 +23,11 @@ class D2Item {
/**
* @param $bits
*/
public function __construct($bits) {
public function __construct($bits, $charName = null) {
if ($bits == '')
return false;
$this->bits = $bits;
$this->charName = $charName;
return $this->parseItem();
}
@ -65,9 +67,19 @@ class D2Item {
$row = bindec($b->readr(4)); // bit 69 Row number of the top of the item, counting from 0.
$_stored = bindec($b->readr(3)); // bit 73
// The number of gems (or skulls or jewels) which have been glued to this item (if socketed). There will be this many additional item structures for the gems immediately following this item, in the order that the gems were inserted.
// get item code
$b->seek(76);
$itemCode = '';
foreach (str_split($b->read(32), 8) as $byte) {
$itemCode .= chr(bindec(strrev($byte)));
}
$this->iData['code'] = trim($itemCode);
$b->seek(108);
$this->iData['gems_in'] = bindec($b->readr(3));
// if item is not compact, then we read extended properties
if (!$this->iData['compact']) {
// bit 111, 32bits, Unique identifier. Diablo II randomly generates a value for this field in order to discourage cheaters from "duping" items.
//This appears to be the item's level; i.e., the level with which the item was created (or 'dropped'). The item level is based on the level of the monster who dropped it, the level of the area you're in if found in a chest, or, in rare cases, your characters level. The item level determines what modifiers are allowed on the item.
$b->seek(143);
@ -106,19 +118,20 @@ class D2Item {
break;
}
// after reading item quality, we're at 154 offset.
// ring/amu/jew/char or not. 1 or 0. if 1, next 3 are set
// If this bit is set, the item has one of multiple pictures associated with it; the next field determines which picture a particular item uses. If this bit is 0, the next field is absent. The picture field is used for rings, amulets, jewels, and charms.
// After the above data, if the item is a ring, amulet, jewel, or charm, then it has a 1 bit followed by three more bits. All other items (that I've seen) have just a single 0 bit.
$b->seek(151); // ring bit
$b->seek(154); // picture bit
$ring = bindec($b->read(1));
if ($ring) { // we read 1 bit at 151, end up at 152
$b->seek(152);
if ($ring) { // we read 1 bit at 154, end up at 155
$b->seek(155);
$ringPic = bindec($b->readr(3)); // for jew, amu, rin, char
}
// if ring bit is 0, go to 152 and read 1 bit
// if ring bit is 0, go to 155 and read 1 bit
else {
// This bit apparently is set for certain class-specific Expansion Set items. It indicates the presence of the next 11-bit field. If this bit is 0, the next field is absent.
$b->seek(152);
$b->seek(155);
$_class_specific = bindec($b->read(1));
}
/* Credit for the discovery of this field's meaning goes entirely to Guillaume Courtin of France. Thanks! :->
@ -128,28 +141,14 @@ class D2Item {
Paladin-only shields
Orbs (Sorceress-Only wands)
*/
// if class specific bit (152) is 1, then go to 153 and read 11 bits
// if class specific bit (155) is 1, then go to 156 and read 11 bits
if ($_class_specific) {
$b->seek(153);
$b->seek(156);
$class_specific = bindec($b->readr(11));
}
//var_dump($b->getOffset());
//
{
// if 151 is 1, but 152 is 0, then we have read 152/153/154 already, so we should be at 155
if ($ring && !$_class_specific) {
$b->seek(155);
// if 151 is 1 then we're at 155. if 152 is 1, then we read from 155 to 165, arrive at bit 166
} else if ($ring && $_class_specific) {
$b->seek(166);
// if 151 is 0, we alreay just read it, then we're at 152. if 152 is 1, then we read from 153 to 163, arrive at 164
} else if ($_class_specific && !$ring) {
$b->seek(164);
}
switch ($quality) {
case D2ItemQuality::LOW_QUALITY:
$low_quality_item_data = bindec($b->readr(3));
@ -181,7 +180,20 @@ class D2Item {
// read 11 bits, prefix.
// if no prefix, then next 11 bits will be suffix
$this->iData['magic_prefix'] = bindec($b->readr(11));
if (!$this->iData['magic_prefix']) {
$this->iData['magic_suffix'] = bindec($b->readr(11));
}
if (!empty($this->iData['magic_prefix'])) {
$sql = "SELECT * FROM magicprefix WHERE ROWID='{$this->iData['magic_prefix']}'";
$res = PDO_FetchRow($sql);
$this->iData['magic_prefix'] = $res;
}
if (!empty($this->iData['magic_suffix'])) {
$sql = "SELECT * FROM magicsuffix WHERE ROWID='{$this->iData['magic_sufffix']}'";
$res = PDO_FetchRow($sql);
$this->iData['magic_suffix'] = $res;
}
break;
case D2ItemQuality::SET:
// Set items have a 12-bit field containing the ID of the set. (Not the set member, but the whole set.) The set member is identified by cross-referencing the item type with the set ID. Also note that set items have an extra field following the item-specific data.
@ -189,20 +201,56 @@ class D2Item {
//var_dump($b->getOffset());
$setid = bindec($b->readr(12));
//var_dump($setid);
$sql = "SELECT ROWID,* from sets WHERE ROWID=$setid";
$res = PDO_FetchRow($sql);
// first get set name
$sql = "SELECT `set` from setitems WHERE ROWID=$setid";
$set = PDO_FetchOne($sql);
$this->iData["setname"] = $set;
//ddump($res);
$sql = "SELECT * from setitems WHERE `set`=? AND item=?";
$res = PDO_FetchRow($sql, [$set, $this->iData['code']]);
$this->iData["setname"] = $res;
$this->iData["data"] = $res;
break;
case D2ItemQuality::RARE:
$this->iData['iquality'] = "Rare";
// this is from rare suffix/prefix.txt
$ID1 = bindec($b->readr(8));
$ID2 = bindec($b->readr(8));
$this->iData['iquality'] = 'Rare';
// these are from magix suffix/perfi
$prefixes = [];
$suffixes = [];
for ($i = 1; $i <= 3; $i++) {
$prefixBit = $b->read(1);
if ($prefixBit) {
$prefixes[] = bindec($b->readr(11));
}
$suffixBit = $b->read(1);
if ($suffixBit) {
$suffixes[] = bindec($b->readr(11));
}
}
// now for each prefix/suffix array, we need to
// generate ISC strings
$this->iData['prefixes'] = $prefixes;
$this->iData['suffixes'] = $suffixes;
break;
case D2ItemQuality::UNIQUE:
//Unique items have an additional 12 bit field, which in most cases is the unique item ID. The few exceptions are certain quest items (e.g., the Horadric Malus).
$uni_item = bindec($b->readr(12));
$unid = bindec($b->readr(12));
$sql = "SELECT * from uniqueitems WHERE ROWID=$unid";
$res = PDO_FetchRow($sql);
$this->iData['data'] = $res;
break;
case D2ItemQuality::CRAFTED:
$this->iData['iquality'] = "Crafted";
@ -213,6 +261,65 @@ class D2Item {
}
}
if ($this->iData['runeword']) {
$runeName_possibly = bindec($b->readr(12));
$b->skip(4);
}
if ($this->iData['personalized']) {
$charName = ""; // Assuming $this->charName is already defined
$charBits = $b->readr(7); // Read the first 7 bits
while ($charBits !== "0000000") { // Check for null termination
$char = chr(bindec($charBits));
$charName .= $char;
$charBits = $b->readr(7); // Read the next 7 bits
}
$b->skip(7); // Skip the null termination bits
}
$tome = $b->readr(5);
// Unknown (denoted as 'timestamp' in various places)
$b->skip(1);
// Now begins item specific data/stats
$sql = "SELECT code FROM armor WHERE code = ?";
$res = PDO_FetchOne($sql, [$this->iData['code']]);
// this is an armor
//Only exists if the item is an armor (i.e. the item code is found in Armor.txt)
// Defense of the armor. Subtract this value by 10 to get the true armor value (note: this -10 matches the "Save Add" column in ItemStatCost.txt for the armor stat).
if (!empty($res)) {
$defense = bindec($b->readr(11)) - 10;
$durability = bindec($b->readr(8));
}
$sql = "SELECT code FROM weapons WHERE code = ?";
$res = PDO_FetchOne($sql, [$this->iData['code']]);
// this is a weapon
// get durability
if (!empty($res)) {
$durability = bindec($b->readr(8));
}
// Only exists if the item's max durability is greater than zero
//The first 8 bits are the item's current durability. The last bit is unknown.
$b->skip(9);
$sql = "SELECT code FROM armor WHERE code = ?";
$res = PDO_FetchOne($sql, [$this->iData['code']]);
// weird behavior/bug
// if item is in a container, bodypart will be NULL
// if item is on bodypart, container will be NULL
@ -298,13 +405,7 @@ class D2Item {
// get ear char's name
}
// get item code
$b->seek(76);
$itemCode = '';
foreach (str_split($b->read(32), 8) as $byte) {
$itemCode .= chr(bindec(strrev($byte)));
}
$this->iData['code'] = trim($itemCode);
$sql = "SELECT * from armor WHERE code = '{$this->iData['code']}'";
$res = PDO_FetchRow($sql);