d2tools/D2Reviver.php

114 lines
5.2 KiB
PHP
Raw Permalink Normal View History

2023-05-30 18:06:58 +00:00
<?php
require_once './src/D2Functions.php';
require_once './src/D2ByteReader.php';
require_once './src/D2BitReader.php';
$str = <<<EOT
__ __
`."._ _,",'
`. "-----, .-----" ,'
`. / \ ,'
\------. _ | |__ ,-.__L | _ _ ,---.
\ .--. \ \'-, | ,',.'. \ ._ \ | \'-'/ / .-. \
| | | | | | | J /__\ L | |_)( | | | f / \ l
| | | | | | | | | ] ] | .-. \ | | | , t \ / j
/ '--' / ,'_'. |.'_'. F F / |__)/ | / '--'/ \ '-' /
/_,----' " " | " " /,' "-----' | "-----' '---'
(' | |'" | |
_,-" "-._ _,-" "-._ (C) HCB https://im.stoned.io
<_____________> <_____________> 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 "<pre>$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 "<pre>$str\n\n\nCharacter is not dead. Have you been taking mushrooms lately?";
}