Checksum code fixed. Added DocBlocks to D2Classes. TODO: Fill in docblocks, refactor, code cleanup

This commit is contained in:
Hash Borgir 2022-07-06 17:41:24 -06:00
parent f3b24de2a8
commit eb28039f88
28 changed files with 1285 additions and 340 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/D2Modder.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/formr/formr" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/D2Modder.iml" filepath="$PROJECT_DIR$/.idea/D2Modder.iml" />
</modules>
</component>
</project>

12
.idea/php.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/formr/formr" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
<option name="suggestChangeDefaultLanguageLevel" value="false" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -42,23 +42,39 @@ foreach ($ISCData as $k => $v) {
//$filePath = "D:\Diablo II\MODS\MedianXL2012\save\Test.d2s"; //$filePath = "D:\Diablo II\MODS\MedianXL2012\save\Test.d2s";
$filePath = "Test.d2s"; $filePath = "Necro.d2s";
$char = new D2Char($filePath); $char = new D2Char($filePath);
//$char->setAllSkills(56); $char->setChar("CharacterStatus", "Died", 0);
$char->setChar("CharacterStatus", "Hardcore", 1);
//$char->setChar("CharacterStatus", "Expansion", 1);
//$char->setChar("LeftmousebuttonskillID", 223);
$char->setAllSkills(1);
//$char->setSkill(1, 99); //$char->setSkill(1, 99);
//$char->setChar("CharacterClass", 1); // 127 //$char->setChar("CharacterClass", "Necromancer"); // 127
$char->setChar("CharacterLevel", 0); //$char->setChar("CharacterProgression", 1); // 0 in normal, 1 finished normal, 2 finished nm, 3 finished hell
$char->setStat("strength", 30); $char->setChar("CharacterLevel", 25);
$char->setStat("energy", 30); //$char->setStat("strength", 70);
$char->setStat("dexterity", 30); //$char->setStat("energy", 70);
$char->setStat("vitality", 30); //$char->setStat("dexterity", 70);
$char->setStat("mana", 120); //$char->setStat("vitality", 70);
$char->setStat("maxmana", 200); //$char->setStat("mana", 200);
$char->setStat("stamina", 80); //$char->setStat("maxmana", 200);
$char->setStat("maxstamina", 120); //$char->setStat("soulcounter", 80);
//
//$char->setStat("hitpoints", 70);
//$char->setStat("maxhp", 70);
//$char->setStat("stamina", 70);
//$char->setStat("maxstamina", 200);
//
//
//$char->setStat("gold", 0);
//$char->setStat("soulcounter", 40273409479012734098712903479012374091827349081273490172093478);
unset($char); // destroy $char so we can read it again after writing to it to get updated stats unset($char); // destroy $char so we can read it again after writing to it to get updated stats
$char = new D2Char($filePath); $char = new D2Char($filePath);
var_dump($char->cData['stats']); var_dump($char->cData);

BIN
bin/d2cr.exe Normal file

Binary file not shown.

Binary file not shown.

BIN
img/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

11
ironman-dev.sqbpro Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,15 +1,31 @@
<?php <?php
/**
*
*/
class D2BitReader { class D2BitReader {
/**
* @var string
*/
private string $bits = ''; private string $bits = '';
/**
* @var int
*/
private int $offset = 0; private int $offset = 0;
/**
* @param string $bits
*/
public function __construct(string $bits = '') { public function __construct(string $bits = '') {
$this->bits = $bits; $this->bits = $bits;
return true; return true;
} }
/**
* @param int $numBits
* @return int
*/
public function skip(int $numBits): int { public function skip(int $numBits): int {
$this->offset += $numBits; $this->offset += $numBits;
return $this->offset; return $this->offset;
@ -17,6 +33,11 @@ class D2BitReader {
/* read X number of bits, like fread */ /* read X number of bits, like fread */
/**
* @param int $numBits
* @param bool $str
* @return string
*/
public function read(int $numBits = 0, bool $str = true): string { 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++) {
@ -26,10 +47,21 @@ class D2BitReader {
return $bits; return $bits;
} }
/**
* @param string $bits
* @param string $bitsToWrite
* @param int $offset
* @return array|string|string[]
*/
public function writeBits(string $bits, string $bitsToWrite, int $offset) { public function writeBits(string $bits, string $bitsToWrite, int $offset) {
return substr_replace($bits, $bitsToWrite, $offset, strlen($bitsToWrite)); return substr_replace($bits, $bitsToWrite, $offset, strlen($bitsToWrite));
} }
/**
* @param int $numBits
* @param bool $str
* @return string
*/
public function readb(int $numBits = 0, bool $str = true): string { public function readb(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++) {
@ -39,6 +71,10 @@ class D2BitReader {
return strrev(str_pad($bits, 16, 0, STR_PAD_RIGHT)); return strrev(str_pad($bits, 16, 0, STR_PAD_RIGHT));
} }
/**
* @param int $numBits
* @return string
*/
public function readr(int $numBits = 0): string { 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++) {
@ -50,6 +86,10 @@ class D2BitReader {
/* seek to offset (like fseek) */ /* seek to offset (like fseek) */
/**
* @param int $pos
* @return bool
*/
public function seek(int $pos): bool { public function seek(int $pos): bool {
if ($pos < 0 || $pos > strlen($this->bits)) { if ($pos < 0 || $pos > strlen($this->bits)) {
return false; return false;
@ -60,6 +100,9 @@ class D2BitReader {
/* rewind offset to 0 */ /* rewind offset to 0 */
/**
* @return bool
*/
public function rewind(): bool { public function rewind(): bool {
$this->offset = 0; $this->offset = 0;
return true; return true;
@ -67,6 +110,10 @@ class D2BitReader {
/* Get Bit */ /* Get Bit */
/**
* @param string $bitNum
* @return int
*/
public function getBit(string $bitNum): int { public function getBit(string $bitNum): int {
if ($bitNum < 0 || $bitNum > strlen($this->bits)) { if ($bitNum < 0 || $bitNum > strlen($this->bits)) {
return false; return false;
@ -76,6 +123,11 @@ class D2BitReader {
/* Set Bit */ /* Set Bit */
/**
* @param int $bitNum
* @param int $bitVal
* @return bool
*/
public function setBit(int $bitNum, int $bitVal): bool { public function setBit(int $bitNum, int $bitVal): bool {
if ($bitVal < 0 || $bitVal > 1) { if ($bitVal < 0 || $bitVal > 1) {
return false; return false;
@ -84,18 +136,32 @@ class D2BitReader {
return true; return true;
} }
/**
* @return string
*/
public function getBits(): string { public function getBits(): string {
return $this->bits; return $this->bits;
} }
/**
* @param string $bits
* @return void
*/
public function setBits(string $bits) { public function setBits(string $bits) {
$this->bits = $bits; $this->bits = $bits;
} }
/**
* @return int
*/
public function getOffset(): int { public function getOffset(): int {
return $this->offset; return $this->offset;
} }
/**
* @param int $offset
* @return bool
*/
public function setOffset(int $offset): bool { public function setOffset(int $offset): bool {
if ($offset < 0 || $offset > strlen($this->bits)) if ($offset < 0 || $offset > strlen($this->bits))
return false; return false;

View File

@ -3,17 +3,33 @@
require_once './src/D2Functions.php'; require_once './src/D2Functions.php';
require_once './src/D2BitReader.php'; require_once './src/D2BitReader.php';
/**
*
*/
class D2ByteReader { class D2ByteReader {
/**
* @var string
*/
private string $data = ''; private string $data = '';
/**
* @var int
*/
private int $offset = 0; private int $offset = 0;
/**
* @param string $data
*/
public function __construct(string $data) { public function __construct(string $data) {
if (!$data) if (!$data)
return false; return false;
$this->data = $data; $this->data = $data;
} }
/**
* @param int $numBytes
* @return bool
*/
public function skip(int $numBytes): bool { public function skip(int $numBytes): bool {
if ($numBytes < 0 || $numBytes > strlen($this->data)) if ($numBytes < 0 || $numBytes > strlen($this->data))
return false; return false;
@ -21,6 +37,10 @@ class D2ByteReader {
return $true; return $true;
} }
/**
* @param int $pos
* @return bool
*/
public function seek(int $pos): bool { public function seek(int $pos): bool {
if ($pos < 0 || $pos > strlen($this->data)) if ($pos < 0 || $pos > strlen($this->data))
return false; return false;
@ -28,6 +48,12 @@ class D2ByteReader {
return true; return true;
} }
/**
* @param int $offset
* @param int $numBytes
* @param bool $str
* @return string
*/
public function readh(int $offset, int $numBytes, bool $str = true): string { public function readh(int $offset, int $numBytes, bool $str = true): string {
$this->seek($offset); $this->seek($offset);
$bytes = null; $bytes = null;
@ -37,6 +63,12 @@ class D2ByteReader {
return unpack('H*', $bytes)[1]; return unpack('H*', $bytes)[1];
} }
/**
* @param int $offset
* @param int $numBytes
* @param bool $str
* @return array
*/
public function readc(int $offset, int $numBytes, bool $str = true): array { public function readc(int $offset, int $numBytes, bool $str = true): array {
$this->seek($offset); $this->seek($offset);
$bytes = null; $bytes = null;
@ -46,15 +78,28 @@ class D2ByteReader {
return unpack('C*', $bytes); return unpack('C*', $bytes);
} }
/**
* @return bool
*/
public function rewind(): bool { public function rewind(): bool {
$this->offset = 0; $this->offset = 0;
return true; return true;
} }
/**
* @param int $offset
* @param int $byte
* @return void
*/
public function writeByte(int $offset, int $byte) { public function writeByte(int $offset, int $byte) {
$this->data[$offset] = pack('C', $byte); $this->data[$offset] = pack('C', $byte);
} }
/**
* @param int $offset
* @param string $bytes
* @return false|void
*/
public function writeBytes(int $offset, string $bytes) { public function writeBytes(int $offset, string $bytes) {
if ($offset < 0 || $offset > strlen($this->data) || $bytes == '') if ($offset < 0 || $offset > strlen($this->data) || $bytes == '')
return false; return false;
@ -64,6 +109,12 @@ class D2ByteReader {
$this->data[$pos] = pack('H*', $byte); $this->data[$pos] = pack('H*', $byte);
} }
} }
/**
* @param int $offset
* @param string $bytes
* @return false|void
*/
public function insertBytes(int $offset, string $bytes) { public function insertBytes(int $offset, string $bytes) {
if ($offset < 0 || $offset > strlen($this->data) || $bytes == '') if ($offset < 0 || $offset > strlen($this->data) || $bytes == '')
return false; return false;
@ -72,18 +123,32 @@ class D2ByteReader {
$this->data = hex2bin($newData); $this->data = hex2bin($newData);
} }
/**
* @return false|string
*/
public function getData() { public function getData() {
return $this->data ? $this->data : false; return $this->data ? $this->data : false;
} }
/**
* @param $data
* @return void
*/
public function setData($data){ public function setData($data){
$this->data = $data; $this->data = $data;
} }
/**
* @return int
*/
public function getOffset(): int { public function getOffset(): int {
return $this->offset; return $this->offset;
} }
/**
* @param string $str
* @return bool
*/
public function isHexString(string $str): bool { public function isHexString(string $str): bool {
if (strlen($str) % 2 == 0 && (ctype_xdigit($str))) { if (strlen($str) % 2 == 0 && (ctype_xdigit($str))) {
return true; return true;
@ -91,6 +156,10 @@ class D2ByteReader {
return false; return false;
} }
/**
* @param $input
* @return string
*/
public function toBits($input): string { public function toBits($input): string {
$output = ''; $output = '';
if ($this->isHexString($input)) { if ($this->isHexString($input)) {
@ -113,6 +182,10 @@ class D2ByteReader {
} }
} }
/**
* @param string $bits
* @return string
*/
public function toBytesR(string $bits) : string { public function toBytesR(string $bits) : string {
foreach (str_split($bits, 8) as $byteString) { foreach (str_split($bits, 8) as $byteString) {
$bytes .= strtoupper(str_pad(dechex(bindec(($byteString))), 2, 0, STR_PAD_LEFT)); $bytes .= strtoupper(str_pad(dechex(bindec(($byteString))), 2, 0, STR_PAD_LEFT));
@ -120,6 +193,10 @@ class D2ByteReader {
return $bytes; return $bytes;
} }
/**
* @param string $bits
* @return string
*/
public function toBytes(string $bits) : string { public function toBytes(string $bits) : string {
foreach (str_split($bits, 8) as $byteString) { foreach (str_split($bits, 8) as $byteString) {
$bytes .= strtoupper(str_pad(dechex(bindec(strrev($byteString))), 2, 0, STR_PAD_LEFT)); $bytes .= strtoupper(str_pad(dechex(bindec(strrev($byteString))), 2, 0, STR_PAD_LEFT));
@ -127,6 +204,10 @@ class D2ByteReader {
return $bytes; return $bytes;
} }
/**
* @param string $bits
* @return string
*/
public function bitsToHexString(string $bits): string { public function bitsToHexString(string $bits): string {
$bytes = ''; $bytes = '';
foreach (str_split($bits, 8) as $byte) { foreach (str_split($bits, 8) as $byte) {
@ -135,6 +216,10 @@ class D2ByteReader {
return $bytes; return $bytes;
} }
/**
* @param string $bits
* @return array
*/
public function bitsToHexArray(string $bits): array { public function bitsToHexArray(string $bits): array {
$bytes = []; $bytes = [];
foreach (str_split($bits, 8) as $byte) { foreach (str_split($bits, 8) as $byte) {
@ -143,6 +228,10 @@ class D2ByteReader {
return $bytes; return $bytes;
} }
/**
* @param string $bits
* @return array
*/
public function bitsToIntArray(string $bits): array { public function bitsToIntArray(string $bits): array {
$bytes = []; $bytes = [];
foreach (str_split($bits, 8) as $byte) { foreach (str_split($bits, 8) as $byte) {
@ -151,18 +240,21 @@ class D2ByteReader {
return $bytes; return $bytes;
} }
/* /**
@return Byte with Nth bit set to X * @param int $byte
* @param int $pos
* @param bool $bit
* @return int
*/ */
public function setBit(int $byte, int $pos, bool $bit) { public function setBit(int $byte, int $pos, bool $bit) {
return ($bit ? ($byte | (1 << $pos)) : ($byte & ~(1 << $pos)) ); return ($bit ? ($byte | (1 << $pos)) : ($byte & ~(1 << $pos)) );
} }
/* /**
@return Bit at Nth position in Byte * @param int $byte
* @param int $pos
* @return int
*/ */
public function getBit(int $byte, int $pos): int { public function getBit(int $byte, int $pos): int {
return intval(($byte & (1 << $pos)) != 0); return intval(($byte & (1 << $pos)) != 0);
} }

View File

@ -8,24 +8,70 @@ require_once 'D2Item.php';
require_once 'D2ByteReader.php'; require_once 'D2ByteReader.php';
require_once 'D2Functions.php'; require_once 'D2Functions.php';
/**
*
*/
class D2Char { class D2Char {
/**
* @var null
*/
public $cData = null; // char data output public $cData = null; // char data output
/**
* @var null
*/
public $items = null; // char item data public $items = null; // char item data
/**
* @var D2ByteReader|null
*/
public $ByteReader = null; // put $data into bytereader public $ByteReader = null; // put $data into bytereader
/**
* @var string|null
*/
public $filePath = null; // .d2s file path public $filePath = null; // .d2s file path
/**
* @var D2CharStructureData
*/
private $sData = null; // char file structure data private $sData = null; // char file structure data
/**
* @var null
*/
private $bData = null; // char binary data from d2s private $bData = null; // char binary data from d2s
/**
* @var false|resource
*/
private $fp = null; // file pointer private $fp = null; // file pointer
/**
* @var false|string
*/
private $data = null; // full d2s file loaded in $data private $data = null; // full d2s file loaded in $data
/**
* @var null
*/
private $ISC = null; private $ISC = null;
/**
* @var null
*/
private $skillData = null; private $skillData = null;
/**
* @return void
*/
public function save() { public function save() {
file_put_contents($this->filePath, $this->data); $this->ByteReader->setData($this->data); // update bytereader data
checksumFix($this->filePath); $this->ByteReader->writeBytes(12, "00000000"); // clear old checksum
$this->data = $this->ByteReader->getData(); // update this data to what we get from bytereader after clearing checksum
$checksum = checksum(unpack('C*', $this->data)); // get new checksum
$this->ByteReader->setData($this->data); // update bytereader data
$this->ByteReader->writeBytes(12, $checksum); // write new checksum
$this->data = $this->ByteReader->getData(); // update this data
file_put_contents($this->filePath, $this->data); // write file
} }
/**
* @param $file
*/
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;
@ -64,6 +110,9 @@ WHERE sk.charclass = '$class'";
return $this->parseChar(); // end of parseChar() calls parseItems(), parseStats, etc. return $this->parseChar(); // end of parseChar() calls parseItems(), parseStats, etc.
} }
/**
* @return void
*/
public function parseItems() { public function parseItems() {
$i_TotalOffset = strpos($this->data, "JM"); $i_TotalOffset = strpos($this->data, "JM");
fseek($this->fp, $i_TotalOffset + 2); fseek($this->fp, $i_TotalOffset + 2);
@ -83,6 +132,9 @@ WHERE sk.charclass = '$class'";
} }
} }
/**
* @return array|null
*/
public function parseChar() { public function parseChar() {
$cData = null; $cData = null;
$cData['Identifier'] = bin2hex($this->bData[0]); $cData['Identifier'] = bin2hex($this->bData[0]);
@ -90,30 +142,46 @@ WHERE sk.charclass = '$class'";
$cData['VersionID'] = ($this->sData->version[unpack('l', $this->bData[4])[1]]); $cData['VersionID'] = ($this->sData->version[unpack('l', $this->bData[4])[1]]);
// 1.41 KB (1,447 bytes) - checks out // 1.41 KB (1,447 bytes) - checks out
$cData['Filesize'] = round(unpack('l', $this->bData[8])[1] / 1024, 2) . " KB"; //$cData['Filesize'] = round(unpack('l', $this->bData[8])[1] / 1024, 2) . " KB";
$cData['Filesize'] = unpack('L', $this->bData[8])[1];
$cData['Checksum'] = bin2hex($this->bData[12]); $cData['Checksum'] = bin2hex($this->bData[12]);
$cData['Activeweapon'] = unpack('l', $this->bData[16]); $cData['Activeweapon'] = unpack('L', $this->bData[16]);
$cData['CharacterName'] = str_replace("\0", "", $this->bData[20]); $cData['CharacterName'] = str_replace("\0", "", $this->bData[20]);
$cData['CharacterStatus'] = array_filter(str_split(strtobits($this->bData[36]))); $characterStatus = array_filter(str_split(strrev(strtobits($this->bData[36]))));
foreach ($cData['CharacterStatus'] as $k => $v) { foreach ($characterStatus as $k => $v) {
$str .= ($characterStatus[$k]) . " "; $str .= $this->sData->characterStatus[$k] . " ";
} }
$cData['CharacterStatus'] = $str; $cData['CharacterStatus'] = trim($str);
$cData['Characterprogression'] = bindec($this->bData[37]);
$progression = hexdec(bin2hex($this->bData[37]));
$cData['CharacterProgression'] = $this->sData->characterProgressionClassic[$progression];
if ($cData['CharacterStatus'] == 'Hardcore Expansion') {
$cData['CharacterProgression'] = $this->sData->characterProgressionExpHC[$progression];
}
if ($cData['CharacterStatus'] == "Expansion") {
$cData['CharacterProgression'] = $this->sData->characterProgressionExp[$progression];
}
if ($cData['CharacterStatus'] == "Hardcore") {
$cData['CharacterProgression'] = $this->sData->characterProgressionClassicHC[$progression];
}
$cData['CharacterClass'] = $this->sData->class[unpack('C', $this->bData[40])[1]]; $cData['CharacterClass'] = $this->sData->class[unpack('C', $this->bData[40])[1]];
$cData['CharacterLevel'] = unpack('C', $this->bData[43])[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['Lastplayed'] = gmdate("Y-m-d\TH:i:s\Z", unpack('L', $this->bData[48])[0]);
$skills = (unpack('l16', $this->bData[56])); $skills = (unpack('l16', $this->bData[56]));
// Hotkey assigned skills
foreach ($skills as $skill) { foreach ($skills as $skill) {
$cData['Assignedskills'][] = ($this->sData->skills[$skill]); $cData['Assignedskills'][] = ($this->sData->skills[$skill]);
$cData['Assignedskills'] = array_filter($cData['Assignedskills']); $cData['Assignedskills'] = array_filter($cData['Assignedskills']);
} }
$cData['LeftmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[120])[1]]; $cData['LeftmousebuttonskillID'] = $this->sData->skills[unpack('L', $this->bData[120])[1]];
$cData['RightmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[124])[1]]; $cData['RightmousebuttonskillID'] = $this->sData->skills[unpack('L', $this->bData[124])[1]];
$cData['LeftswapmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[128])[1]]; $cData['LeftswapmousebuttonskillID'] = $this->sData->skills[unpack('L', $this->bData[128])[1]];
$cData['RightswapmousebuttonskillID'] = $this->sData->skills[unpack('i', $this->bData[132])[1]]; $cData['RightswapmousebuttonskillID'] = $this->sData->skills[unpack('L', $this->bData[132])[1]];
// Char menu appearance not needed // Char menu appearance not needed
// $cData['Charactermenuappearance'] = unpack('i', $this->bData[136]); // $cData['Charactermenuappearance'] = unpack('i', $this->bData[136]);
@ -128,7 +196,7 @@ WHERE sk.charclass = '$class'";
// 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];
$cData['MercenaryDead'] = unpack('i', $this->bData[177])[1]; $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['MercenaryID'] = unpack('H*', $this->bData[179]);
$cData['MercenaryNameID'] = unpack('S', $this->bData[183])[1]; $cData['MercenaryNameID'] = unpack('S', $this->bData[183])[1];
@ -149,13 +217,16 @@ WHERE sk.charclass = '$class'";
$this->cData['skills'] = $this->parseSkills(); $this->cData['skills'] = $this->parseSkills();
unset($this->items); unset($this->items);
unset($this->bData); //unset($this->bData);
unset($this->sData); unset($this->sData);
unset($this->fp); unset($this->fp);
return $this->cData; return $this->cData;
} }
/**
* @return array
*/
public function parseSkills() { public function parseSkills() {
$if = strposX($this->data, 'if', 1) + 2; // find if and skip it $if = strposX($this->data, 'if', 1) + 2; // find if and skip it
$jm = strposX($this->data, 'JM', 1); $jm = strposX($this->data, 'JM', 1);
@ -177,6 +248,10 @@ WHERE sk.charclass = '$class'";
return $cData; return $cData;
} }
/**
* @param int $points
* @return void
*/
public function setAllSkills(int $points) { public function setAllSkills(int $points) {
$if = strposX($this->data, 'if', 1) + 2; // find if and skip it $if = strposX($this->data, 'if', 1) + 2; // find if and skip it
$jm = strposX($this->data, 'JM', 1); $jm = strposX($this->data, 'JM', 1);
@ -188,6 +263,11 @@ WHERE sk.charclass = '$class'";
$this->save(); $this->save();
} }
/**
* @param int $skill
* @param int $points
* @return void
*/
public function setSkill(int $skill, int $points) { public function setSkill(int $skill, int $points) {
$skill -= 1; $skill -= 1;
$if = strposX($this->data, 'if', 1) + 2; // find if and skip it $if = strposX($this->data, 'if', 1) + 2; // find if and skip it
@ -198,6 +278,9 @@ WHERE sk.charclass = '$class'";
$this->save(); $this->save();
} }
/**
* @return void
*/
public function parseStats() { public function parseStats() {
$gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it $gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it
$if = strposX($this->data, 'if', 1); $if = strposX($this->data, 'if', 1);
@ -211,7 +294,7 @@ WHERE sk.charclass = '$class'";
$stats->rewind(); $stats->rewind();
for ($i = 0; $i <= strlen($bits); $i++) { for ($i = 0; $i <= strlen($bits); $i++) {
$id = hexdec($this->ByteReader->toBytesR($stats->readb(9))); $id = hexdec($this->ByteReader->toBytesR($stats->readb(9)));
if ($this->ISC[$id]['CSvBits'] !== NULL) { if ($this->ISC[$id]['CSvBits'] !== NULL && $this->ISC[$id]['CSvBits'] !== '') {
$stats->skip($this->ISC[$id]['CSvBits']); $stats->skip($this->ISC[$id]['CSvBits']);
} }
$ids[$id] = $id; $ids[$id] = $id;
@ -219,24 +302,31 @@ WHERE sk.charclass = '$class'";
$stats->rewind(); $stats->rewind();
foreach ($ids as $id) { foreach ($ids as $id) {
$stats->skip(9); $stats->skip(9);
if ($this->ISC[$id]['CSvBits'] !== NULL) { if ($this->ISC[$id]['CSvBits'] !== NULL && $this->ISC[$id]['CSvBits'] !== '') {
$val = $stats->readb($this->ISC[$id]['CSvBits']); $val = $stats->readb($this->ISC[$id]['CSvBits']);
}
$stat = $this->ISC[$id]['Stat']; $stat = $this->ISC[$id]['Stat'];
$values[$stat] = hexdec($this->ByteReader->toBytesR($val)); $values[$stat] = hexdec($this->ByteReader->toBytesR($val));
} }
}
$values['hitpoints'] = (int) round($values['hitpoints'] >> 11); $values['hitpoints'] = (int) round($values['hitpoints'] >> 11);
$values['maxhp'] = (int) round($values['maxhp'] >> 11); $values['maxhp'] = (int) round($values['maxhp'] >> 11);
$values['mana'] = (int) round($values['mana'] >> 11); $values['mana'] = (int) round($values['mana'] >> 11);
$values['maxmana'] = (int) round($values['maxmana'] >> 11); $values['maxmana'] = (int) round($values['maxmana'] >> 11);
$values['stamina'] = (int) round($values['stamina'] >> 11); $values['stamina'] = (int) round($values['stamina'] >> 11);
$values['maxstamina'] = (int) round($values['maxstamina'] >> 11); $values['maxstamina'] = (int) round($values['maxstamina'] >> 11);
$values['killcounter'] = (int) round($values['killcounter'] >> 1); $values['soulcounter'] = (int) round($values['soulcounter'] / 2);
$values['killcounter'] = (int) round($values['killcounter'] / 2);
$this->cData['stats'] = $values; $this->cData['stats'] = $values;
} }
public function setChar(string $stat, mixed $val) { /**
* @param string $stat
* @param mixed $val
* @param mixed|null $val2
* @return false|void
*/
public function setChar(string $stat, mixed $val, mixed $val2 = null) {
switch ($stat) { switch ($stat) {
case 'CharacterName': case 'CharacterName':
if (strlen($val) < 1 || strlen($val) > 15) { if (strlen($val) < 1 || strlen($val) > 15) {
@ -244,15 +334,20 @@ WHERE sk.charclass = '$class'";
} }
$pack = $this->ByteReader->bitsToHexString($this->ByteReader->toBits(pack('Z16', $val))); $pack = $this->ByteReader->bitsToHexString($this->ByteReader->toBits(pack('Z16', $val)));
$this->ByteReader->writeBytes(20, $pack); $this->ByteReader->writeBytes(20, $pack);
$this->data = $this->ByteReader->getData();
$this->save();
rename($this->filePath, $_SESSION['savepath'] . "$val.d2s"); rename($this->filePath, $_SESSION['savepath'] . "$val.d2s");
break; break;
case "CharacterClass": case "CharacterClass":
$this->ByteReader->writeByte(40, $val); $classes = [
$this->data = $this->ByteReader->getData(); 'Amazon' => 0,
$this->save(); 'Sorceress' => 1,
'Necromancer' => 2,
'Paladin' => 3,
'Barbarian' => 4,
'Druid' => 5,
'Assassin' => 6
];
$this->ByteReader->writeByte(40, $classes[$val]);
break; break;
case "CharacterLevel": case "CharacterLevel":
if ($val > 99) { if ($val > 99) {
@ -271,25 +366,110 @@ WHERE sk.charclass = '$class'";
$res = PDO_FetchOne($sql); $res = PDO_FetchOne($sql);
$this->setStat('experience', $res); $this->setStat('experience', $res);
break; break;
case 'CharacterStatus':
switch ($val) {
case 'Died':
$status = (strtobits($this->data[36]));
$status[3] = $val2;
$byte = $this->ByteReader->bitsToHexString($status);
$this->ByteReader->writeByte(36, hexdec($byte));
break;
case 'Hardcore':
$status = (strtobits($this->data[36]));
$status[2] = $val2;
$byte = $this->ByteReader->bitsToHexString($status);
$this->ByteReader->writeByte(36, hexdec($byte));
break;
case 'Expansion':
$status = strrev(strtobits($this->data[36]));
$status[5] = $val2;
$byte = $this->ByteReader->bitsToHexString($status);
$this->ByteReader->writeByte(36, hexdec($byte));
break;
} }
break;
case 'CharacterProgression': // 0 in normal, 1 finished normal, 2 finished nm, 3 finished hell
switch ($val) {
case 0: // in normal
$this->data[37] = pack('C', 3);
break;
case 1: // finished normal
$this->data[37] = pack('C', 8);
break;
case 2: // finished nm
$this->data[37] = pack('C', 13);
break;
case 3: // finished hell
$this->data[37] = pack('C', 15);
break;
$this->save();
}
break;
case 'LeftmousebuttonskillID':
$this->ByteReader->writeBytes(120, dechex($val));
break;
case 'RightmousebuttonskillID':
$this->ByteReader->writeBytes(124, dechex($val));
break;
case 'LeftswapmousebuttonskillID':
$this->ByteReader->writeBytes(128, dechex($val));
break;
case 'RightswapmousebuttonskillID':
$this->ByteReader->writeBytes(132, dechex($val));
break;
} }
// finally save char data to d2s file
$this->data = $this->ByteReader->getData();
$this->save();
}
/**
* @return void
*/
public function resetFileSize() {
$filesize = strlen($this->data);
$this->fp = fopen($this->filePath, "r+b");
fseek($this->fp, 8);
fwrite($this->fp, pack('L', $filesize));
fclose($this->fp);
$this->fp = fopen($this->filePath, "r+b");
checksumFix($this->filePath);
fseek($this->fp, 8);
//ddump(unpack('L', fread($this->fp, 4))[1]);
}
/**
* @return false|string
*/
public function generateAllStats() { public function generateAllStats() {
// 003C08E081000F067860C001071C0008020800F040020064A000000A2C00C0030C000D000000006E05000000FE3F // 003C08E081000F067860C001071C0008020800F040020064A000000A2C00C0030C000D000000006E05000000FE3F
$stats = ''; $stats = '';
foreach ($this->ISC as $i) { for ($i = 0; $i < 16; $i++) {
$id = strrev(str_pad((decbin($i['ID'])), 9, 0, STR_PAD_LEFT)); $id = strrev(str_pad((decbin((int) $this->ISC[$i]['ID'])), 9, 0, STR_PAD_LEFT));
$val = strrev(str_pad((decbin(20)), (int) $i['CSvBits'], 0, STR_PAD_LEFT)); $val = strrev(str_pad((decbin(20)), (int) $this->ISC[$i]['CSvBits'], 0, STR_PAD_LEFT));
// dump($id);
// dump($val);
$stat = $id . $val; $stat = $id . $val;
$stats .= $stat; $stats .= $stat;
} }
$stats .= "000011111111100";
dump($stats);
$gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it $gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it
$if = strposX($this->data, 'if', 1); $if = strposX($this->data, 'if', 1);
$len = $if - $gf; $len = $if - $gf;
$statall = $stats . "11111111100"; $bytes = $this->ByteReader->toBytes($stats);
$bytes = $this->ByteReader->toBytes($statall);
// refresh this data // refresh this data
$data = $this->ByteReader->getData(); $data = $this->ByteReader->getData();
@ -303,11 +483,19 @@ WHERE sk.charclass = '$class'";
$this->data = $data; $this->data = $data;
$this->save(); $this->save();
$this->resetFileSize();
$filedata = file_get_contents($this->filePath); $filedata = file_get_contents($this->filePath);
$this->ByteReader = new D2ByteReader($filedata); $this->ByteReader = new D2ByteReader($filedata);
$this->data = $this->ByteReader->getData(); $this->data = $this->ByteReader->getData();
return $this->data;
} }
/**
* @param string $stat
* @param mixed $val
* @return void
*/
public function setStat(string $stat, mixed $val) { public function setStat(string $stat, mixed $val) {
$gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it $gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it
$if = strposX($this->data, 'if', 1); $if = strposX($this->data, 'if', 1);
@ -324,15 +512,19 @@ WHERE sk.charclass = '$class'";
for ($i = 0; $i <= strlen($bits); $i++) { for ($i = 0; $i <= strlen($bits); $i++) {
$id = hexdec($this->ByteReader->toBytesR($stats->readb(9))); $id = hexdec($this->ByteReader->toBytesR($stats->readb(9)));
$_offsets[$id] = $stats->getOffset(); $_offsets[$id] = $stats->getOffset();
if ($this->ISC[$id]['CSvBits'] !== null && $this->ISC[$id]['CSvBits'] !== '') {
$stats->skip($this->ISC[$id]['CSvBits']); $stats->skip($this->ISC[$id]['CSvBits']);
} }
}
$_offsets[0] = 9; $_offsets[0] = 9;
$offsets = null; $offsets = null;
foreach ($_offsets as $k => $v) { foreach ($_offsets as $k => $v) {
$_stat = $this->ISC[$k]['Stat']; $_stat = $this->ISC[$k]['Stat'];
$_stats[$_stat] = $this->ISC[$k]['Stat']; $_stats[$_stat] = $this->ISC[$k]['Stat'];
$csvbits[$_stat] = $this->ISC[$k]['CSvBits']; $csvbits[$_stat] = $this->ISC[$k]['CSvBits'];
if ($this->ISC[$k]['CSvBits'] !== null && $this->ISC[$k]['CSvBits'] !== '') {
$maxValues[$_stat] = pow(2, $this->ISC[$k]['CSvBits']) - 1; $maxValues[$_stat] = pow(2, $this->ISC[$k]['CSvBits']) - 1;
}
$offsets[$_stat] = $v; $offsets[$_stat] = $v;
} }
if ($stat == 'hitpoints' || $stat == 'maxhp' || $stat == 'mana' || $stat == 'maxmana' || $stat == 'stamina' || $stat == 'maxstamina') { if ($stat == 'hitpoints' || $stat == 'maxhp' || $stat == 'mana' || $stat == 'maxmana' || $stat == 'stamina' || $stat == 'maxstamina') {
@ -344,6 +536,9 @@ WHERE sk.charclass = '$class'";
} }
$bitsToWrite = strrev(str_pad(decbin(intval($val)), $csvbits[$_stats[$stat]], 0, STR_PAD_LEFT)); $bitsToWrite = strrev(str_pad(decbin(intval($val)), $csvbits[$_stats[$stat]], 0, STR_PAD_LEFT));
$statOffset = $offsets[$_stats[$stat]]; $statOffset = $offsets[$_stats[$stat]];
if (!array_key_exists($stat, $_stats)) {
$this->ByteReader->toBits($this->generateAllStats());
} else {
$newBits = $stats->writeBits($bits, $bitsToWrite, $statOffset) . "11111111100"; // 0x1FF padding $newBits = $stats->writeBits($bits, $bitsToWrite, $statOffset) . "11111111100"; // 0x1FF padding
$stats->setBits($newBits); $stats->setBits($newBits);
$bytes = $this->ByteReader->toBytes($newBits); $bytes = $this->ByteReader->toBytes($newBits);
@ -351,7 +546,12 @@ WHERE sk.charclass = '$class'";
$this->data = $this->ByteReader->getData(); $this->data = $this->ByteReader->getData();
$this->save(); $this->save();
} }
}
/**
* @param $file
* @return array
*/
public function getQuestData($file) { public function getQuestData($file) {
$questsNorm = null; $questsNorm = null;
$questsNM = null; $questsNM = null;
@ -390,6 +590,10 @@ WHERE sk.charclass = '$class'";
return $quests; return $quests;
} }
/**
* @param $file
* @return array
*/
public function getWaypointsData($file) { public function getWaypointsData($file) {
$wp = null; $wp = null;
fseek($this->fp, $this->sData->wpOffsetsNorm); fseek($this->fp, $this->sData->wpOffsetsNorm);

View File

@ -2,84 +2,306 @@
require_once 'D2BitReader.php'; require_once 'D2BitReader.php';
/**
*
*/
class D2CharItem { class D2CharItem {
/**
* @var string
*/
private string $bits; private string $bits;
/**
* @var string
*/
public $basename = ''; //name of the base item public $basename = ''; //name of the base item
/**
* @var string
*/
public $item_name = ''; //name string public $item_name = ''; //name string
/**
* @var string
*/
public $item_rank = ''; //normal/exceptional/elite public $item_rank = ''; //normal/exceptional/elite
/**
* @var string
*/
public $item_type = ''; //basic type: armor/weapon/misc public $item_type = ''; //basic type: armor/weapon/misc
//flags //flags
/**
* @var int
*/
public $identified = 0; public $identified = 0;
/**
* @var int
*/
public $sockets = 0; public $sockets = 0;
/**
* @var int
*/
public $ear = 0; public $ear = 0;
/**
* @var int
*/
public $starter = 0; public $starter = 0;
/**
* @var int
*/
public $compact = 0; public $compact = 0;
/**
* @var
*/
public $ethereal; public $ethereal;
/**
* @var
*/
public $personalized; public $personalized;
/**
* @var
*/
public $runeword; public $runeword;
/**
* @var
*/
public $version; public $version;
/**
* @var string
*/
public $runeword_name = ''; public $runeword_name = '';
/**
* @var string
*/
public $runes_title = ''; public $runes_title = '';
//placement //placement
/**
* @var int
*/
public $location = 0; public $location = 0;
/**
* @var int
*/
public $body = 0; public $body = 0;
/**
* @var int
*/
public $col = 0; public $col = 0;
/**
* @var int
*/
public $row = 0; public $row = 0;
/**
* @var int
*/
public $container = 0; public $container = 0;
/**
* @var
*/
public $parent; public $parent;
/**
* @var
*/
public $storage; public $storage;
/**
* @var
*/
public $bodypart; public $bodypart;
/**
* @var int
*/
public $invH = 0; public $invH = 0;
/**
* @var int
*/
public $invW = 0; public $invW = 0;
/**
* @var int
*/
public $beltrows = 1; public $beltrows = 1;
//features //features
/**
* @var string
*/
public $item_code = ''; //3 letter code from txt public $item_code = ''; //3 letter code from txt
/**
* @var int
*/
public $SocketsFilled = 0; public $SocketsFilled = 0;
/**
* @var int
*/
public $SocketsNum = 0; public $SocketsNum = 0;
/**
* @var bool
*/
public $socketable = false; //gem, rune, jewel public $socketable = false; //gem, rune, jewel
/**
* @var string
*/
public $fingerprint = ''; public $fingerprint = '';
/**
* @var int
*/
public $itemlvl = 0; //item level public $itemlvl = 0; //item level
/**
* @var int
*/
public $quality = 0; public $quality = 0;
/**
* @var bool
*/
public $isCharm = false; public $isCharm = false;
/**
* @var bool
*/
public $isJewel = false; public $isJewel = false;
/**
* @var string
*/
public $magic_rank = 'Normal'; //normal/magic/rare/crafted/set/unique public $magic_rank = 'Normal'; //normal/magic/rare/crafted/set/unique
/**
* @var int
*/
public $set_id = 0; //set item id from txt public $set_id = 0; //set item id from txt
/**
* @var string
*/
public $set_item = ''; public $set_item = '';
/**
* @var int
*/
public $set_name = 0; //set name, if it is set item, public $set_name = 0; //set name, if it is set item,
/**
* @var string
*/
public $personname = ''; public $personname = '';
/**
* @var int
*/
public $questdif = -1; public $questdif = -1;
/**
* @var
*/
public $gold; public $gold;
/**
* @var string
*/
public $GUID = ''; public $GUID = '';
/**
* @var int
*/
public $defense = 0; public $defense = 0;
/**
* @var int
*/
public $mindam = 0; public $mindam = 0;
/**
* @var int
*/
public $maxdam = 0; public $maxdam = 0;
/**
* @var int
*/
public $mindam2 = 0; public $mindam2 = 0;
/**
* @var int
*/
public $maxdam2 = 0; public $maxdam2 = 0;
/**
* @var int
*/
public $mindammi = 0; public $mindammi = 0;
/**
* @var int
*/
public $maxdammi = 0; public $maxdammi = 0;
/**
* @var int
*/
public $MaxDur = 0; public $MaxDur = 0;
/**
* @var int
*/
public $CurDur = 0; public $CurDur = 0;
/**
* @var int
*/
public $reqlvl = 0; public $reqlvl = 0;
/**
* @var int
*/
public $reqstr = 0; public $reqstr = 0;
/**
* @var int
*/
public $reqdex = 0; public $reqdex = 0;
/**
* @var int
*/
public $speed = 0; public $speed = 0;
/**
* @var int
*/
public $throwing = 0; public $throwing = 0;
/**
* @var int
*/
public $stackable = 0; public $stackable = 0;
/**
* @var string
*/
public $charName = ''; //ear's name public $charName = ''; //ear's name
/**
* @var
*/
public $gfx; //graphic file name public $gfx; //graphic file name
/**
* @var int
*/
public $baseTrans = -1; //transform indexes for colour remap base item public $baseTrans = -1; //transform indexes for colour remap base item
/**
* @var int
*/
public $magicTrans = -1; //transform indexes for colour remap magic item public $magicTrans = -1; //transform indexes for colour remap magic item
/**
* @var
*/
public $type; //type column from txt public $type; //type column from txt
/**
* @var string
*/
public $spelldesc = ''; //desc for potions public $spelldesc = ''; //desc for potions
/**
* @var
*/
public $ditem; //link to item properties from txt public $ditem; //link to item properties from txt
//mods //mods
/**
* @var int
*/
public $dammult = 100; //damage multiply public $dammult = 100; //damage multiply
/**
* @var int
*/
public $damminadd = 0; //damage add public $damminadd = 0; //damage add
/**
* @var int
*/
public $dammaxadd = 0; //damage add public $dammaxadd = 0; //damage add
/**
* @var int
*/
public $defmult = 100; //defense multiply public $defmult = 100; //defense multiply
/**
* @var int
*/
public $defadd = 0; //defense multiply public $defadd = 0; //defense multiply
/**
* @var int[]
*/
public $resist = array(0, 0, 0, 0, 0, 0); //phy, mag, fire, light, cold, poison public $resist = array(0, 0, 0, 0, 0, 0); //phy, mag, fire, light, cold, poison
/**
* @var int[]
*/
public $attributes = array(0, 0, 0, 0); //str, dex, vit, ene public $attributes = array(0, 0, 0, 0); //str, dex, vit, ene
//arrays //arrays
@ -87,11 +309,23 @@ class D2CharItem {
//socketed items, collected in above function, //socketed items, collected in above function,
//because item has only data for itselt, and //because item has only data for itselt, and
//gems/runes/jewels are standalone //gems/runes/jewels are standalone
/**
* @var array
*/
public $SocketItems = array(); public $SocketItems = array();
/**
* @var array
*/
public $properties = array(); //item variable properties public $properties = array(); //item variable properties
/**
* @var array
*/
public $propids = array(); //properties ids list public $propids = array(); //properties ids list
/**
* @param string $bits
*/
public function __construct(string $bits){ public function __construct(string $bits){
if ($bits == '') return false; if ($bits == '') return false;
$this->bits = $bits; $this->bits = $bits;
@ -99,6 +333,9 @@ class D2CharItem {
return $this->parseItem(); return $this->parseItem();
} }
/**
* @return void
*/
public function parseItem(){ public function parseItem(){
} }

View File

@ -7,12 +7,24 @@ ini_set('max_execution_time', '0');
session_start(); session_start();
ob_start(); ob_start();
/**
*
*/
define('DB_FILE', $_SESSION['modname'] . ".db"); define('DB_FILE', $_SESSION['modname'] . ".db");
PDO_Connect("sqlite:" . DB_FILE); PDO_Connect("sqlite:" . DB_FILE);
/**
*
*/
class D2CharStructureData { class D2CharStructureData {
/**
* @var
*/
public $skills; public $skills;
/**
* @var string[]
*/
public $class = [ public $class = [
0 => 'Amazon', 0 => 'Amazon',
1 => 'Sorceress', 1 => 'Sorceress',
@ -22,6 +34,9 @@ class D2CharStructureData {
5 => 'Druid', 5 => 'Druid',
6 => 'Assassin' 6 => 'Assassin'
]; ];
/**
* @var string[]
*/
public $characterStatus = [ public $characterStatus = [
0 => '', 0 => '',
1 => '', 1 => '',
@ -32,6 +47,93 @@ class D2CharStructureData {
6 => 'Ladder', 6 => 'Ladder',
7 => '' 7 => ''
]; ];
/**
* @var string[]
*/
public $characterProgressionClassicHC = [
0 => '',
1 => '',
2 => '',
3 => '',
4 => 'Count / Countess',
5 => 'Count / Countess',
6 => 'Count / Countess',
7 => 'Count / Countess',
8 => 'Duke / Duchess',
9 => 'Duke / Duchess',
10 => 'Duke / Duchess',
11 => 'Duke / Duchess',
12 => 'Duke / Duchess',
13 => '',
14 => '',
15 => '',
];
/**
* @var string[]
*/
public $characterProgressionClassic = [
0 => '',
1 => '',
2 => '',
3 => '',
4 => 'Sir / Dame',
5 => 'Sir / Dame',
6 => 'Sir / Dame',
7 => 'Sir / Dame',
8 => 'Lord / Lady',
9 => 'Lord / Lady',
10 => 'Lord / Lady',
11 => 'Lord / Lady',
12 => 'Baron / Baroness',
13 => '',
14 => '',
15 => '',
];
/**
* @var string[]
*/
public $characterProgressionExp = [
0 => '',
1 => '',
2 => '',
3 => '',
4 => '',
5 => 'Slayer',
6 => 'Slayer',
7 => 'Slayer',
8 => 'Slayer',
9 => '',
10 => 'Champion',
11 => 'Champion',
12 => 'Champion',
13 => 'Champion',
14 => 'Patriarch / Matriarch ',
15 => 'Patriarch / Matriarch ',
];
/**
* @var string[]
*/
public $characterProgressionExpHC = [
0 => '',
1 => '',
2 => '',
3 => '',
4 => '',
5 => 'Destroyer',
6 => 'Destroyer',
7 => 'Destroyer',
8 => 'Destroyer',
9 => '',
10 => 'Conqueror',
11 => 'Conqueror',
12 => 'Conqueror',
13 => 'Conqueror',
14 => 'Guardian',
15 => 'Guardian',
];
/**
* @var int[]
*/
public $offsets = [ public $offsets = [
0 => 4, // Identifier 0 => 4, // Identifier
4 => 4, // Version ID 4 => 4, // Version ID
@ -67,6 +169,9 @@ class D2CharStructureData {
633 => 81, // Waypoints 633 => 81, // Waypoints
714 => 51, // NPC Introductions 714 => 51, // NPC Introductions
]; ];
/**
* @var string[]
*/
public $qNorm = [ public $qNorm = [
345 => 'introWarriv', 345 => 'introWarriv',
347 => 'Den_Of_Evil', 347 => 'Den_Of_Evil',
@ -110,8 +215,10 @@ class D2CharStructureData {
423 => 'Rite_Of_Passage', 423 => 'Rite_Of_Passage',
425 => 'Eve_Of_Destruction', 425 => 'Eve_Of_Destruction',
// read 425, pointer at 427, + 14 = 441 qNM offset // read 425, pointer at 427, + 14 = 441 qNM offset
]; ];
/**
* @var string[]
*/
public $qNM = [ public $qNM = [
441 => 'introWarrivNM', 441 => 'introWarrivNM',
443 => 'Den_Of_Evil_NM', 443 => 'Den_Of_Evil_NM',
@ -155,9 +262,10 @@ class D2CharStructureData {
519 => 'Rite_Of_Passage', 519 => 'Rite_Of_Passage',
521 => 'Eve_Of_Destruction', 521 => 'Eve_Of_Destruction',
// read 521, pointer at 523, + 14 = 537 qHell offset // read 521, pointer at 523, + 14 = 537 qHell offset
]; ];
/**
* @var string[]
*/
public $qHell = [ public $qHell = [
537 => 'introWarriv', 537 => 'introWarriv',
539 => 'Den_Of_Evil_Hell', 539 => 'Den_Of_Evil_Hell',
@ -201,7 +309,9 @@ class D2CharStructureData {
615 => 'Rite_Of_Passage', 615 => 'Rite_Of_Passage',
617 => 'Eve_Of_Destruction', 617 => 'Eve_Of_Destruction',
]; ];
/**
* @var string[]
*/
public $version = [ public $version = [
71 => "1.00 through v1.06", 71 => "1.00 through v1.06",
87 => "1.07 or Expansion Set v1.08", 87 => "1.07 or Expansion Set v1.08",
@ -209,12 +319,21 @@ class D2CharStructureData {
92 => "v1.09 (both the standard game and the Expansion Set.)", 92 => "v1.09 (both the standard game and the Expansion Set.)",
96 => "v1.10+" 96 => "v1.10+"
]; ];
/**
* @var int
*/
public $wpOffsetsNorm = 643; public $wpOffsetsNorm = 643;
/**
* @var int
*/
public $wpOffsetsNM = 667; public $wpOffsetsNM = 667;
/**
* @var int
*/
public $wpOffsetsHell = 691; public $wpOffsetsHell = 691;
/**
* @var string[]
*/
public $wpNames = [ public $wpNames = [
0 => 'Act 1 - Rogue_Encampment', 0 => 'Act 1 - Rogue_Encampment',
1 => 'Act 1 - Cold_Plains', 1 => 'Act 1 - Cold_Plains',
@ -256,17 +375,22 @@ class D2CharStructureData {
37 => "Act 5 - The_Ancients_Way", 37 => "Act 5 - The_Ancients_Way",
38 => 'Act 5 - Worldstone_Keep_level_2' 38 => 'Act 5 - Worldstone_Keep_level_2'
]; ];
/**
* @var int[]|string[]
*/
public $_qNorm; public $_qNorm;
/**
* @var int[]|string[]
*/
public $_qNM; public $_qNM;
/**
* @var int[]|string[]
*/
public $_qHell; public $_qHell;
/**
/* *
Initialize Skills From Skills.txt
*/ */
public function __construct() { public function __construct() {
$sql = " $sql = "
SELECT SELECT

View File

@ -42,12 +42,23 @@
*/ */
/**
*
*/
class D2Database { class D2Database {
/**
*
*/
public function __construct() { public function __construct() {
PDO_Connect("sqlite:" . DB_FILE); PDO_Connect("sqlite:" . DB_FILE);
} }
/**
* @param $file
* @param $data
* @return void
*/
public function createTables($file, $data) { public function createTables($file, $data) {
$tableName = basename($file); $tableName = basename($file);
$tableName = strtolower(substr($tableName, 0, -4)); $tableName = strtolower(substr($tableName, 0, -4));
@ -68,6 +79,11 @@ class D2Database {
} }
} }
/**
* @param $file
* @param $data
* @return void
*/
public function fillsTables($file, $data) { public function fillsTables($file, $data) {
$tableName = basename($file); $tableName = basename($file);
$tableName = strtolower(substr($tableName, 0, -4)); $tableName = strtolower(substr($tableName, 0, -4));
@ -106,6 +122,10 @@ class D2Database {
} }
} }
/**
* @param $data
* @return void
*/
public function writeTbl($data) { public function writeTbl($data) {
$sql = 'CREATE TABLE IF NOT EXISTS `strings` (`Key` VARCHAR(255), `String` VARCHAR(255));'; $sql = 'CREATE TABLE IF NOT EXISTS `strings` (`Key` VARCHAR(255), `String` VARCHAR(255));';
$res = PDO_Execute($sql); $res = PDO_Execute($sql);
@ -120,6 +140,10 @@ class D2Database {
$res = PDO_Execute($sql); $res = PDO_Execute($sql);
} }
/**
* @param $key
* @return mixed
*/
public function getString($key) { public function getString($key) {
$sql = "SELECT String FROM `strings` WHERE `Key`='$key'"; $sql = "SELECT String FROM `strings` WHERE `Key`='$key'";

View File

@ -42,8 +42,14 @@
*/ */
/**
*
*/
class D2DocGenerator { class D2DocGenerator {
/**
*
*/
public function __construct() { public function __construct() {
require_once './config.php'; require_once './config.php';
require_once './_pdo.php'; require_once './_pdo.php';
@ -61,6 +67,9 @@ class D2DocGenerator {
$idata = new D2ItemData(); $idata = new D2ItemData();
} }
/**
* @return array
*/
public function getIscProps() { public function getIscProps() {
$sql = " $sql = "
SELECT p.`code` as prop, SELECT p.`code` as prop,
@ -103,6 +112,9 @@ class D2DocGenerator {
return $isc; return $isc;
} }
/**
* @return array
*/
public function getStrings() { public function getStrings() {
// load strings // load strings
$sql = 'SELECT * FROM strings'; $sql = 'SELECT * FROM strings';
@ -110,6 +122,9 @@ class D2DocGenerator {
return $strings; return $strings;
} }
/**
* @return array
*/
public function getItemTypesTbl() { public function getItemTypesTbl() {
// load itemtypes table in memory // load itemtypes table in memory
$sql = "SELECT ItemType,Code FROM itemtypes"; $sql = "SELECT ItemType,Code FROM itemtypes";
@ -118,6 +133,9 @@ class D2DocGenerator {
return $itemtypesTbl; return $itemtypesTbl;
} }
/**
* @return array
*/
public function getNameStr() { public function getNameStr() {
// load namestr from 3 files // load namestr from 3 files
$sql = "SELECT code,namestr FROM armor $sql = "SELECT code,namestr FROM armor
@ -130,6 +148,10 @@ class D2DocGenerator {
return $namestr; return $namestr;
} }
/**
* @param $code
* @return string
*/
public function getImage($code) { public function getImage($code) {
$sql = "SELECT invfile FROM armor WHERE `code`=\"$code\" OR `type`=\"$code\" OR `type2`=\"$code\""; $sql = "SELECT invfile FROM armor WHERE `code`=\"$code\" OR `type`=\"$code\" OR `type2`=\"$code\"";
$img = PDO_FetchOne($sql); $img = PDO_FetchOne($sql);
@ -144,6 +166,10 @@ class D2DocGenerator {
return $img = (!empty($img)) ? "$img.png" : "1.png"; return $img = (!empty($img)) ? "$img.png" : "1.png";
} }
/**
* @param $code
* @return false
*/
public function getItemName($code) { public function getItemName($code) {
$sql = "SELECT name FROM armor WHERE `code`=\"$code\" OR `type`=\"$code\" OR `type2`=\"$code\""; $sql = "SELECT name FROM armor WHERE `code`=\"$code\" OR `type`=\"$code\" OR `type2`=\"$code\"";
$name = PDO_FetchOne($sql); $name = PDO_FetchOne($sql);
@ -158,6 +184,9 @@ class D2DocGenerator {
return $name; return $name;
} }
/**
* @return void
*/
public function generateDocs() { public function generateDocs() {
} }

View File

@ -42,11 +42,23 @@
*/ */
/**
*
*/
class D2Files { class D2Files {
/**
* @var array
*/
public $files = []; public $files = [];
/**
* @var
*/
public $charFiles; public $charFiles;
/**
*
*/
public function __construct() { public function __construct() {
$filesToIgnore = [ $filesToIgnore = [
"aiparms.txt", "aiparms.txt",
@ -64,6 +76,9 @@ class D2Files {
return $this->files; return $this->files;
} }
/**
* @return mixed
*/
public function getSaveFiles() { public function getSaveFiles() {
$glob = glob($_SESSION['savepath'] . '*.d2s'); $glob = glob($_SESSION['savepath'] . '*.d2s');

View File

@ -42,6 +42,10 @@
*/ */
/**
* @param $var
* @return void
*/
function ddump($var) { function ddump($var) {
//echo "<pre>"; //echo "<pre>";
var_dump($var); var_dump($var);
@ -51,6 +55,10 @@ function ddump($var) {
die(); die();
} }
/**
* @param $var
* @return void
*/
function dump($var) { function dump($var) {
//echo "<pre>"; //echo "<pre>";
var_dump($var); var_dump($var);
@ -59,6 +67,10 @@ function dump($var) {
//echo json_encode($var, JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT); //echo json_encode($var, JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT);
} }
/**
* @param string $str
* @return string
*/
function strtobits(string $str): string { function strtobits(string $str): string {
$ret = ""; $ret = "";
for ($i = 0; $i < strlen($str); ++$i) { for ($i = 0; $i < strlen($str); ++$i) {
@ -74,14 +86,29 @@ function strtobits(string $str): string {
return $ret; return $ret;
} }
/**
* @param string $hex
* @return string
*/
function swapEndianness(string $hex) { function swapEndianness(string $hex) {
return implode('', array_reverse(str_split($hex, 2))); return implode('', array_reverse(str_split($hex, 2)));
} }
/**
* @param int $n
* @param int $p
* @param bool $b
* @return int
*/
function setBit(int $n, int $p, bool $b) { function setBit(int $n, int $p, bool $b) {
return ($b ? ($n | (1 << $p)) : ($n & ~(1 << $p)) ); return ($b ? ($n | (1 << $p)) : ($n & ~(1 << $p)) );
} }
/**
* @param int $b
* @param int $p
* @return int
*/
function getBit(int $b, int $p) { function getBit(int $b, int $p) {
return intval(($b & (1 << $p)) !== 0); return intval(($b & (1 << $p)) !== 0);
} }
@ -91,29 +118,21 @@ function getBit(int $b, int $p) {
* @param $data * @param $data
* @return string * @return string
*/ */
//function checksum($data) { function checksum($fileData) {
// $nSignature = 0; $nSignature = 0;
// $checksum = 0; foreach ($fileData as $k => $byte) {
// foreach ($data as $k => $byte) { if ($k == 12 || $k == 13 || $k == 14 || $k == 15) {
//// if ($k == 12 || $k == 13 || $k == 14 || $k == 15) { $byte = 0;
//// $byte = 0; }
//// } $nSignature = ((($nSignature << 1) | ($nSignature >> 31)) + $byte & 0xFFFFFFFF);
// $nSignature = ((($nSignature << 1) | ($nSignature >> 31)) + $byte) & 0xFFFFFFFF; }
// $checksum = (($checksum << 1) & 0xffffffff) + $byte + (($checksum & 0x80000000) != 0 ? 1 : 0); return swapEndianness(str_pad(dechex($nSignature), 8, 0, STR_PAD_LEFT));
// } }
// dump(swapEndianness(dechex($nSignature)));
// dump(swapEndianness(dechex($checksum)));
// //return swapEndianness(dechex($nSignature));
// //return swapEndianness(dechex($checksum)); // all of a sudden started returning wrong value, no longer works
//}
//
//function checksumFix(object $ByteReader, string $filePath) {
// $ByteReader->writeBytes(12, 00000000); // zero old checksum
// $newChecksum = checksum(unpack('C*', $ByteReader->getData())); // get new checksum
// $ByteReader->writeBytes(12, $newChecksum); // write new checksum
// file_put_contents($filePath, $ByteReader->getData()); // write bytestream to file
//}
/**
* @param string $filePath
* @return string
*/
function checksumFix(string $filePath) { function checksumFix(string $filePath) {
return trim(shell_exec("bin\d2scs.exe \"$filePath\"")); return trim(shell_exec("bin\d2scs.exe \"$filePath\""));
} }
@ -135,10 +154,18 @@ function strposX($haystack, $needle, $number) {
} }
} }
/**
* @param string $bit
* @return int
*/
function isBit(string $bit): int { function isBit(string $bit): int {
return ((int) $bit ? 1 : 0 ); return ((int) $bit ? 1 : 0 );
} }
/**
* @param $input
* @return string
*/
function toBits($input): string { function toBits($input): string {
$output = ''; $output = '';
if (is_string($input)) { if (is_string($input)) {
@ -156,6 +183,9 @@ function toBits($input): string {
} }
} }
/**
* @return void
*/
function print_mem() { function print_mem() {
/* Currently used memory */ /* Currently used memory */
$mem_usage = memory_get_usage(); $mem_usage = memory_get_usage();
@ -167,6 +197,11 @@ function print_mem() {
echo 'Peak usage: <strong>' . round($mem_peak / 1024 / 1024) . 'MB</strong> RAM.<br><br>'; echo 'Peak usage: <strong>' . round($mem_peak / 1024 / 1024) . 'MB</strong> RAM.<br><br>';
} }
/**
* @param $src
* @param $dst
* @return void
*/
function rcopy($src, $dst) { function rcopy($src, $dst) {
// open the source directory // open the source directory
$dir = opendir($src); $dir = opendir($src);
@ -185,6 +220,10 @@ function rcopy($src, $dst) {
closedir($dir); closedir($dir);
} }
/**
* @param $src
* @return void
*/
function rrmdir($src) { function rrmdir($src) {
$dir = opendir($src); $dir = opendir($src);
while (false !== ( $file = readdir($dir))) { while (false !== ( $file = readdir($dir))) {

View File

@ -3,11 +3,23 @@
require_once 'D2BitReader.php'; require_once 'D2BitReader.php';
require_once 'D2ItemStructureData.php'; require_once 'D2ItemStructureData.php';
/**
*
*/
class D2Item { class D2Item {
/**
* @var null
*/
private $bits = null; private $bits = null;
/**
* @var null
*/
public $iData = null; public $iData = null;
/**
* @param $bits
*/
public function __construct($bits) { public function __construct($bits) {
if ($bits == '') if ($bits == '')
return false; return false;
@ -20,6 +32,9 @@ class D2Item {
* @return array of item details * @return array of item details
*/ */
/**
* @return null
*/
private function parseItem() { private function parseItem() {
$b = new D2BitReader($this->bits); $b = new D2BitReader($this->bits);

View File

@ -1,10 +1,22 @@
<?php <?php
/**
*
*/
class D2ItemData { class D2ItemData {
/**
* @var
*/
public $strings; public $strings;
/**
* @var
*/
public $images; public $images;
/**
* @return void
*/
public function getImages() { public function getImages() {
$sql = "SELECT code,uniqueinvfile,invfile,type,type2 FROM armor $sql = "SELECT code,uniqueinvfile,invfile,type,type2 FROM armor
UNION ALL UNION ALL
@ -24,6 +36,11 @@ class D2ItemData {
unset($isc2); unset($isc2);
} }
/**
* @param $index
* @param $doc
* @return false|string
*/
public function uniqueItems($index, $doc = FALSE) { public function uniqueItems($index, $doc = FALSE) {
if (empty($this->images)) { if (empty($this->images)) {
$this->getImages(); $this->getImages();
@ -897,6 +914,10 @@ class D2ItemData {
} }
} }
/**
* @param $iscStat
* @return mixed
*/
public function getIscStrings($iscStat) { public function getIscStrings($iscStat) {
if (empty($this->strings)) { if (empty($this->strings)) {
$this->getStrings(); $this->getStrings();
@ -904,6 +925,9 @@ class D2ItemData {
return ($this->strings[$iscStat]); return ($this->strings[$iscStat]);
} }
/**
* @return void
*/
public function getStrings() { public function getStrings() {
$sql = " $sql = "
SELECT p.`code` as prop, SELECT p.`code` as prop,

View File

@ -42,6 +42,9 @@
*/ */
/**
*
*/
class D2ItemDesc { class D2ItemDesc {
/* /*
@ -135,13 +138,25 @@ class D2ItemDesc {
*/ */
/**
* @var string
*/
public $str = ''; public $str = '';
// takes value, param. // takes value, param.
// value == Value to show on this stat // value == Value to show on this stat
//descfunc 14 //descfunc 14
/**
* @var
*/
public $skilltabsDesc; public $skilltabsDesc;
/**
* @var
*/
public $skilltabsDescClean; public $skilltabsDescClean;
/**
* @var string[]
*/
public $skilltabs = [ public $skilltabs = [
//ama //ama
'0' => 'StrSklTabItem3', '0' => 'StrSklTabItem3',
@ -172,6 +187,9 @@ class D2ItemDesc {
'19' => 'StrSklTabItem20', '19' => 'StrSklTabItem20',
'20' => 'StrSklTabItem21' '20' => 'StrSklTabItem21'
]; ];
/**
* @var string[]
*/
public $charClass = [ public $charClass = [
"ama" => "Amazon", "ama" => "Amazon",
"sor" => "Sorceress", "sor" => "Sorceress",
@ -245,6 +263,12 @@ class D2ItemDesc {
* *
*/ */
/**
* @param $par
* @param $min
* @param $max
* @return void
*/
public function prep($par, $min, $max) { public function prep($par, $min, $max) {
} }
@ -268,15 +292,18 @@ class D2ItemDesc {
*/ */
/**
*
*/
public function __construct() { public function __construct() {
} }
/**
* @param $params
* @return false|string
*/
public function getDesc($params = []) { public function getDesc($params = []) {
if (empty($params)) if (empty($params))
return false; return false;

View File

@ -1,40 +1,112 @@
<?php <?php
/**
*
*/
class D2ItemLocation { class D2ItemLocation {
//location //location
/**
*
*/
const STORED = 0; const STORED = 0;
/**
*
*/
const EQUIPPED = 1; const EQUIPPED = 1;
/**
*
*/
const BELTI = 2; const BELTI = 2;
/**
*
*/
const CURSOR = 4; const CURSOR = 4;
/**
*
*/
const ITEM = 6; const ITEM = 6;
} }
/**
*
*/
class D2ItemLocationStored { class D2ItemLocationStored {
//storage //storage
/**
*
*/
const NONE = 0; const NONE = 0;
/**
*
*/
const INVENTORY = 1; const INVENTORY = 1;
/**
*
*/
const CUBE = 4; const CUBE = 4;
/**
*
*/
const STASH = 5; const STASH = 5;
} }
/**
*
*/
class D2ItemLocationBody { class D2ItemLocationBody {
//body parts //body parts
/**
*
*/
const HELMET = 1; const HELMET = 1;
/**
*
*/
const AMULET = 2; const AMULET = 2;
/**
*
*/
const ARMOR = 3; const ARMOR = 3;
/**
*
*/
const WEAPONR = 4; const WEAPONR = 4;
/**
*
*/
const WEAPONL = 5; const WEAPONL = 5;
/**
*
*/
const RINGR = 6; const RINGR = 6;
/**
*
*/
const RINGL = 7; const RINGL = 7;
/**
*
*/
const BELT = 8; const BELT = 8;
/**
*
*/
const BOOTS = 9; const BOOTS = 9;
/**
*
*/
const GLOVES = 10; const GLOVES = 10;
/**
*
*/
const WEAPONR2 = 11; const WEAPONR2 = 11;
/**
*
*/
const WEAPONL2 = 12; const WEAPONL2 = 12;
} }

View File

@ -42,10 +42,21 @@
*/ */
/**
*
*/
class D2SaveTXT { class D2SaveTXT {
/**
* @var mixed
*/
public $path; public $path;
/**
* @param $file
* @param $data
* @return void
*/
public function save($file, $data) { public function save($file, $data) {
$fp = fopen($this->path.DIRECTORY_SEPARATOR.$file, 'a+'); $fp = fopen($this->path.DIRECTORY_SEPARATOR.$file, 'a+');
@ -54,6 +65,10 @@ class D2SaveTXT {
} }
/**
* @param $file
* @return void
*/
public function saveBackup($file){ public function saveBackup($file){
// if dir doesn't exist, create it // if dir doesn't exist, create it
@ -72,6 +87,10 @@ class D2SaveTXT {
} }
} }
/**
* @param $filename
* @return void
*/
public function saveTblEnries($filename) { public function saveTblEnries($filename) {
$post = $_POST; $post = $_POST;
@ -83,6 +102,9 @@ class D2SaveTXT {
file_put_contents($file, $str, FILE_APPEND); file_put_contents($file, $str, FILE_APPEND);
} }
/**
*
*/
public function __construct() { public function __construct() {
$this->path = TXT_PATH; $this->path = TXT_PATH;
} }

View File

@ -1,8 +1,17 @@
<?php <?php
/**
*
*/
class D2Strings { class D2Strings {
/**
* @var array
*/
public array $strings; public array $strings;
/**
*
*/
public function __construct(){ public function __construct(){
$sql = "SELECT * FROM strings"; $sql = "SELECT * FROM strings";
$this->strings = PDO_FetchAssoc($sql); $this->strings = PDO_FetchAssoc($sql);

View File

@ -41,19 +41,40 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/**
*
*/
class D2TxtParser { class D2TxtParser {
/**
* @var mixed
*/
public $path = TXT_PATH; public $path = TXT_PATH;
/**
* @var
*/
public $db; public $db;
/**
*
*/
public function __construct() { public function __construct() {
} }
/**
* @param $file
* @return array
*/
public function parseFile($file) { public function parseFile($file) {
return $this->parseData($file); return $this->parseData($file);
} }
/**
* @param $file
* @return array
*/
function filterProps($file) { function filterProps($file) {
$data = $this->parseData($file); $data = $this->parseData($file);
$propsToFilter = file(FILTER_PROPERTIES_FILE, FILE_IGNORE_NEW_LINES); $propsToFilter = file(FILTER_PROPERTIES_FILE, FILE_IGNORE_NEW_LINES);
@ -65,6 +86,10 @@ class D2TxtParser {
return $filteredProps; return $filteredProps;
} }
/**
* @param $file
* @return array
*/
public function parseData($file) { public function parseData($file) {
$file = $this->path . $file; $file = $this->path . $file;
$rows = array_map(function ($v) { $rows = array_map(function ($v) {

View File

@ -47,7 +47,7 @@
<div> <div>
<div class="center item_desc" style=""> <div class="center item_desc" style="">
<div style="height: 116px;background: url(/img/items/bg.png) center top no-repeat;"> <div style="height: 116px;background: url(/img/bg.png) center top no-repeat;">
<a href="" target="_blank" class="item_debug_link"> <a href="" target="_blank" class="item_debug_link">
<img style="" class="item img-fluid" src=""> <img style="" class="item img-fluid" src="">
</a> </a>

211
test.php
View File

@ -1,195 +1,42 @@
<?php <?php
// from unpack('H*', file_get_contents("Char.d2s")) for 3v4l.org to read d2s file here
//$data = hex2bin('55aa55aa60000000f1060000b5bc59b300000000536f72630000000000000000000000002400000001103863000000003cc7ae62ffffffffffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff000000000000570300000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000005e3e9b11000000008888d5f80100000035b70300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576f6f21060000002a0101000190fd9ffe9ffd9ffe9ffd9f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057530100000050000201ffffffff7f00000000000000000000000000000000000201ffffffff7f00000000000000000000000000000000000201ffffffff7f0000000000000000000000000000000000017734002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067660028083082000a06644000b10252067094c1018082800020292400480a0a0030c202008cc060dcc0f0ca3afa160a0000e03f696600000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000004a4d32004a4d100082006500069226160382af144fb080e03f4a4d1000820065000a9226260302791c00c480e03f4a4d10008000654415c076c606821c7ffff58080010a06fe034a4d1000800065008013268303827e82896086dccb000066c5bcfeffff014a4d1000820065000022f6860782b067b7ec80e03f4a4d1000820065001a922636038249b154da80a025040000c07f4a4d1000a0006500a442665363034a4d1000a0006500fe13367703024a4d1000a0006500de13362703024a4d1000a0006500be13361703024a4d1000a00065009e13360703024a4d1000a00065007e1336a707024a4d1000a00065005e13369707024a4d1000a00065003e1336f706024a4d1000a0006500c013e65606024a4d1000a00065006012d68607024a4d1000a00065000e12d60603024a4d1000a00065006412564306024a4d1000800065001e83179303029920441686e03f4a4d1000a0006500fe12d69606024a4d1000a0006500de12d68606024a4d1000a0006500be12d67606024a4d1000a00065009e12e67606024a4d1000a00065007e12e69606024a4d1000a0006500fc13e6a606024a4d1000a0006500dc13c68603024a4d1000a0006500bc13e6b606024a4d1000800065009c83272303826f9aed3186e03f4a4d1000800065007c8317730302acd0c26e86e03f4a4d1000a00065005c13560603024a4d1000a00065003c13569607024a4d1000a00065001c13464603024a4d1000a00065001212d66607024a4d1000a00865001412d66607024a4d1000a0006500fc1226f606024a4d1000a0006500dc12265607024a4d1000a0086500bc12265607024a4d1000a00865009c12265607024a4d1000a00865007c12265607024a4d1000a0086500fa13265607024a4d1000a0006500da43665363034a4d1000a0086500ba43665363034a4d1000a00065009a53468333064a4d10008000650016421616038245fc36c6822cff014a4d100080006500a032166707028c6e88690101000408052c30c0c00054e03f4a4d10008000650411c026c60602385900c78080010a08fe034a4d1000800065004632166707822c1b5e930001000498042c30c0c00054e03f4a4d1000800065007a8337130382a07c2a5786e03f4a4d1008800065000ec326c60602915c92ea8280010a07e23f4a4d100880046500b23237470612e9b107ed80104b83c140fc3776ece0f80f4a4d1000a00065180010365603024a4d00006a664a4d00006b6600');
require_once './src/D2Functions.php';
require_once './src/D2BitReader.php';
class D2ByteReader {
private string $data;
private int $offset = 0;
public function __construct(string $data) {
if (!$data)
return false;
$this->data = $data;
}
public function skip(int $numBytes): bool {
if ($numBytes < 0 || $numBytes > strlen($this->data))
return false;
$this->offset += $numBytes;
return $true;
}
public function seek(int $pos): bool {
if ($pos < 0 || $pos > strlen($this->data))
return false;
$this->offset = $pos;
return true;
}
public function readh(int $offset, int $numBytes, bool $str = true): string {
$this->seek($offset);
$bytes = null;
for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
$str ? $bytes .= $this->data[$i] : $bytes[] = $this->data[$i];
}
return unpack('H*', $bytes)[1];
}
public function readc(int $offset, int $numBytes, bool $str = true): array {
$this->seek($offset);
$bytes = null;
for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
$str ? $bytes .= $this->data[$i] : $bytes[] = $this->data[$i];
}
return unpack('C*', $bytes);
}
public function rewind(): bool {
$this->offset = 0;
return true;
}
public function writeByte(int $offset, int $byte) {
$this->data[$offset] = pack('C', $byte);
}
public function writeBytes(int $offset, string $bytes) {
if ($offset < 0 || $offset > strlen($this->data) || $bytes == '')
return false;
$_bytes = str_split($bytes, 2);
foreach ($_bytes as $k => $byte) {
$pos = $offset + $k;
$this->data[$pos] = pack('H*', $byte);
}
}
public function getData() {
return $this->data ? $this->data : false;
}
public function getOffset(): int {
return $this->offset;
}
public function isHexString(string $str): bool {
if (strlen($str) % 2 == 0 && (ctype_xdigit($str))) {
return true;
}
return false;
}
public function toBits($input): string {
$output = '';
if ($this->isHexString($input)) {
foreach (str_split($input, 2) as $byte) {
$output .= (strrev(str_pad(decbin(hexdec($byte)), 8, 0, STR_PAD_LEFT)));
}
return $output;
} else if (is_string($input)) {
foreach (str_split($input) as $i) {
$output .= strrev(str_pad(decbin(ord($i)), 8, 0, STR_PAD_LEFT));
}
return $output;
} else if (is_int($input)) {
return strrev(str_pad(decbin($input), 8, 0, STR_PAD_LEFT));
} else if (is_array($input)) {
foreach ($input as $i) {
$output .= $this->tobits($i);
}
return $output;
}
}
public function toBytes(string $bits) {
foreach (str_split($bits, 8) as $byteString) {
$bytes[] = (bindec(strrev($byteString)));
}
foreach ($bytes as $byte) {
dump($byte);
}
}
public function bitsToHexString(string $bits): string {
$bytes = '';
foreach (str_split($bits, 8) as $byte) {
$bytes .= (str_pad(dechex((bindec(strrev($byte)))), 2, 0, STR_PAD_LEFT));
}
return $bytes;
}
public function bitsToHexArray(string $bits): array {
$bytes = [];
foreach (str_split($bits, 8) as $byte) {
$bytes[] = (str_pad(dechex((bindec(strrev($byte)))), 2, 0, STR_PAD_LEFT));
}
return $bytes;
}
public function bitsToIntArray(string $bits): array {
$bytes = [];
foreach (str_split($bits, 8) as $byte) {
$bytes[] = (int) (str_pad(dechex((bindec(strrev($byte)))), 2, 0, STR_PAD_LEFT));
}
return $bytes;
}
/* /*
@return Byte with Nth bit set to X Diablo 2 D2S Save File Checksum Calculator
By Hash Borgir (https://www.psychedelicsdaily.com)
@date 6/4/2022
Checksum field is at byte 12.
Bytes 12/13/14/15 as a uint32.
Set this to 0.
After clearing the checksum field add up the values of all the bytes in the file and rotate the running total one bit to the left before adding the next byte.
*/ */
public function setBit(int $byte, int $pos, bool $bit) { function swapEndianness(string $hex) {
return ($bit ? ($byte | (1 << $pos)) : ($byte & ~(1 << $pos)) ); return implode('', array_reverse(str_split($hex, 2)));
} }
/* function checksum($fileData) {
@return Bit at Nth position in Byte $nSignature = 0;
*/ foreach ($fileData as $k => $byte) {
if ($k == 12 || $k == 13 || $k == 14 || $k == 15) {
public function getBit(int $byte, int $pos): int { $byte = 0;
return intval(($byte & (1 << $pos)) != 0); }
$nSignature = ((($nSignature << 1) | ($nSignature >> 31)) + $byte & 0xFFFFFFFF);
}
return swapEndianness(str_pad(dechex($nSignature), 8, 0, STR_PAD_LEFT));
} }
} $filename = "D:\Diablo II\MODS\ironman-dev\save\Necro.d2s";
$fp = fopen($filename, "r+b");
fseek($fp, 12); // go to byte 12
fwrite($fp, pack('I', 0)); // clear the checksum field uInt32
$filename = "D:\Diablo II\MODS\ironman-dev\save\Barb.d2s";
$fileData = unpack('C*', file_get_contents($filename)); // open file and unpack $fileData = unpack('C*', file_get_contents($filename)); // open file and unpack
var_dump(checksum($fileData)); var_dump(checksum($fileData));
fseek($fp, 12); // go to byte 12
fwrite($fp, pack('H8', checksum($fileData))); // write new checksum
fclose($fp);
//$file = "D:\Diablo II\MODS\ironman-dev\save\Barb.d2s";
//$data = file_get_contents($file);
//
//$c = new D2ByteReader($data);
//
//$checksum = checksum(unpack('C*', $c->getData()));
//
//ddump($checksum);
//$bytes = $c->readh(643, 5);
//$bits = $c->toBits($bytes);
//
//$bits[4] = 0;
//$bits[5] = 0;
//$bits[8] = 0;
//$newBytes = $c->bitsToHexString($bits);
//$c->writeBytes(643, $newBytes);
//
//$c->writeBytes(12, "00000000"); // zero old checksum
//
//dump($c->readh(12, 4));
//
//$newChecksum = checksum(unpack('C*', $c->getData())); // get new checksum
//$c->writeBytes(12, $newChecksum); // write new checksum
//
//dump($c->readh(12, 4));
//
//file_put_contents($file, $c->getData()); // write bytestream to file