* @copyright 2007 - 2008 (C) by Wrzasq * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3 */ /** * Universal OTServ binary formats reader. * *
* This class is general class that handle node-based binary OTServ files. It provides no file format-related logic, only loading nodes from files. To open given file format you need to use proper class like {@link OTS_OTBMFile OTS_OTBMFile} or {@link OTS_ItemsList OTS_ItemsList} classes. *
* ** This class is mostly usefull when you create own extensions for POT code. *
* * @package POT * @version 0.1.3 * @property-write IOTS_FileCache $cacheDriver Cache driver. */ class OTS_FileLoader { /** * Start of node. */ const NODE_START = 0xFE; /** * End of node. */ const NODE_END = 0xFF; /** * Escape another special byte. */ const ESCAPE_CHAR = 0xFD; /** * File handle. * * @var resource */ private $file; /** * Root node. * * @var OTS_FileNode */ protected $root; /** * Cache handler. * * @var IOTS_FileCache */ protected $cache; /** * Magic PHP5 method. * ** Allows object serialisation. *
* * @version 0.0.6 * @since 0.0.6 * @return array List of properties that should be saved. */ public function __sleep() { return array('root', 'cache'); } /** * Creates clone of object. * ** Copy of object needs to have different ID. *
* * @version 0.0.6 * @since 0.0.6 */ public function __clone() { // clones node tree $this->root = clone $this->root; } /** * Magic PHP5 method. * ** Allows object importing from {@link http://www.php.net/manual/en/function.var-export.php var_export()}. *
* * @version 0.0.6 * @since 0.0.6 * @param array $properties List of object properties. */ public static function __set_state($properties) { $object = new self(); // loads properties foreach($properties as $name => $value) { $object->$name = $value; } return $object; } /** * Sets cache handler. * ** You have to set cache driver before loading/saving any file in order to apply it to that file. *
* * @param IOTS_FileCache $cache Cache handler (leave this parameter if you want to unset caching). */ public function setCacheDriver(IOTS_FileCache $cache = null) { $this->cache = $cache; } /** * Opens file. * * @version 0.1.3 * @param string $file Filepath. * @throws E_OTS_FileLoaderError When error occurs during file operation. */ public function loadFile($file) { // attemts to read from cache if( isset($this->cache) ) { $this->root = $this->cache->readCache( md5_file($file) ); // checks if cache was loaded if( isset($this->root) ) { return; } } // opens for read in binary mode $this->file = fopen($file, 'rb'); if($this->file) { // reads file version $version = unpack('V', fread($this->file, 4) ); if($version[1] > 0) { throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_INVALID_FILE_VERSION); } $this->safeSeek(4); if( $this->readByte() != self::NODE_START) { throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_INVALID_FORMAT); } // reads root node $this->root = new OTS_FileNode(); $this->parseNode($this->root); fclose($this->file); // writes new cache if( isset($this->cache) ) { $this->cache->writeCache( md5_file($file), $this->root); } } // failed to open the file else { throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_CAN_NOT_OPEN); } } /** * Loades node from file. * * @param OTS_FileNode $node Node into which file fragment should be parsed. * @throws E_OTS_FileLoaderError When error occurs during file operation. */ private function parseNode(OTS_FileNode $node) { $current = $node; // reads node type $current->setType( $this->readByte() ); $buffer = ''; $propertiesSet = false; while(true) { // reads single byte from file $byte = $this->readByte(); switch($byte) { // start of new node case self::NODE_START: // reads child node $child = new OTS_FileNode(); $current->setBuffer($buffer); $propertiesSet = true; $current->setChild($child); $this->parseNode($child); break; // end of current node case self::NODE_END: if(!$propertiesSet) { $current->setBuffer($buffer); } // end of file if($node === $this->root) { return; } switch( $this->readByte() ) { // starts next node case self::NODE_START: $next = new OTS_FileNode(); $current->setNext($next); $current = $next; $current->setType( $this->readByte() ); $propertiesSet = false; $buffer = ''; break; // up 1 level and move 1 position back case self::NODE_END: $this->safeSeek( $this->safeTell() - 1); return; // invliad file format default: throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_INVALID_FORMAT); } break; // ignores next character case self::ESCAPE_CHAR: $buffer .= chr( $this->readByte() ); break; // normal character default: $buffer .= chr($byte); } } } /** * Reads single byte from file. * * @return int Read value. * @throws E_OTS_FileLoaderError When error occurs during file operation. */ private function readByte() { $value = fgetc($this->file); // checks eof if($value === false) { throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_EOF); } return ord($value); } /** * Seeks file pointer into given position. * * @param int $pos Seek position. * @throws E_OTS_FileLoaderError When error occurs during file operation. */ private function safeSeek($pos) { if( fseek($this->file, $pos, SEEK_SET) == -1) { // error occured throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_SEEK_ERROR); } } /** * Returns current position in file. * * @return int Position in file. * @throws E_OTS_FileLoaderError When error occurs during file operation. */ private function safeTell() { // reads file position $pos = ftell($this->file); // checks if operation succeeded if($pos === false) { throw new E_OTS_FileLoaderError(E_OTS_FileLoaderError::ERROR_TELL_ERROR); } return $pos; } /** * Magic PHP5 method. * * @version 0.1.0 * @since 0.1.0 * @param string $name Property name. * @param mixed $value Property value. * @throws OutOfBoundsException For non-supported properties. */ public function __set($name, $value) { switch($name) { case 'cacheDriver': $this->setCacheDriver($value); break; // not-supported default: throw new OutOfBoundsException(); } } } /**#@-*/