diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..ded57de Binary files /dev/null and b/.DS_Store differ diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..b681626 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/CharEditor.php b/CharEditor.php index a04b751..0c28dba 100644 --- a/CharEditor.php +++ b/CharEditor.php @@ -1,9 +1,5 @@ $v) { //$filePath = "D:\Diablo II\MODS\MedianXL2012\save\Test.d2s"; -$filePath = "Necro.d2s"; +$filePath = "Test.d2s"; $char = new D2Char($filePath); -$char->setChar("CharacterStatus", "Died", 0); -$char->setChar("CharacterStatus", "Hardcore", 1); + +//$char->setChar("CharacterStatus", "Died", 0); +//$char->setChar("CharacterStatus", "Hardcore", 1); //$char->setChar("CharacterStatus", "Expansion", 1); //$char->setChar("LeftmousebuttonskillID", 223); @@ -53,13 +55,21 @@ $char->setAllSkills(1); //$char->setSkill(1, 99); //$char->setChar("CharacterClass", "Necromancer"); // 127 //$char->setChar("CharacterProgression", 1); // 0 in normal, 1 finished normal, 2 finished nm, 3 finished hell -$char->setChar("CharacterLevel", 25); -//$char->setStat("strength", 70); -//$char->setStat("energy", 70); -//$char->setStat("dexterity", 70); -//$char->setStat("vitality", 70); -//$char->setStat("mana", 200); -//$char->setStat("maxmana", 200); + +//$char->setChar("CharacterStatus", "Died", 1); +$char->setChar("CharacterLevel", 99); +$char->setStat("strength", 270); +$char->setStat("energy", 270); +$char->setStat("dexterity", 270); +$char->setStat("vitality", 270); +$char->setStat("hitpoints", 100); +$char->setStat("maxhp", 150); +$char->setStat("stamina", 280); +$char->setStat("maxstamina", 290); +$char->setStat("mana", 70); +$char->setStat("maxmana", 200); + + //$char->setStat("soulcounter", 80); // //$char->setStat("hitpoints", 70); @@ -77,4 +87,6 @@ $char->setChar("CharacterLevel", 25); unset($char); // destroy $char so we can read it again after writing to it to get updated stats $char = new D2Char($filePath); -var_dump($char->cData); + +var_dump($char->cData['CharacterStatus']); +var_dump($char->cData['stats']); diff --git a/D2Reviver.php b/D2Reviver.php new file mode 100644 index 0000000..743b62c --- /dev/null +++ b/D2Reviver.php @@ -0,0 +1,114 @@ + <_____________> https://d2mods.org +EOT; +$cbits = [ + 0 => 10, 1 => 10, 2 => 10, 3 => 10, 4 => 10, 5 => 8, 6 => 21, 7 => 21, 8 => 21, 9 => 21, 10 => 21, + 11 => 21, 12 => 7, 13 => 32, 14 => 25, 15 => 25, 37 => 31, 39 => 31, 41 => 31, 43 => 31, 45 => 31, + 183 => 31, 184 => 31, 185 => 31]; // CSvBits to skip + +$exp = [ + 0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715, 72144, 90180, 112725, 140906, 176132, 220165, 275207, 344008, + 430010, 537513, 671891, 839864, 1049830, 1312287, 1640359, 2050449, 2563061, 3203826, 3902260, 4663553, 5493363, 6397855, + 7383752, 8458379, 9629723, 10906488, 12298162, 13815086, 15468534, 17270791, 19235252, 21376515, 23710491, 26254525, 29027522, + 32050088, 35344686, 38935798, 42850109, 47116709, 51767302, 56836449, 62361819, 68384473, 74949165, 82104680, 89904191, 98405658, + 107672256, 117772849, 128782495, 140783010, 153863570, 168121381, 183662396, 200602101, 219066380, 239192444, 261129853, 285041630, + 311105466, 339515048, 370481492, 404234916, 441026148, 481128591, 524840254, 572485967, 624419793, 681027665, 742730244, 809986056, + 883294891, 963201521, 1050299747, 1145236814, 1248718217, 1361512946, 1484459201, 1618470619, 1764543065, 1923762030, 2097310703, + 2286478756, 2492671933, 2717422497, 2962400612, 3229426756, 3520485254, 3837739017]; + +//$file = "D:\Diablo II\MODS\ironman-dev\save\Necro.d2s"; +$file = $argv[1]; +if (!file_exists($file)) + die("$file does not exist!"); + +// create ByteReader object from file binary data +$BR = new D2ByteReader(file_get_contents($file)); +$data = $BR->getData(); + +// offset 36 +// 7 6 5 4 3 2 1 0 +// unknown Expansion Character unknown Died Hardcore unknown +$statusByte = unpack('C', $data[36])[1]; +$dBit = getBit($statusByte, 3); + +// if Character is dead, then make it alive, reduce 5 levels as penalty + +if ($dBit) { + // using {} for better readability + { + $gf = strposX($data, 'gf', 1) + 2; // find gf and skip it + $if = strposX($data, 'if', 1); + $len = $if - $gf; + // create bitstream + $stats = new D2BitReader($BR->toBits($BR->readh($gf, $len))); + // remove 0x1FF from end + $bits = $stats->getBits(); + $cleanbits = substr($bits, 0, -11); + $stats->setBits($cleanbits); + $bits = $stats->getBits(); + // rewind bitstream + $stats->rewind(); + // get bit offsets for each stat + $offsets = []; + for ($i = 0; $i < count($cbits); $i++) { + $id = hexdec($BR->toBytesR($stats->readb(9))); + $offsets[$id] = $stats->getOffset(); + $stats->skip($cbits[$id]); + } + $offsets[0] = 9; + // get current character level + $level = unpack('C', $data[43])[1]; + $stats->seek($offsets[12]); + $_level = hexdec($BR->toBytesR($stats->readb(7))); + // reduce current character level by 5, no penalty if < 6 + if ($_level >= 80) { + $level -= 10; + } else if ($_level >= 5 || $level <= 80) { + $level -= 5; + } else if ($_level <= 5) { + $level = 1; + } + } + $data[43] = pack('C', $level); + $statusByteNew = setBit($statusByte, 3, 0); // make character alive + $data[36] = pack('C', $statusByteNew); // make character alive + + $bitsToWriteLevel = strrev(str_pad(decbin(intval($level)), $cbits[12], 0, STR_PAD_LEFT)); + $bitsToWriteExp = strrev(str_pad(decbin(intval($exp[$level])), $cbits[13], 0, STR_PAD_LEFT)); + + $_newBitsTemp = $stats->writeBits($bits, $bitsToWriteLevel, $offsets[12]); + $newBits = $stats->writeBits($_newBitsTemp, $bitsToWriteExp, $offsets[13]) . "11111111100"; + $bytes = $BR->toBytes($newBits); + + $BR->setData($data); + $BR->writeBytes($gf, $bytes); + $BR->writeBytes(12, "00000000"); + $checksum = checksum(unpack('C*', $BR->getData())); + $BR->writeBytes(12, $checksum); + $data = $BR->getData(); + file_put_contents($file, $data); + + echo "
$str\n\n\nCharacter is now alive. Go take some mushrooms!\n";
+    echo "Previous Level: $_level\n";
+    echo "Revive Penalty: " . $_level - $level . "\n";
+    echo "New Level: $level\n";
+} else {
+    echo "
$str\n\n\nCharacter is not dead. Have you been taking mushrooms lately?";
+}
\ No newline at end of file
diff --git a/bin/D2SC.exe b/bin/D2SC.exe
new file mode 100644
index 0000000..8b2dfed
Binary files /dev/null and b/bin/D2SC.exe differ
diff --git a/processFiles.php b/processFiles.php
index 25605e4..a9bfcd8 100755
--- a/processFiles.php
+++ b/processFiles.php
@@ -107,14 +107,13 @@ if (file_exists(APP_DB)) {
     processFilesManuallyInSqlite();
     processFilesManuallyInSqliteLinux();
 
-    
     function toPngAll() {
         $q = realpath("bin\qdc6.exe");
-        $iPath = $_SESSION['modpath']."\data\global\items";
-        $oPath = realpath("docs\\{$_SESSION['modname']}\img\\items");       
-        exec("$q --first-frame-only \"$iPath\" -o \"$oPath\"");    
-    }    
-    
+        $iPath = $_SESSION['modpath'] . "\data\global\items";
+        $oPath = realpath("docs\\{$_SESSION['modname']}\img\\items");
+        exec("$q --first-frame-only \"$iPath\" -o \"$oPath\"");
+    }
+
     if (!is_dir("docs\\{$_SESSION['modname']}")) {
         mkdir("docs\\{$_SESSION['modname']}");
         mkdir("docs\\{$_SESSION['modname']}\\fonts", 0777, TRUE);
@@ -124,36 +123,38 @@ if (file_exists(APP_DB)) {
     rcopy(realpath('docs/template/fonts'), realpath("docs\\{$_SESSION['modname']}\\fonts"));
     rcopy(realpath('docs/template/img'), realpath("docs\\{$_SESSION['modname']}\\img"));
     rcopy(realpath('docs/template/res'), realpath("docs\\{$_SESSION['modname']}\\res"));
-    
+
     toPngAll();
-    
-    
-    
+
     /*
       Process tbl files
-     *
-     *
-    */
+     */
     $tbl = $_SESSION['tbl'];
-
     // ddump($tbl);
-
     /*
       $string = D2Tbl::getStrings($tbl."string.tbl");
       $stringExpansion = D2Tbl::getStrings($tbl."expansionstring.tbl");
       $stringPatch = D2Tbl::getStrings($tbl."patchstring.tbl");
-
       $strings = array_merge($stringPatch, $stringExpansion, $string);
      */
-
     $strs = ["dummy" => "dummy"];
     foreach (glob($tbl . "*.tbl") as $filename) {
         $strings = D2Tbl::getStrings($filename);
         if (!empty($strings))
             $strs = array_merge($strs, $strings);
     }
-
     $db->writeTbl($strs);
+
+    // write each table to invidual db table
+    $tbl = $_SESSION['tbl'];
+    foreach (glob($tbl . "*.tbl") as $filename) {
+        $tblName = pathinfo($filename)['filename'];
+        $strings = D2Tbl::getStrings($filename);
+        if (!empty($strings)) {
+            $db->writeTbls($tblName, $strings);
+        }
+        
+    }
 } else {
     // if config db does not exist, go to configure page
     header("Location: /src/D2Config.php");
@@ -167,7 +168,7 @@ if (file_exists(APP_DB)) {
 
 
     
-       
+        
     
     
     
diff --git a/src/D2BitReader.php b/src/D2BitReader.php
index 70c61a4..861c340 100644
--- a/src/D2BitReader.php
+++ b/src/D2BitReader.php
@@ -54,6 +54,7 @@ class D2BitReader {
      * @return array|string|string[]
      */
     public function writeBits(string $bits, string $bitsToWrite, int $offset) {
+        //$this->bits = substr_replace($bits, $bitsToWrite, $offset, strlen($bitsToWrite));
         return substr_replace($bits, $bitsToWrite, $offset, strlen($bitsToWrite));
     }
 
@@ -63,7 +64,7 @@ class D2BitReader {
      * @return string
      */
     public function readb(int $numBits = 0, bool $str = true): string {
-        $bits = null;
+        $bits = '';
         for ($i = $this->offset; $i < $this->offset + $numBits; $i++) {
             $str ? $bits .= $this->bits[$i] : $bits[] = $this->bits[$i];
         }
diff --git a/src/D2ByteReader.php b/src/D2ByteReader.php
index 4c087bd..35e62ff 100644
--- a/src/D2ByteReader.php
+++ b/src/D2ByteReader.php
@@ -54,7 +54,22 @@ class D2ByteReader {
      * @param bool $str
      * @return string
      */
-    public function readh(int $offset, int $numBytes, bool $str = true): string {
+    public function read(int $offset, int $numBytes) {
+        $this->seek($offset);
+        $bytes = null;
+        for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
+            $bytes .= $this->data[$i];
+        }
+        return $bytes;
+    }
+    
+    /**
+     * @param int $offset
+     * @param int $numBytes
+     * @param bool $str
+     * @return string
+     */
+    public function readh(int $offset, int $numBytes, bool $str = true) {
         $this->seek($offset);
         $bytes = null;
         for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
@@ -62,14 +77,14 @@ class D2ByteReader {
         }
         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) {
         $this->seek($offset);
         $bytes = null;
         for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
@@ -187,6 +202,7 @@ class D2ByteReader {
      * @return string
      */
     public function toBytesR(string $bits) : string {
+        $bytes = '';
         foreach (str_split($bits, 8) as $byteString) {
             $bytes .= strtoupper(str_pad(dechex(bindec(($byteString))), 2, 0, STR_PAD_LEFT));
         }
diff --git a/src/D2Char.php b/src/D2Char.php
index a4fdc75..5f8c6f7 100644
--- a/src/D2Char.php
+++ b/src/D2Char.php
@@ -219,7 +219,7 @@ WHERE sk.charclass = '$class'";
         unset($this->items);
         //unset($this->bData);
         unset($this->sData);
-        unset($this->fp);
+        //unset($this->fp);
 
         return $this->cData;
     }
@@ -292,7 +292,7 @@ WHERE sk.charclass = '$class'";
         $bytes = $this->ByteReader->toBytes($bits);
 
         $stats->rewind();
-        for ($i = 0; $i <= strlen($bits); $i++) {
+        for ($i = 0; $i <= count($this->ISC); $i++) {
             $id = hexdec($this->ByteReader->toBytesR($stats->readb(9)));
             if ($this->ISC[$id]['CSvBits'] !== NULL && $this->ISC[$id]['CSvBits'] !== '') {
                 $stats->skip($this->ISC[$id]['CSvBits']);
@@ -314,8 +314,8 @@ WHERE sk.charclass = '$class'";
         $values['maxmana'] = (int) round($values['maxmana'] >> 11);
         $values['stamina'] = (int) round($values['stamina'] >> 11);
         $values['maxstamina'] = (int) round($values['maxstamina'] >> 11);
-        $values['soulcounter'] = (int) round($values['soulcounter'] / 2);
-        $values['killcounter'] = (int) round($values['killcounter'] / 2);
+//        $values['soulcounter'] = (int) round($values['soulcounter'] / 2);
+//        $values['killcounter'] = (int) round($values['killcounter'] / 2);
 
         $this->cData['stats'] = $values;
     }
@@ -463,7 +463,7 @@ WHERE sk.charclass = '$class'";
         }
         $stats .= "000011111111100";
 
-        dump($stats);
+        //dump($stats);
 
         $gf = strposX($this->data, 'gf', 1) + 2; // find gf and skip it
         $if = strposX($this->data, 'if', 1);
diff --git a/src/D2Database.php b/src/D2Database.php
index 80ac05f..1bd728b 100755
--- a/src/D2Database.php
+++ b/src/D2Database.php
@@ -140,6 +140,24 @@ class D2Database {
 		$res = PDO_Execute($sql);
 	}
 
+    /**
+     * @param $data
+     * @return void
+     */
+    public function writeTbls($table,$data) {
+		$sql = "CREATE TABLE IF NOT EXISTS `$table` (`Key` VARCHAR(255), `String` VARCHAR(255));";
+		$res = PDO_Execute($sql);
+
+		$sql = "INSERT INTO `$table` (`Key`,`String`) VALUES ";
+		foreach ($data as $k => $v) {
+			$sql .= "(\"$k\",\"$v\"),";
+		}
+		$sql = rtrim($sql, ", ");
+		$sql .= ";";
+
+		$res = PDO_Execute($sql);
+	}
+    
     /**
      * @param $key
      * @return mixed
diff --git a/test.php b/test.php
deleted file mode 100644
index 3c1c5e6..0000000
--- a/test.php
+++ /dev/null
@@ -1,42 +0,0 @@
- $byte) {
-        if ($k == 12 || $k == 13 || $k == 14 || $k == 15) {
-            $byte = 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
-
-$fileData = unpack('C*', file_get_contents($filename)); // open file and unpack
-
-
-var_dump(checksum($fileData));
-
-fseek($fp, 12); // go to byte 12
-fwrite($fp, pack('H8', checksum($fileData))); // write new checksum
-fclose($fp);
\ No newline at end of file