mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2025-12-21 06:43:58 -06:00
more d2s parsing, refactor
This commit is contained in:
120
src/D2Char.php
120
src/D2Char.php
@@ -4,20 +4,21 @@ require_once 'D2CharStructureData.php';
|
||||
require_once 'D2Files.php';
|
||||
require_once 'D2BitReader.php';
|
||||
require_once 'D2Strings.php';
|
||||
require_once 'D2Item.php';
|
||||
|
||||
class D2Char {
|
||||
|
||||
public $cData; // char data output
|
||||
public $items; // char item data
|
||||
private $sData; // char file structure data
|
||||
private $bData; // char binary data from d2s
|
||||
private $filePath; // .d2s file path
|
||||
private $fp; // file pointer
|
||||
public $cData = null; // char data output
|
||||
public $items = null; // char item data
|
||||
private $sData = null; // char file structure data
|
||||
private $bData = null; // char binary data from d2s
|
||||
private $filePath = null; // .d2s file path
|
||||
private $fp = null; // file pointer
|
||||
|
||||
public function __construct($file) {
|
||||
$this->sData = new D2CharStructureData();
|
||||
$this->filePath = $_SESSION['savepath'] . $file;
|
||||
$this->fp = fopen($this->filePath, "rb+");
|
||||
$this->fp = fopen($this->filePath, "r+b");
|
||||
|
||||
// read offsets here from sData and put into $this->bData
|
||||
// which will be used for cData output
|
||||
@@ -25,62 +26,120 @@ class D2Char {
|
||||
fseek($this->fp, $k);
|
||||
$this->bData[$k] = fread($this->fp, $v);
|
||||
}
|
||||
$this->strings = new D2Strings();
|
||||
return $this->parseChar();
|
||||
|
||||
|
||||
return $this->parseChar(); // end of parseChar() calls parseItems()
|
||||
}
|
||||
|
||||
public function parseItems(){
|
||||
|
||||
// parse items from d2s and add to $this->items[]
|
||||
/*
|
||||
* @return Array of items
|
||||
*/
|
||||
public function parseItems() {
|
||||
$_data = file_get_contents($this->filePath);
|
||||
// get offset of first JM and skip it
|
||||
$_offset = strpos($_data, "JM") + 2;
|
||||
// seek to items_total offset
|
||||
fseek($this->fp, $_offset);
|
||||
// item total is a SHORT 16 bits, 2 bytes
|
||||
$_total = unpack('S*', (fread($this->fp, 2)))[1];
|
||||
|
||||
// Items start from 2nd JM
|
||||
for ($i = 2; $i <= $_total; $i++) {
|
||||
// seek to Each JM (every item begins with JM
|
||||
fseek($this->fp, strposX($_data, 'JM', $i));
|
||||
// read and unpack 21 bytes as array of byte/char (8 bit each)
|
||||
$_items[] = unpack('C*', fread($this->fp, 21));
|
||||
}
|
||||
// Convert item bytes to 21x8 bitfield
|
||||
// each byte is reversed with strrev() for correct bit position
|
||||
// to convert back to ascii, read bits from any position, reverse, bindec(chr())
|
||||
foreach ($_items as $_item) {
|
||||
$item = null;
|
||||
foreach ($_item as $i_bytes) {
|
||||
$item .= strrev(str_pad(decbin($i_bytes), 8, 0, STR_PAD_LEFT));
|
||||
// $item .= (str_pad(dechex($i_bytes), 2, 0, STR_PAD_LEFT));
|
||||
}
|
||||
$this->items[] = new D2Item($item); // return an array of item details
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function parseChar() {
|
||||
|
||||
// dump(unpack('l', $this->bData[4])[1]);
|
||||
|
||||
$cData = null;
|
||||
$cData['Identifier'] = bin2hex($this->bData[0]);
|
||||
// 96 is v1.10+ - checks out
|
||||
$cData['VersionID'] = $sData->version[unpack('l', $this->bData[4])[1]];
|
||||
// 96 is v1.10+ - checks out
|
||||
$cData['VersionID'] = ($this->sData->version[unpack('l', $this->bData[4])[1]]);
|
||||
|
||||
// 1.41 KB (1,447 bytes) - checks out
|
||||
$cData['Filesize'] = round(unpack('l', $this->bData[8])[1] / 1024, 2) . " KB";
|
||||
$cData['Checksum'] = bin2hex($this->bData['12']);
|
||||
$cData['Activeweapon'] = unpack('l', $this->bData['16']);
|
||||
$cData['Checksum'] = bin2hex($this->bData[12]);
|
||||
$cData['Activeweapon'] = unpack('l', $this->bData[16]);
|
||||
$cData['CharacterName'] = str_replace("\0", "", $this->bData[20]);
|
||||
$cData['CharacterStatus'] = array_filter(str_split(strtobits($this->bData[36])));
|
||||
foreach ($cData['CharacterStatus'] as $k => $v) {
|
||||
$str .= ($characterStatus[$k]) . " ";
|
||||
}
|
||||
$cData['CharacterStatus'] = $str;
|
||||
$cData['Characterprogression'] = bindec($this->bData['37']);
|
||||
$cData['Characterprogression'] = bindec($this->bData[37]);
|
||||
$cData['CharacterClass'] = $this->sData->class[unpack('C', $this->bData[40])[1]];
|
||||
$cData['CharacterLevel'] = unpack('C', $this->bData[43])[1];
|
||||
$cData['Lastplayed'] = gmdate("Y-m-d\TH:i:s\Z", unpack('I', $this->bData[48])[0]);
|
||||
$cData['Assignedskills'] = (unpack('i16', $this->bData['56']));
|
||||
|
||||
$skills = (unpack('l16', $this->bData[56]));
|
||||
foreach($skills as $skill){
|
||||
$cData['Assignedskills'][] = $this->sData->skills[$skill];
|
||||
}
|
||||
|
||||
//ddump($this->bData);
|
||||
//ddump($cData);
|
||||
|
||||
$cData['LeftmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[120])[1]];
|
||||
$cData['RightmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[124])[1]];
|
||||
$cData['LeftswapmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[128])[1]];
|
||||
$cData['RightswapmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[132])[1]];
|
||||
$cData['Charactermenuappearance'] = unpack('i', $this->bData[136]);
|
||||
|
||||
// Char menu appearance not needed
|
||||
// $cData['Charactermenuappearance'] = unpack('i', $this->bData[136]);
|
||||
|
||||
|
||||
// todo: refactor to use D2BitstreamReader here
|
||||
$x = str_split(strtobits($this->bData[168]), 8);
|
||||
$onDifficulty['Norm'] = $x[0][0];
|
||||
//$x[0][0] ? $diff = 'Normal' : ($x[1][0] ? $diff = 'Nitemare' : $diff = 'Hell');
|
||||
$onDifficulty['NM'] = $x[1][0];
|
||||
$onDifficulty['NM'] = $x[1][0];
|
||||
$onDifficulty['Hell'] = $x[2][0];
|
||||
$cData['Difficulty'] = array_filter($onDifficulty);
|
||||
$cData['MapID'] = $this->bData['171'];
|
||||
$cData['Mercenarydead'] = unpack('i', $this->bData['177']);
|
||||
$cData['MercenaryID'] = $this->bData['179'];
|
||||
$cData['MercenaryNameID'] = $this->bData['183'];
|
||||
$cData['Mercenarytype'] = $this->bData['185'];
|
||||
$cData['Mercenaryexperience'] = $this->bData['187'];
|
||||
$cData['Difficulty'] = array_filter($onDifficulty); // $diff;
|
||||
|
||||
// Map ID. This value looks like a random number, but it corresponds with one of the longwords
|
||||
// found in the character.map file, according to the difficulty being played. Not needed
|
||||
//$cData['MapID'] = $this->bData[171];
|
||||
|
||||
$cData['MercenaryDead'] = unpack('i', $this->bData[177])[1];
|
||||
// This looks like a random ID for your mercenary.
|
||||
// $cData['MercenaryID'] = unpack('H*', $this->bData[179]);
|
||||
$cData['MercenaryNameID'] = unpack('S', $this->bData[183])[1];
|
||||
$cData['MercenaryType'] = unpack('S', $this->bData[185])[1];
|
||||
$cData['MercenaryExperience'] = unpack('l', $this->bData[187])[1];
|
||||
$cData['Quests'][] = $this->getQuestData($file);
|
||||
$cData['Waypoints'] = $this->getWaypointsData($file);
|
||||
$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;
|
||||
$this->parseItems();
|
||||
|
||||
|
||||
|
||||
return $this->cData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getQuestData($file) {
|
||||
$questsNorm = null;
|
||||
$questsNM = null;
|
||||
@@ -173,6 +232,5 @@ class D2Char {
|
||||
// }
|
||||
}
|
||||
return $waypoints;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user