mirror of
https://github.com/slawkens/myaac.git
synced 2025-04-27 01:39:22 +02:00
362 lines
8.5 KiB
PHP
362 lines
8.5 KiB
PHP
<?php
|
|
|
|
/**#@+
|
|
* @version 0.0.6
|
|
* @since 0.0.6
|
|
*/
|
|
|
|
/**
|
|
* Code in this file bases on oryginal OTServ binary format loading C++ code (fileloader.h, fileloader.cpp).
|
|
*
|
|
* @package POT
|
|
* @version 0.1.3
|
|
* @author Wrzasq <wrzasq@gmail.com>
|
|
* @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.
|
|
*
|
|
* <p>
|
|
* 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.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* This class is mostly usefull when you create own extensions for POT code.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* <p>
|
|
* Allows object serialisation.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* <p>
|
|
* Copy of object needs to have different ID.
|
|
* </p>
|
|
*
|
|
* @version 0.0.6
|
|
* @since 0.0.6
|
|
*/
|
|
public function __clone()
|
|
{
|
|
// clones node tree
|
|
$this->root = clone $this->root;
|
|
}
|
|
|
|
/**
|
|
* Magic PHP5 method.
|
|
*
|
|
* <p>
|
|
* Allows object importing from {@link http://www.php.net/manual/en/function.var-export.php var_export()}.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* <p>
|
|
* You have to set cache driver before loading/saving any file in order to apply it to that file.
|
|
* </p>
|
|
*
|
|
* @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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**#@-*/
|
|
|
|
?>
|