mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2024-11-30 12:36:03 +00: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:
parent
29063867c5
commit
e51e40175e
@ -66,25 +66,48 @@ $stats->setBits($cleanbits);
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$stats->rewind();
|
$stats->rewind();
|
||||||
for($i=0; $i <= strlen($bits); $i++) {
|
for($i=0; $i <= strlen($bits); $i++) {
|
||||||
$id = hexdec($ByteReader->toBytesR($stats->readb(9)));
|
$id = hexdec($ByteReader->toBytesR($stats->readb(9)));
|
||||||
if (!empty($ISC[$id])){
|
$stats->skip($ISC[$id]['CSvBits']);
|
||||||
|
$ids[$id] = $id;
|
||||||
|
}
|
||||||
|
$stats->rewind();
|
||||||
|
foreach($ids as $id){
|
||||||
|
$stats->skip(9);
|
||||||
$val = $stats->readb($ISC[$id]['CSvBits']);
|
$val = $stats->readb($ISC[$id]['CSvBits']);
|
||||||
$stat = $ISC[$id]['Stat'];
|
$stat = $ISC[$id]['Stat'];
|
||||||
$values[$stat] = hexdec($ByteReader->toBytesR($val));
|
$values[$stat] = hexdec($ByteReader->toBytesR($val));
|
||||||
}
|
}
|
||||||
}
|
$values['hitpoints'] = (int) round($values['hitpoints'] >> 11);
|
||||||
$values['hitpoints'] = (int) round($values['hitpoints'] / 2048);
|
$values['maxhp'] = (int) round($values['maxhp'] >> 11);
|
||||||
$values['maxhp'] = (int) round($values['maxhp'] / 2048);
|
$values['mana'] = (int) round($values['mana'] >> 11);
|
||||||
$values['mana'] = (int) round($values['mana'] / 2048);
|
$values['maxmana'] = (int) round($values['maxmana'] >> 11);
|
||||||
$values['maxmana'] = (int) round($values['maxmana'] / 2048);
|
$values['stamina'] = (int) round($values['stamina'] >> 11);
|
||||||
$values['stamina'] = (int) round($values['stamina'] / 2048);
|
$values['maxstamina'] = (int) round($values['maxstamina'] >> 11);
|
||||||
$values['maxstamina'] = (int) round($values['maxstamina'] / 2048);
|
$values['killcounter'] = (int) round($values['killcounter'] >> 1);
|
||||||
$values['killcounter'] = (int) round($values['killcounter'] / 2);
|
|
||||||
|
|
||||||
var_dump($values);
|
var_dump($values);
|
||||||
|
|
||||||
|
//$stats->rewind();
|
||||||
|
//for($i=0; $i <= strlen($bits); $i++) {
|
||||||
|
// $id = hexdec($ByteReader->toBytesR($stats->readb(9)));
|
||||||
|
// if (!empty($ISC[$id])){
|
||||||
|
// $val = $stats->readb($ISC[$id]['CSvBits']);
|
||||||
|
// $stat = $ISC[$id]['Stat'];
|
||||||
|
// $values[$stat] = hexdec($ByteReader->toBytesR($val));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//$values['hitpoints'] = (int) round($values['hitpoints'] / 2048);
|
||||||
|
//$values['maxhp'] = (int) round($values['maxhp'] / 2048);
|
||||||
|
//$values['mana'] = (int) round($values['mana'] / 2048);
|
||||||
|
//$values['maxmana'] = (int) round($values['maxmana'] / 2048);
|
||||||
|
//$values['stamina'] = (int) round($values['stamina'] / 2048);
|
||||||
|
//$values['maxstamina'] = (int) round($values['maxstamina'] / 2048);
|
||||||
|
//$values['killcounter'] = (int) round($values['killcounter'] / 2);
|
||||||
|
//
|
||||||
|
//var_dump($values);
|
||||||
|
|
||||||
|
|
||||||
//array_pop($ids);
|
//array_pop($ids);
|
||||||
//
|
//
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
class D2BitReader {
|
class D2BitReader {
|
||||||
|
|
||||||
private string $bits;
|
private string $bits = '';
|
||||||
private int $offset = 0;
|
private int $offset = 0;
|
||||||
|
|
||||||
public function __construct(string $bits = '') {
|
public function __construct(string $bits = '') {
|
||||||
@ -17,7 +17,7 @@ class D2BitReader {
|
|||||||
|
|
||||||
/* read X number of bits, like fread */
|
/* read X number of bits, like fread */
|
||||||
|
|
||||||
public function read(int $numBits = 0, bool $str = true) {
|
public function read(int $numBits = 0, bool $str = true) : string {
|
||||||
$bits = null;
|
$bits = null;
|
||||||
for ($i = $this->offset; $i < $this->offset + $numBits; $i++) {
|
for ($i = $this->offset; $i < $this->offset + $numBits; $i++) {
|
||||||
$str ? $bits .= $this->bits[$i] : $bits[] = $this->bits[$i];
|
$str ? $bits .= $this->bits[$i] : $bits[] = $this->bits[$i];
|
||||||
@ -26,7 +26,16 @@ class D2BitReader {
|
|||||||
return $bits;
|
return $bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function readr(int $numBits = 0) {
|
public function readb(int $numBits = 0, bool $str = true) : string {
|
||||||
|
$bits = null;
|
||||||
|
for ($i = $this->offset; $i < $this->offset + $numBits; $i++) {
|
||||||
|
$str ? $bits .= $this->bits[$i] : $bits[] = $this->bits[$i];
|
||||||
|
}
|
||||||
|
$this->offset += $numBits;
|
||||||
|
return strrev(str_pad($bits, 16, 0, STR_PAD_RIGHT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readr(int $numBits = 0) : string {
|
||||||
$bits = null;
|
$bits = null;
|
||||||
for ($i = $this->offset; $i < $this->offset + $numBits; $i++) {
|
for ($i = $this->offset; $i < $this->offset + $numBits; $i++) {
|
||||||
$bits .= $this->bits[$i];
|
$bits .= $this->bits[$i];
|
||||||
@ -74,6 +83,9 @@ class D2BitReader {
|
|||||||
public function getBits(): string {
|
public function getBits(): string {
|
||||||
return $this->bits;
|
return $this->bits;
|
||||||
}
|
}
|
||||||
|
public function setBits(string $bits) {
|
||||||
|
$this->bits = $bits;
|
||||||
|
}
|
||||||
|
|
||||||
public function getOffset(): int {
|
public function getOffset(): int {
|
||||||
return $this->offset;
|
return $this->offset;
|
||||||
|
@ -5,7 +5,7 @@ require_once './src/D2BitReader.php';
|
|||||||
|
|
||||||
class D2ByteReader {
|
class D2ByteReader {
|
||||||
|
|
||||||
private string $data;
|
private string $data = '';
|
||||||
private int $offset = 0;
|
private int $offset = 0;
|
||||||
|
|
||||||
public function __construct(string $data) {
|
public function __construct(string $data) {
|
||||||
@ -102,13 +102,18 @@ class D2ByteReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toBytes(string $bits) {
|
public function toBytesR(string $bits) : string {
|
||||||
foreach (str_split($bits, 8) as $byteString) {
|
foreach (str_split($bits, 8) as $byteString) {
|
||||||
$bytes[] = (bindec(strrev($byteString)));
|
$bytes .= strtoupper(str_pad(dechex(bindec(($byteString))), 2, 0, STR_PAD_LEFT));
|
||||||
}
|
}
|
||||||
foreach ($bytes as $byte) {
|
return $bytes;
|
||||||
dump($byte);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toBytes(string $bits) : string {
|
||||||
|
foreach (str_split($bits, 8) as $byteString) {
|
||||||
|
$bytes .= strtoupper(str_pad(dechex(bindec(strrev($byteString))), 2, 0, STR_PAD_LEFT));
|
||||||
|
}
|
||||||
|
return $bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bitsToHexString(string $bits): string {
|
public function bitsToHexString(string $bits): string {
|
||||||
|
@ -15,11 +15,23 @@ class D2Char {
|
|||||||
private $bData = null; // char binary data from d2s
|
private $bData = null; // char binary data from d2s
|
||||||
private $filePath = null; // .d2s file path
|
private $filePath = null; // .d2s file path
|
||||||
private $fp = null; // file pointer
|
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) {
|
public function __construct($file) {
|
||||||
$this->sData = new D2CharStructureData();
|
$this->sData = new D2CharStructureData();
|
||||||
$this->filePath = $_SESSION['savepath'] . $file;
|
$this->filePath = $_SESSION['savepath'] . $file;
|
||||||
$this->fp = fopen($this->filePath, "r+b");
|
$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
|
// read offsets here from sData and put into $this->bData
|
||||||
// which will be used for cData output
|
// which will be used for cData output
|
||||||
@ -28,20 +40,17 @@ class D2Char {
|
|||||||
$this->bData[$k] = fread($this->fp, $v);
|
$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() {
|
public function parseItems() {
|
||||||
$data = file_get_contents($this->filePath);
|
$i_TotalOffset = strpos($this->data, "JM");
|
||||||
$ByteReader = new D2ByteReader($data);
|
|
||||||
|
|
||||||
$i_TotalOffset = strpos($data, "JM");
|
|
||||||
fseek($this->fp, $i_TotalOffset + 2);
|
fseek($this->fp, $i_TotalOffset + 2);
|
||||||
$i_Total = unpack('S*', (fread($this->fp, 2)))[1];
|
$i_Total = unpack('S*', (fread($this->fp, 2)))[1];
|
||||||
|
|
||||||
$i_Offsets = [];
|
$i_Offsets = [];
|
||||||
for ($i = 0; $i <= $i_Total; $i++) {
|
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) {
|
foreach ($i_Offsets as $k => $v) {
|
||||||
$itemOffsets[$v] = $i_Offsets[$k + 1] - $i_Offsets[$k];
|
$itemOffsets[$v] = $i_Offsets[$k + 1] - $i_Offsets[$k];
|
||||||
@ -49,14 +58,11 @@ class D2Char {
|
|||||||
array_pop($itemOffsets);
|
array_pop($itemOffsets);
|
||||||
$_items = [];
|
$_items = [];
|
||||||
foreach ($itemOffsets as $offset => $bytes) {
|
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() {
|
public function parseChar() {
|
||||||
|
|
||||||
// dump(unpack('l', $this->bData[4])[1]);
|
|
||||||
|
|
||||||
$cData = null;
|
$cData = null;
|
||||||
$cData['Identifier'] = bin2hex($this->bData[0]);
|
$cData['Identifier'] = bin2hex($this->bData[0]);
|
||||||
// 96 is v1.10+ - checks out
|
// 96 is v1.10+ - checks out
|
||||||
@ -82,9 +88,6 @@ class D2Char {
|
|||||||
$cData['Assignedskills'][] = $this->sData->skills[$skill];
|
$cData['Assignedskills'][] = $this->sData->skills[$skill];
|
||||||
}
|
}
|
||||||
|
|
||||||
//ddump($this->bData);
|
|
||||||
//ddump($cData);
|
|
||||||
|
|
||||||
$cData['LeftmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[120])[1]];
|
$cData['LeftmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[120])[1]];
|
||||||
$cData['RightmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[124])[1]];
|
$cData['RightmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[124])[1]];
|
||||||
$cData['LeftswapmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[128])[1]];
|
$cData['LeftswapmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[128])[1]];
|
||||||
@ -92,16 +95,13 @@ class D2Char {
|
|||||||
|
|
||||||
// Char menu appearance not needed
|
// Char menu appearance not needed
|
||||||
// $cData['Charactermenuappearance'] = unpack('i', $this->bData[136]);
|
// $cData['Charactermenuappearance'] = unpack('i', $this->bData[136]);
|
||||||
|
|
||||||
|
|
||||||
// todo: refactor to use D2BitstreamReader here
|
// todo: refactor to use D2BitstreamReader here
|
||||||
$x = str_split(strtobits($this->bData[168]), 8);
|
$x = str_split(strtobits($this->bData[168]), 8);
|
||||||
//$x[0][0] ? $diff = 'Normal' : ($x[1][0] ? $diff = 'Nitemare' : $diff = 'Hell');
|
//$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['NM'] = $x[1][0];
|
||||||
$onDifficulty['Hell'] = $x[2][0];
|
$onDifficulty['Hell'] = $x[2][0];
|
||||||
$cData['Difficulty'] = array_filter($onDifficulty); // $diff;
|
$cData['Difficulty'] = ($onDifficulty); // $diff;
|
||||||
|
|
||||||
// Map ID. This value looks like a random number, but it corresponds with one of the longwords
|
// 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
|
// found in the character.map file, according to the difficulty being played. Not needed
|
||||||
//$cData['MapID'] = $this->bData[171];
|
//$cData['MapID'] = $this->bData[171];
|
||||||
@ -122,13 +122,59 @@ class D2Char {
|
|||||||
$this->parseItems(); // parse items will populate $this->items
|
$this->parseItems(); // parse items will populate $this->items
|
||||||
$cData['items'] = $this->items; // cData[items] will be $this->items
|
$cData['items'] = $this->items; // cData[items] will be $this->items
|
||||||
$this->cData = $cData;
|
$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;
|
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) {
|
public function getQuestData($file) {
|
||||||
$questsNorm = null;
|
$questsNorm = null;
|
||||||
@ -225,4 +271,5 @@ class D2Char {
|
|||||||
}
|
}
|
||||||
return $waypoints;
|
return $waypoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ $form = new Formr\Formr();
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
ddump($charData);
|
//dump($charData);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ EOT;
|
|||||||
<select id='CharacterClass'>
|
<select id='CharacterClass'>
|
||||||
$option
|
$option
|
||||||
</select>
|
</select>
|
||||||
<input style="border: 1px solid black;width: 34px;" type="number" id="CharacterLevel" value="{$c->cData['CharacterLevel']}"><br>
|
Level: <input style="border: 1px solid black;width: 54px;" type="number" id="CharacterLevel" value="{$c->cData['CharacterLevel']}"><br>
|
||||||
$radio
|
$radio
|
||||||
</div>
|
</div>
|
||||||
<div class="col"><h2>Quests</h2>
|
<div class="col"><h2>Quests</h2>
|
||||||
|
Loading…
Reference in New Issue
Block a user