mirror of
https://gitlab.com/hashborgir/d2tools.git
synced 2025-09-18 01:22:08 +00:00
Checksum code fixed. Added DocBlocks to D2Classes. TODO: Fill in docblocks, refactor, code cleanup
This commit is contained in:
219
test.php
219
test.php
@@ -1,195 +1,42 @@
|
||||
<?php
|
||||
|
||||
// from unpack('H*', file_get_contents("Char.d2s")) for 3v4l.org to read d2s file here
|
||||
//$data = hex2bin('55aa55aa60000000f1060000b5bc59b300000000536f72630000000000000000000000002400000001103863000000003cc7ae62ffffffffffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff000000000000570300000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000005e3e9b11000000008888d5f80100000035b70300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576f6f21060000002a0101000190fd9ffe9ffd9ffe9ffd9f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057530100000050000201ffffffff7f00000000000000000000000000000000000201ffffffff7f00000000000000000000000000000000000201ffffffff7f0000000000000000000000000000000000017734002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067660028083082000a06644000b10252067094c1018082800020292400480a0a0030c202008cc060dcc0f0ca3afa160a0000e03f696600000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000004a4d32004a4d100082006500069226160382af144fb080e03f4a4d1000820065000a9226260302791c00c480e03f4a4d10008000654415c076c606821c7ffff58080010a06fe034a4d1000800065008013268303827e82896086dccb000066c5bcfeffff014a4d1000820065000022f6860782b067b7ec80e03f4a4d1000820065001a922636038249b154da80a025040000c07f4a4d1000a0006500a442665363034a4d1000a0006500fe13367703024a4d1000a0006500de13362703024a4d1000a0006500be13361703024a4d1000a00065009e13360703024a4d1000a00065007e1336a707024a4d1000a00065005e13369707024a4d1000a00065003e1336f706024a4d1000a0006500c013e65606024a4d1000a00065006012d68607024a4d1000a00065000e12d60603024a4d1000a00065006412564306024a4d1000800065001e83179303029920441686e03f4a4d1000a0006500fe12d69606024a4d1000a0006500de12d68606024a4d1000a0006500be12d67606024a4d1000a00065009e12e67606024a4d1000a00065007e12e69606024a4d1000a0006500fc13e6a606024a4d1000a0006500dc13c68603024a4d1000a0006500bc13e6b606024a4d1000800065009c83272303826f9aed3186e03f4a4d1000800065007c8317730302acd0c26e86e03f4a4d1000a00065005c13560603024a4d1000a00065003c13569607024a4d1000a00065001c13464603024a4d1000a00065001212d66607024a4d1000a00865001412d66607024a4d1000a0006500fc1226f606024a4d1000a0006500dc12265607024a4d1000a0086500bc12265607024a4d1000a00865009c12265607024a4d1000a00865007c12265607024a4d1000a0086500fa13265607024a4d1000a0006500da43665363034a4d1000a0086500ba43665363034a4d1000a00065009a53468333064a4d10008000650016421616038245fc36c6822cff014a4d100080006500a032166707028c6e88690101000408052c30c0c00054e03f4a4d10008000650411c026c60602385900c78080010a08fe034a4d1000800065004632166707822c1b5e930001000498042c30c0c00054e03f4a4d1000800065007a8337130382a07c2a5786e03f4a4d1008800065000ec326c60602915c92ea8280010a07e23f4a4d100880046500b23237470612e9b107ed80104b83c140fc3776ece0f80f4a4d1000a00065180010365603024a4d00006a664a4d00006b6600');
|
||||
/*
|
||||
Diablo 2 D2S Save File Checksum Calculator
|
||||
By Hash Borgir (https://www.psychedelicsdaily.com)
|
||||
@date 6/4/2022
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
public function setBit(int $byte, int $pos, bool $bit) {
|
||||
return ($bit ? ($byte | (1 << $pos)) : ($byte & ~(1 << $pos)) );
|
||||
}
|
||||
|
||||
/*
|
||||
@return Bit at Nth position in Byte
|
||||
*/
|
||||
|
||||
public function getBit(int $byte, int $pos): int {
|
||||
return intval(($byte & (1 << $pos)) != 0);
|
||||
}
|
||||
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)));
|
||||
}
|
||||
|
||||
$filename = "D:\Diablo II\MODS\ironman-dev\save\Barb.d2s";
|
||||
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));
|
||||
|
||||
|
||||
|
||||
|
||||
//$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
|
||||
|
||||
fseek($fp, 12); // go to byte 12
|
||||
fwrite($fp, pack('H8', checksum($fileData))); // write new checksum
|
||||
fclose($fp);
|
Reference in New Issue
Block a user