diff --git a/src/Reader.php b/src/Reader.php new file mode 100644 index 0000000..ee143c3 --- /dev/null +++ b/src/Reader.php @@ -0,0 +1,182 @@ + + * @copyright 2006, 2007 The Bearpaw Project Work Group + * @copyright 2007, 2008 BEHR Software Systems + * @version $Rev$ + */ +class Reader +{ + /** + * @var resource The underlying file descriptor. + */ + private $_fd; + + /** + * @var integer The file size. + */ + private $_size; + + /** + * Opens the file given as a parameter. + * + * @param string $filename The absolute or relative path to the file. + * @throws ReaderException if the file cannot be read. + */ + public function __construct($filename) + { + if (($this->_fd = fopen($filename, "rb")) === false) + throw new ReaderException("Unable to open file:" . $filename); + + fseek($this->_fd, 0, SEEK_END); + $this->_size = ftell($this->_fd); + fseek($this->_fd, 0); + } + + /** + * Closes the file. + */ + public function __destruct() + { + @fclose($this->_fd); + } + + /** + * Checks whether there is more to be read in the file. + * + * @return boolean Returns true if the end of the file has not yet + * been reached; false otherwise. + */ + public function available() + { + return $this->getOffset() < $this->_size; + } + + /** + * Jumps size amount of bytes in the file stream. + * + * @return void + * @throws ReaderException if size attribute is not greater or + * equal than zero. + */ + public function skip($size) + { + if ($size < 0) + throw new ReaderException("Invalid argument"); + fseek($this->_fd, $size, SEEK_CUR); + } + + /** + * Reads length amount of bytes from the file stream. + * + * @return string Returns read bytes as a string + * @throws ReaderException if length attribute is not greater than + * zero. + */ + public function read($length) + { + if ($length <= 0) + throw new ReaderException("Invalid argument"); + return fread($this->_fd, $length); + } + + /** + * Returns the current point of operation. + * + * @return integer Returns the current cursor position. + */ + public function getOffset() + { + return ftell($this->_fd); + } + + /** + * Sets the point of operation, ie the cursor offset value. + * + * @return void + */ + public function setOffset($offset) + { + fseek($this->_fd, $offset); + } + + /** + * Returns the file size in bytes. + * + * @return integer Returns the file size in bytes. + */ + public function getSize() + { + return $this->_size; + } + + /** + * Magic function to delegate the call to helper methods of Transform class + * to transform read data in another format. + * + * The read data length is determined from the helper method name. For methods + * where arbitrary data lengths are accepted a parameter can be used to + * specify the length. + * + * @param string $method The method to be called. + * @param string $params The parameters should the function accept them. + * @return mixed + * @throws ReaderException if no such transformer is implemented + */ + public function __call($method, $params) { + $chunks = array(); + if (preg_match + ("/get([a-z]{3,6})?(\d{1,2})?(?:LE|BE)?/i", $method, $chunks) && + method_exists("Transform", $method)) { + return call_user_func + (array("Transform", $method), + $this->read(preg_match("/String|(?:H|L)Hex/", $chunks[1]) ? + (isset($params[0]) ? $params[0] : 1) : + ($chunks[1] == "GUID" ? 16 : $chunks[2] / 8))); + } else throw new ReaderException("Unknown method: " . $method); + } +} diff --git a/src/ReaderException.php b/src/ReaderException.php new file mode 100644 index 0000000..c056049 --- /dev/null +++ b/src/ReaderException.php @@ -0,0 +1,46 @@ + + * @copyright 2008 BEHR Software Systems + * @version $Rev$ + */ +class ReaderException extends Exception +{ +} diff --git a/src/Transform.php b/src/Transform.php new file mode 100644 index 0000000..aeb91d2 --- /dev/null +++ b/src/Transform.php @@ -0,0 +1,282 @@ + + * @copyright 2006, 2007 The Bearpaw Project Work Group + * @copyright 2007, 2008 BEHR Software Systems + * @version $Rev$ + * @static + */ +final class Transform +{ + const MACHINE_ENDIAN_ORDER = 0; + const LITTLE_ENDIAN_ORDER = 1; + const BIG_ENDIAN_ORDER = 2; + + /** + * Default private constructor for a static class. + */ + private function __construct() {} + + /** + * Returns machine-endian ordered binary data as 64-bit float. PHP does not + * support 64-bit integers as the long integer is of 32-bits but using + * aritmetic operations it is implicitly converted into floating point which + * is of 64-bits long. + * + * @return integer Returns the resulting 64-bit integer. + */ + public static function getInt64($raw, $order = self::MACHINE_ENDIAN_ORDER) + { + list(, $lo, $hi) = unpack(($order == 2 ? "L" : + ($order == 1 ? "V" : "N")) . "*", $raw); + return $hi * 0xffffffff + $lo; + } + + /** + * Returns little-endian ordered binary data as 64-bit float. PHP does not + * support 64-bit integers as the long integer is of 32-bits but using + * aritmetic operations it is implicitly converted into floating point which + * is of 64-bits long. + * + * @return integer Returns the resulting 64-bit integer. + */ + public static function getInt64LE($raw) + { + return ReaderUtils::getInt64($raw, self::LITTLE_ENDIAN_ORDER); + } + + /** + * Returns big-endian ordered binary data as 64-bit float. PHP does not + * support 64-bit integers as the long integer is of 32-bits but using + * aritmetic operations it is implicitly converted into floating point which + * is of 64-bits long. + * + * @return integer Returns the resulting 64-bit integer. + */ + public static function getInt64BE($raw) + { + return ReaderUtils::getInt64($raw, self::BIG_ENDIAN_ORDER); + } + + /** + * Returns machine-endian ordered binary data as signed 32-bit integer. + * + * @return integer Returns the resulting signed 32-bit integer. + */ + public static function getInt32($raw) + { + list(, $int) = unpack("l*", $raw); + return $int; + } + + /** + * Returns machine-endian ordered binary data as unsigned 32-bit integer. + * + * @return integer Returns the resulting unsigned 32-bit integer. + */ + public static function getUInt32($raw, $order = self::MACHINE_ENDIAN_ORDER) + { + list(, $int) = unpack(($order == 2 ? "N" : + ($order == 1 ? "V" : "L")) . "*", $raw); + return $int; + } + + /** + * Returns little-endian ordered binary data as unsigned 32-bit integer. + * + * @return integer Returns the resulting unsigned 32-bit integer. + */ + public static function getUInt32LE($raw) + { + return ReaderUtils::getUInt32($raw, self::LITTLE_ENDIAN_ORDER); + } + + /** + * Returns big-endian ordered binary data as unsigned 32-bit integer. + * + * @return integer Returns the resulting unsigned 32-bit integer. + */ + public static function getUInt32BE($raw) + { + return ReaderUtils::getUInt32($raw, self::BIG_ENDIAN_ORDER); + } + + /** + * Returns machine endian ordered binary data as signed 16-bit integer. + * + * @return integer Returns the resulting signed 16-bit integer. + */ + public static function getInt16($raw) + { + list(, $int) = unpack("s*", $raw); + return $int; + } + + /** + * Returns machine endian ordered binary data as unsigned 16-bit integer. + * + * @return integer Returns the resulting unsigned 16-bit integer. + */ + public static function getUInt16($raw, $order) + { + list(, $int) = unpack(($order == 2 ? "n" : + ($order == 1 ? "v" : "S")) . "*", $raw); + return $int; + } + + /** + * Returns little-endian ordered binary data as unsigned 16-bit integer. + * + * @return integer Returns the resulting unsigned 16-bit integer. + */ + public static function getUInt16LE($raw) + { + return ReaderUtils::getUInt16($raw, self::LITTLE_ENDIAN_ORDER); + } + + /** + * Returns big-endian ordered binary data as unsigned 16-bit integer. + * + * @return integer Returns the resulting unsigned 16-bit integer. + */ + public static function getUInt16BE($raw) + { + return ReaderUtils::getUInt16($raw, self::BIG_ENDIAN_ORDER); + } + + /** + * Returns binary data as 8-bit integer. + * + * @return integer Returns the resulting 8-bit integer. + */ + public static function getInt8($raw) + { + return $raw; + } + + /** + * Returns binary data as string. Removes terminating zero. + * + * @return string Returns the resulting string. + */ + public static function getString8($raw) + { + $string = ""; + foreach (unpack("C*", $raw) as $char) + $string .= pack("c", $char); + return rtrim($string, "\0"); + } + + /** + * Returns machine-endian ordered binary data as string. + * + * @return string Returns the resulting string. + */ + public static function getString16($raw, $order = self::MACHINE_ENDIAN_ORDER) + { + $string = ""; + foreach (unpack(($order == 2 ? "n" : + ($order == 1 ? "v" : "S")) . "*", $raw) as $char) + $string .= pack("S", $char); + return rtrim($string, "\0"); + } + + /** + * Returns little-endian ordered binary data as string. + * + * @return string Returns the resulting string. + */ + public static function getString16LE($raw) + { + return ReaderUtils::getString16($raw, self::LITTLE_ENDIAN_ORDER); + } + + /** + * Returns big-endian ordered binary data as string. + * + * @return string Returns the resulting string. + */ + public static function getString16BE($raw) + { + return ReaderUtils::getString16($raw, self::BIG_ENDIAN_ORDER); + } + + /** + * Returns binary data as hexadecimal string having high nibble first. + * + * @return string Returns the resulting string. + */ + public static function getHHex($raw) + { + list($hex) = unpack("H*0", $raw); + return $hex; + } + + /** + * Returns binary data as hexadecimal string having low nibble first. + * + * @return string Returns the resulting string. + */ + public static function getLHex($raw) + { + list($hex) = unpack("h*0", $raw); + return $hex; + } + + /** + * Returns the little-endian ordered raw data as big-endian ordered + * hexadecimal GUID string. + * + * @return string Returns the raw data in appropriate GUID format. + */ + public static function getGUID($raw) + { + $C = @unpack("V1V/v2v/N2N", $raw); + list($hex) = @unpack("H*0", pack + ("NnnNN", $C["V"], $C["v1"], $C["v2"], $C["N1"], $C["N2"])); + + /* Fixes a bug in PHP versions earlier than Jan 25 2006 */ + if (implode("", unpack("H*", pack("H*", "a"))) == "a00") + $hex = substr($hex, 0, -1); + + return preg_replace + ("/^(.{8})(.{4})(.{4})(.{4})/", "\\1-\\2-\\3-\\4-", $hex); + } +}