mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2024-11-30 04:26:03 +00:00
Working copy
This commit is contained in:
parent
eb28039f88
commit
dd662e26f6
@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/docs/ironman-dev_backup" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,9 +1,5 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ERROR | E_PARSE);
|
||||
set_time_limit(-1);
|
||||
ini_set('max_input_time', '-1');
|
||||
ini_set('max_execution_time', '0');
|
||||
session_start();
|
||||
ob_start();
|
||||
|
||||
@ -16,6 +12,11 @@ require_once './src/D2BitReader.php';
|
||||
require_once './src/D2Item.php';
|
||||
require_once './src/D2Char.php';
|
||||
|
||||
error_reporting(E_ERROR | E_PARSE);
|
||||
set_time_limit(-1);
|
||||
ini_set('max_input_time', '-1');
|
||||
ini_set('max_execution_time', '0');
|
||||
|
||||
define('DB_FILE', $_SESSION['modname'] . ".db");
|
||||
PDO_Connect("sqlite:" . DB_FILE);
|
||||
|
||||
@ -42,10 +43,11 @@ foreach ($ISCData as $k => $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']);
|
||||
|
114
D2Reviver.php
Normal file
114
D2Reviver.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?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?";
|
||||
}
|
BIN
bin/D2SC.exe
Normal file
BIN
bin/D2SC.exe
Normal file
Binary file not shown.
@ -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)) {
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; URL=/" />
|
||||
<meta http-equiv="refresh" content="0; URL=/" />
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
42
test.php
42
test.php
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
function swapEndianness(string $hex) {
|
||||
return implode('', array_reverse(str_split($hex, 2)));
|
||||
}
|
||||
|
||||
function checksum($fileData) {
|
||||
$nSignature = 0;
|
||||
foreach ($fileData as $k => $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);
|
Loading…
Reference in New Issue
Block a user