d2tools/src/D2ByteReader.php
2023-06-04 23:01:07 -06:00

361 lines
16 KiB
PHP

<?php
require_once './src/D2Functions.php';
require_once './src/D2BitReader.php';
/**
* A class for reading and manipulating bytes.
*/
class D2ByteReader {
/**
* @var string The byte data.
*/
private string $data = '';
/**
* @var int The current offset position.
*/
private int $offset = 0;
/**
* Constructs a new D2ByteReader instance.
*
* @param string $data The byte data to read.
*/
public function __construct(string $data) {
if (!$data) { // Check if the provided data is empty or false
return false; // Return false if the data is empty or false
}
$this->data = $data; // Assign the provided data to the internal property "data"
}
/**
* Skips a specified number of bytes.
*
* @param int $numBytes The number of bytes to skip.
* @return bool Returns true on success, false otherwise.
*/
public function skip(int $numBytes): bool {
if ($numBytes < 0 || $numBytes > strlen($this->data)) { // Check if the specified number of bytes is within the valid range
return false; // Return false if the number of bytes is invalid
}
$this->offset += $numBytes; // Increment the internal offset by the specified number of bytes
return true; // Return true to indicate a successful skip operation
}
/**
* Moves the offset position to a specified position.
*
* @param int $pos The new offset position.
* @return bool Returns true if the offset position is valid, false otherwise.
*/
public function seek(int $pos): bool {
if ($pos < 0 || $pos > strlen($this->data)) { // Check if the specified position is within the valid range
return false; // Return false if the position is invalid
}
$this->offset = $pos; // Set the internal offset to the specified position
return true; // Return true to indicate a successful seek operation
}
/**
* Reads a specified number of bytes from the current offset position.
*
* @param int $offset The offset position to start reading from.
* @param int $numBytes The number of bytes to read.
* @param bool $str Indicates whether to return the result as a string (default) or an array.
* @return string|array The read bytes.
*/
public function read(int $offset, int $numBytes, bool $str = true) {
$this->seek($offset); // Set the internal offset to the specified position
$bytes = null; // Initialize a variable to store the read bytes
// Loop through the specified number of bytes
for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
// Depending on the $str parameter, concatenate the byte to $bytes as a string or add it to $bytes array
$str ? $bytes .= $this->data[$i] : $bytes[] = $this->data[$i];
}
return $bytes; // Return the read bytes as either a string or an array
}
/**
* Reads a specified number of bytes from the current offset position and returns them as a hex string.
*
* @param int $offset The offset position to start reading from.
* @param int $numBytes The number of bytes to read.
* @param bool $str Indicates whether to return the result as a string (default) or an array.
* @return string The read bytes as a hex string.
*/
public function readh(int $offset, int $numBytes, bool $str = true) {
$this->seek($offset); // Set the internal offset to the specified position
$bytes = null; // Initialize a variable to store the read bytes
// Loop through the specified number of bytes
for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
// Depending on the $str parameter, concatenate the byte to $bytes as a string or add it to $bytes array
$str ? $bytes .= $this->data[$i] : $bytes[] = $this->data[$i];
}
return unpack('H*', $bytes)[1]; // Unpack the bytes into a hexadecimal string and return it
}
/**
* Reads a specified number of bytes from the current offset position and returns them as an array of integers.
*
* @param int $offset The offset position to start reading from.
* @param int $numBytes The number of bytes to read.
* @param bool $str Indicates whether to return the result as a string (default) or an array.
* @return array The read bytes as an array of integers.
*/
public function readc(int $offset, int $numBytes, bool $str = true) {
$this->seek($offset); // Set the internal offset to the specified position
$bytes = null; // Initialize a variable to store the read bytes
// Loop through the specified number of bytes
for ($i = $this->offset; $i < $this->offset + $numBytes; $i++) {
// Depending on the $str parameter, concatenate the byte to $bytes as a string or add it to $bytes array
$str ? $bytes .= $this->data[$i] : $bytes[] = $this->data[$i];
}
return unpack('C*', $bytes); // Unpack the bytes into an array of unsigned characters
}
/**
* Rewinds the offset position to the beginning.
*
* @return bool Returns true on success.
*/
public function rewind(): bool {
$this->offset = 0; // Set the offset property to 0, indicating the start position
return true; // Return true to indicate that the rewind operation was successful
}
/**
* Writes a byte at the specified offset position.
*
* @param int $offset The offset position to write the byte.
* @param int $byte The byte value to write.
* @return void
*/
public function writeByte(int $offset, int $byte) {
$this->data[$offset] = pack('C', $byte);
}
/**
* Writes multiple bytes starting from the specified offset position.
*
* @param int $offset The offset position to start writing from.
* @param string $bytes The bytes to write.
* @return false|void Returns false on failure or void on success.
*/
public function writeBytes(int $offset, string $bytes) {
if ($offset < 0 || $offset > strlen($this->data) || $bytes == '') {
return false; // Return false if the offset is invalid or the bytes string is empty
}
$_bytes = str_split($bytes, 2); // Split the bytes string into an array of two-character chunks
foreach ($_bytes as $k => $byte) {
$pos = $offset + $k; // Calculate the position in the data to write the byte
$this->data[$pos] = pack('H*', $byte); // Pack the hexadecimal byte into binary format and write it at the specified position
}
}
/**
* Inserts bytes at the specified offset position.
*
* @param int $offset The offset position to insert the bytes.
* @param string $bytes The bytes to insert.
* @return false|void Returns false on failure or void on success.
*/
public function insertBytes(int $offset, string $bytes) {
if ($offset < 0 || $offset > strlen($this->data) || $bytes == '') {
return false; // Return false if the offset is invalid or the bytes string is empty
}
$data = bin2hex($this->data); // Convert the binary data to a hexadecimal string
$newData = substr_replace($data, $bytes, $offset, 0); // Insert the new bytes at the specified offset in the hexadecimal string
$this->data = hex2bin($newData); // Convert the modified hexadecimal string back to binary data and update the 'data' property
}
/**
* Returns the byte data.
*
* @return false|string Returns the byte data on success, or false if no data is set.
*/
public function getData() {
return $this->data ? $this->data : false; // Return the value of the 'data' property if it exists, otherwise return false
}
/**
* Sets the byte data.
*
* @param $data The byte data to set.
* @return void
*/
public function setData($data) {
$this->data = $data; // Set the value of the 'data' property to the provided data
}
/**
* Returns the current offset position.
*
* @return int The current offset position.
*/
public function getOffset(): int {
return $this->offset; // Return the value of the 'offset' property
}
/**
* Checks if a string is a valid hexadecimal string.
*
* @param string $str The string to check.
* @return bool Returns true if the string is a valid hexadecimal string, false otherwise.
*/
public function isHexString(string $str): bool {
// Check if the length of the string is even and if all characters are hexadecimal using ctype_xdigit function
if (strlen($str) % 2 == 0 && ctype_xdigit($str)) {
return true; // Return true if the string is a valid hexadecimal string
}
return false; // Return false if the string is not a valid hexadecimal string
}
/**
* Converts input to a string of bits.
*
* @param mixed $input The input to convert.
* @return string The converted string of bits.
*/
public function toBits($input): string {
$output = ''; // Initialize an empty string to store the resulting bits
// Check if the input is a hexadecimal string
if ($this->isHexString($input)) {
// Convert each byte of the hexadecimal string to binary and concatenate the bits to the output
// Split the hexadecimal string into an array of bytes (2 characters per byte)
foreach (str_split($input, 2) as $byte) {
// Convert the byte from hexadecimal to decimal using `hexdec` function,
// then convert the decimal to binary using `decbin` function,
// reverse the resulting binary string using `strrev` function,
// and pad the binary string with leading zeros to make it 8 bits long using `str_pad` function.
// Finally, concatenate the resulting bits to the output string.
$output .= (strrev(str_pad(decbin(hexdec($byte)), 8, 0, STR_PAD_LEFT)));
}
return $output; // Return the resulting bits
} else if (is_string($input)) {
// Convert each character of the string to binary and concatenate the bits to the output
// Split the string into an array of characters
foreach (str_split($input) as $i) {
// Convert the character to its ASCII value using `ord` function,
// then convert the ASCII value to binary using `decbin` function,
// reverse the resulting binary string using `strrev` function,
// and pad the binary string with leading zeros to make it 8 bits long using `str_pad` function.
// Finally, concatenate the resulting bits to the output string.
$output .= strrev(str_pad(decbin(ord($i)), 8, 0, STR_PAD_LEFT));
}
return $output; // Return the resulting bits
} else if (is_int($input)) {
// Convert the integer to binary and return the resulting bits
// Convert the integer to binary using `decbin` function,
// reverse the resulting binary string using `strrev` function,
// and pad the binary string with leading zeros to make it 8 bits long using `str_pad` function.
// Finally, return the resulting bits.
return strrev(str_pad(decbin($input), 8, 0, STR_PAD_LEFT));
} else if (is_array($input)) {
// If the input is an array, recursively call the `toBits` function for each element and concatenate the bits to the output
// Iterate over each element in the input array
foreach ($input as $i) {
// Recursively call the `toBits` function for the current element,
// and concatenate the resulting bits to the output string.
$output .= $this->toBits($i);
}
return $output; // Return the resulting bits
}
}
/**
* Converts a string of bits to bytes (reversing the bit order).
*
* @param string $bits The string of bits to convert.
* @return string The converted bytes.
*/
public function toBytesR(string $bits): string {
$bytes = ''; // Initialize an empty string to store the resulting bytes
// Split the bits string into an array of byte-sized chunks (8 bits per chunk)
foreach (str_split($bits, 8) as $byteString) {
// Convert each byte-sized chunk from binary to decimal using `bindec` function,
// then convert the decimal to hexadecimal using `dechex` function,
// and pad the resulting hexadecimal string with leading zeros to make it 2 characters long using `str_pad` function.
// Finally, concatenate the resulting byte to the bytes string.
$bytes .= strtoupper(str_pad(dechex(bindec($byteString)), 2, 0, STR_PAD_LEFT));
}
return $bytes; // Return the resulting bytes
}
/**
* Converts a string of bits to bytes.
*
* @param string $bits The string of bits to convert.
* @return string The converted bytes.
*/
public function toBytes(string $bits): string {
$bytes = ''; // Initialize an empty string to store the resulting bytes
// Split the bits string into an array of byte-sized chunks (8 bits per chunk)
foreach (str_split($bits, 8) as $byteString) {
// Reverse the order of bits within each byte-sized chunk using `strrev` function,
// then convert the reversed binary string to decimal using `bindec` function,
// then convert the decimal to hexadecimal using `dechex` function,
// and pad the resulting hexadecimal string with leading zeros to make it 2 characters long using `str_pad` function.
// Finally, concatenate the resulting byte to the bytes string.
$bytes .= strtoupper(str_pad(dechex(bindec(strrev($byteString))), 2, 0, STR_PAD_LEFT));
}
return $bytes; // Return the resulting bytes
}
/**
* Converts a string of bits to a hexadecimal string.
*
* @param string $bits The string of bits to convert.
* @return string The converted hexadecimal string.
*/
public function bitsToHexString(string $bits): string {
$bytes = ''; // Initialize an empty string to store the resulting hexadecimal bytes
// Split the bits string into an array of byte-sized chunks (8 bits per chunk)
foreach (str_split($bits, 8) as $byte) {
// Reverse the order of bits within each byte-sized chunk using `strrev` function,
// then convert the reversed binary string to decimal using `bindec` function,
// then convert the decimal to hexadecimal using `dechex` function,
// and pad the resulting hexadecimal string with leading zeros to make it 2 characters long using `str_pad` function.
// Finally, concatenate the resulting hexadecimal byte to the bytes string.
$bytes .= (str_pad(dechex(bindec(strrev($byte))), 2, 0, STR_PAD_LEFT));
}
return $bytes; // Return the resulting hexadecimal bytes
}
/**
* Converts a string of bits to an array of hexadecimal values.
*
* @param string $bits The string of bits to convert.
* @return array The converted array of hexadecimal values.
*/
public function bitsToHexArray(string $bits): array {
$bytes = []; // Initialize an empty array to store the resulting hexadecimal bytes
// Split the bits string into an array of byte-sized chunks (8 bits per chunk)
foreach (str_split($bits, 8) as $byte) {
// Reverse the order of bits within each byte-sized chunk using `strrev` function,
// then convert the reversed binary string to decimal using `bindec` function,
// then convert the decimal to hexadecimal using `dechex` function,
// and pad the resulting hexadecimal string with leading zeros to make it 2 characters long using `str_pad` function.
// Finally, add the resulting hexadecimal byte to the bytes array.
$bytes[] = (str_pad(dechex(bindec(strrev($byte))), 2, 0, STR_PAD_LEFT));
}
return $bytes; // Return the resulting array of hexadecimal bytes
}
}