mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2025-12-21 06:43:58 -06:00
D2S Parsing almost done. Todo: full item parsing, Editor GUI, NPC Intro Data, refactor code, writeQuest/writeStat function etc.
This commit is contained in:
119
src/D2Char.php
119
src/D2Char.php
@@ -15,11 +15,23 @@ class D2Char {
|
||||
private $bData = null; // char binary data from d2s
|
||||
private $filePath = null; // .d2s file path
|
||||
private $fp = null; // file pointer
|
||||
private $data = null; // full d2s file loaded in $data
|
||||
private $ByteReader = null; // put $data into bytereader
|
||||
private $ISC = null;
|
||||
|
||||
public function __construct($file) {
|
||||
$this->sData = new D2CharStructureData();
|
||||
$this->filePath = $_SESSION['savepath'] . $file;
|
||||
$this->fp = fopen($this->filePath, "r+b");
|
||||
$this->data = file_get_contents($this->filePath);
|
||||
$this->ByteReader = new D2ByteReader($this->data);
|
||||
|
||||
$sql = "SELECT ID,Stat,CSvBits FROM itemstatcost WHERE Saved=1";
|
||||
$ISCData = PDO_FetchAll($sql);
|
||||
|
||||
foreach ($ISCData as $k => $v) {
|
||||
$this->ISC[$v['ID']] = $v;
|
||||
}
|
||||
|
||||
// read offsets here from sData and put into $this->bData
|
||||
// which will be used for cData output
|
||||
@@ -28,40 +40,34 @@ class D2Char {
|
||||
$this->bData[$k] = fread($this->fp, $v);
|
||||
}
|
||||
|
||||
return $this->parseChar(); // end of parseChar() calls parseItems()
|
||||
return $this->parseChar(); // end of parseChar() calls parseItems(), parseStats, etc.
|
||||
}
|
||||
|
||||
public function parseItems() {
|
||||
$data = file_get_contents($this->filePath);
|
||||
$ByteReader = new D2ByteReader($data);
|
||||
|
||||
$i_TotalOffset = strpos($data, "JM");
|
||||
$i_TotalOffset = strpos($this->data, "JM");
|
||||
fseek($this->fp, $i_TotalOffset + 2);
|
||||
$i_Total = unpack('S*', (fread($this->fp, 2)))[1];
|
||||
|
||||
$i_Offsets = [];
|
||||
for ($i = 0; $i <= $i_Total; $i++) {
|
||||
$i_Offsets[] = strposX($data, "JM", $i + 2);
|
||||
$i_Offsets[] = strposX($this->data, "JM", $i + 2);
|
||||
}
|
||||
foreach ($i_Offsets as $k => $v) {
|
||||
$itemOffsets[$v] = $i_Offsets[$k + 1] - $i_Offsets[$k];
|
||||
}
|
||||
array_pop($itemOffsets);
|
||||
$_items=[];
|
||||
$_items = [];
|
||||
foreach ($itemOffsets as $offset => $bytes) {
|
||||
$this->items[] = new D2Item($ByteReader->toBits($ByteReader->readh($offset, $bytes)));
|
||||
}
|
||||
$this->items[] = new D2Item($this->ByteReader->toBits($this->ByteReader->readh($offset, $bytes)));
|
||||
}
|
||||
}
|
||||
|
||||
public function parseChar() {
|
||||
|
||||
// dump(unpack('l', $this->bData[4])[1]);
|
||||
|
||||
$cData = null;
|
||||
$cData['Identifier'] = bin2hex($this->bData[0]);
|
||||
// 96 is v1.10+ - checks out
|
||||
// 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]);
|
||||
@@ -76,38 +82,32 @@ class D2Char {
|
||||
$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]);
|
||||
|
||||
|
||||
$skills = (unpack('l16', $this->bData[56]));
|
||||
foreach($skills as $skill){
|
||||
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]];
|
||||
|
||||
|
||||
// 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);
|
||||
//$x[0][0] ? $diff = 'Normal' : ($x[1][0] ? $diff = 'Nitemare' : $diff = 'Hell');
|
||||
$onDifficulty['NM'] = $x[1][0];
|
||||
$onDifficulty['Normal'] = $x[0][0];
|
||||
$onDifficulty['NM'] = $x[1][0];
|
||||
$onDifficulty['Hell'] = $x[2][0];
|
||||
$cData['Difficulty'] = array_filter($onDifficulty); // $diff;
|
||||
|
||||
// Map ID. This value looks like a random number, but it corresponds with one of the longwords
|
||||
$cData['Difficulty'] = ($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.
|
||||
// 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];
|
||||
@@ -116,20 +116,66 @@ class D2Char {
|
||||
$cData['Waypoints'] = $this->getWaypointsData($file);
|
||||
$cData['NPCIntroductions'] = $this->bData[714];
|
||||
$cData['filePath'] = $this->filePath;
|
||||
|
||||
// returns an array of items,
|
||||
|
||||
// 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();
|
||||
|
||||
|
||||
unset($this->items);
|
||||
unset($this->bData);
|
||||
unset($this->sData);
|
||||
unset($this->ByteReader);
|
||||
unset($this->data);
|
||||
unset($this->fp);
|
||||
unset($this->ISC);
|
||||
|
||||
return $this->cData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function parseStats() {
|
||||
$gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it
|
||||
$if = strposX($this->data, 'if', 1);
|
||||
$len = $if - $gf;
|
||||
$stats = new D2BitReader($this->ByteReader->toBits($this->ByteReader->readh($gf, $len)));
|
||||
|
||||
$bits = $stats->getBits();
|
||||
$cleanbits = substr($bits, 0, -11);
|
||||
$stats->setBits($cleanbits);
|
||||
$bits = $stats->getBits();
|
||||
|
||||
$stats->rewind();
|
||||
for($i=0; $i <= strlen($bits); $i++) {
|
||||
$id = hexdec($this->ByteReader->toBytesR($stats->readb(9)));
|
||||
if($this->ISC[$id]['CSvBits'] !== NULL){
|
||||
$stats->skip($this->ISC[$id]['CSvBits']);
|
||||
}
|
||||
$ids[$id] = $id;
|
||||
}
|
||||
$stats->rewind();
|
||||
foreach($ids as $id){
|
||||
$stats->skip(9);
|
||||
if($this->ISC[$id]['CSvBits'] !== NULL){
|
||||
$val = $stats->readb($this->ISC[$id]['CSvBits']);
|
||||
}
|
||||
$stat = $this->ISC[$id]['Stat'];
|
||||
$values[$stat] = hexdec($this->ByteReader->toBytesR($val));
|
||||
}
|
||||
$values['hitpoints'] = (int) round($values['hitpoints'] >> 11);
|
||||
$values['maxhp'] = (int) round($values['maxhp'] >> 11);
|
||||
$values['mana'] = (int) round($values['mana'] >> 11);
|
||||
$values['maxmana'] = (int) round($values['maxmana'] >> 11);
|
||||
$values['stamina'] = (int) round($values['stamina'] >> 11);
|
||||
$values['maxstamina'] = (int) round($values['maxstamina'] >> 11);
|
||||
$values['killcounter'] = (int) round($values['killcounter'] >> 1);
|
||||
|
||||
$this->cData['stats'] = $values;
|
||||
}
|
||||
|
||||
public function getQuestData($file) {
|
||||
$questsNorm = null;
|
||||
$questsNM = null;
|
||||
@@ -181,7 +227,7 @@ class D2Char {
|
||||
fseek($this->fp, $this->sData->wpOffsetsNorm + 4);
|
||||
$a5 = strrev(strtobits(fread($this->fp, 1)));
|
||||
$wp['Norm'] = str_split($a1 . $a2 . $a3 . $a4 . $a5);
|
||||
|
||||
|
||||
// ddump($wp['Norm']);
|
||||
|
||||
fseek($this->fp, $this->sData->wpOffsetsNM);
|
||||
@@ -224,5 +270,6 @@ class D2Char {
|
||||
// }
|
||||
}
|
||||
return $waypoints;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user