Simple reader for uncompressed TibiaCam files (binary files, up to 60MB).
It’s also example of fast reading binary data from file and handling it as stream.
<?php
class BinaryData
{
/** @var resource */
private $data;
/** @var int */
private $length;
/**
* BinaryData constructor.
* @param string $data
*/
public function __construct($data)
{
$this->data = fopen('php://memory', 'wb+');
fwrite($this->data, $data);
$this->length = ftell($this->data);
rewind($this->data);
}
/**
* @return int
*/
public function getLength()
{
return $this->length;
}
/**
* @return bool
*/
public function isEof()
{
return ftell($this->data) == $this->length;
}
/**
* @param int $length
*/
public function skip($length)
{
fseek($this->data, $length, SEEK_CUR);
}
/**
* @return int
*/
public function getU8()
{
return unpack('C', fread($this->data, 1))[1];
}
/**
* @return int
*/
public function getU16()
{
return unpack('v', fread($this->data, 2))[1];
}
/**
* @return int
*/
public function getU32()
{
return unpack('V', fread($this->data, 4))[1];
}
/**
* @return int
*/
public function getU64()
{
return unpack('P', fread($this->data, 8))[1];
}
/**
* @param int $length
* @return string
*/
public function getString($length = -1)
{
if ($length == -1) {
$length = $this->getU16();
}
return fread($this->data, $length);
}
}
class NetworkPacket extends BinaryData
{
/**
* 0x01 = packets sent to player
* 0x03 = packets received from player
* 0x05 = refresh packets for fast cam rewinding
*/
const TYPE_OUT = 0x01;
const TYPE_IN = 0x03;
const TYPE_REFRESH = 0x05;
/** @var int */
private $type;
/**
* NetworkPacket constructor.
* @param string $data
*/
public function __construct($data)
{
parent::__construct($data);
$this->type = $this->getU8();
}
/**
* @return int
*/
public function getType()
{
return $this->type;
}
}
class Cam extends BinaryData
{
/**
* Cam constructor.
* @param string $filePath path to cam file
*/
public function __construct($filePath)
{
parent::__construct(file_get_contents($filePath));
}
/**
* @return array
*/
public function readTibiaCamAttributes()
{
$this->skip(8); // text: TIBIACAM
$camVersion = $this->getU8();
$playerId = $this->getU32();
$playerName = $this->getString();
$protocolVersion = $this->getU16();
$timeCreated = $this->getU64();
return [$camVersion, $playerId, $playerName, $protocolVersion, $timeCreated];
}
}
$fileName = 'nowy.cam.ready';
$cam = new Cam($fileName);
$camAttributes = $cam->readTibiaCamAttributes();
var_dump($camAttributes);
while (!$cam->isEof()) {
$packetTime = $cam->getU64();
var_dump($packetTime);
$packet = new NetworkPacket($cam->getString());
var_dump($packet->getType());
var_dump($packet->getLength());
}
<?php
class BinaryData
{
/** @var resource */
private $data;
/** @var int */
private $length;
/**
* BinaryData constructor.
* @param string $data
*/
public function __construct($data)
{
$this->data = fopen('php://memory', 'wb+');
fwrite($this->data, $data);
$this->length = ftell($this->data);
rewind($this->data);
}
/**
* @return int
*/
public function getLength()
{
return $this->length;
}
/**
* @return bool
*/
public function isEof()
{
return ftell($this->data) == $this->length;
}
/**
* @param int $length
*/
public function skip($length)
{
fseek($this->data, $length, SEEK_CUR);
}
/**
* @return int
*/
public function getU8()
{
return unpack('C', fread($this->data, 1))[1];
}
/**
* @return int
*/
public function getU16()
{
return unpack('v', fread($this->data, 2))[1];
}
/**
* @return int
*/
public function getU32()
{
return unpack('V', fread($this->data, 4))[1];
}
/**
* @return int
*/
public function getU64()
{
return unpack('P', fread($this->data, 8))[1];
}
/**
* @param int $length
* @return string
*/
public function getString($length = -1)
{
if ($length == -1) {
$length = $this->getU16();
}
return fread($this->data, $length);
}
}
class NetworkPacket extends BinaryData
{
/**
* 0x01 = packets sent to player
* 0x03 = packets received from player
* 0x05 = refresh packets for fast cam rewinding
*/
const TYPE_OUT = 0x01;
const TYPE_IN = 0x03;
const TYPE_REFRESH = 0x05;
/** @var int */
private $type;
/**
* NetworkPacket constructor.
* @param string $data
*/
public function __construct($data)
{
parent::__construct($data);
$this->type = $this->getU8();
}
/**
* @return int
*/
public function getType()
{
return $this->type;
}
}
class Cam extends BinaryData
{
/**
* Cam constructor.
* @param string $filePath path to cam file
*/
public function __construct($filePath)
{
parent::__construct(file_get_contents($filePath));
}
/**
* @return array
*/
public function readTibiaCamAttributes()
{
$this->skip(8); // text: TIBIACAM
$camVersion = $this->getU8();
$playerId = $this->getU32();
$playerName = $this->getString();
$protocolVersion = $this->getU16();
$timeCreated = $this->getU64();
return [$camVersion, $playerId, $playerName, $protocolVersion, $timeCreated];
}
}
$fileName = 'nowy.cam.ready';
$cam = new Cam($fileName);
$camAttributes = $cam->readTibiaCamAttributes();
var_dump($camAttributes);
while (!$cam->isEof()) {
$packetTime = $cam->getU64();
var_dump($packetTime);
$packet = new NetworkPacket($cam->getString());
var_dump($packet->getType());
var_dump($packet->getLength());
}
<?php class BinaryData { /** @var resource */ private $data; /** @var int */ private $length; /** * BinaryData constructor. * @param string $data */ public function __construct($data) { $this->data = fopen('php://memory', 'wb+'); fwrite($this->data, $data); $this->length = ftell($this->data); rewind($this->data); } /** * @return int */ public function getLength() { return $this->length; } /** * @return bool */ public function isEof() { return ftell($this->data) == $this->length; } /** * @param int $length */ public function skip($length) { fseek($this->data, $length, SEEK_CUR); } /** * @return int */ public function getU8() { return unpack('C', fread($this->data, 1))[1]; } /** * @return int */ public function getU16() { return unpack('v', fread($this->data, 2))[1]; } /** * @return int */ public function getU32() { return unpack('V', fread($this->data, 4))[1]; } /** * @return int */ public function getU64() { return unpack('P', fread($this->data, 8))[1]; } /** * @param int $length * @return string */ public function getString($length = -1) { if ($length == -1) { $length = $this->getU16(); } return fread($this->data, $length); } } class NetworkPacket extends BinaryData { /** * 0x01 = packets sent to player * 0x03 = packets received from player * 0x05 = refresh packets for fast cam rewinding */ const TYPE_OUT = 0x01; const TYPE_IN = 0x03; const TYPE_REFRESH = 0x05; /** @var int */ private $type; /** * NetworkPacket constructor. * @param string $data */ public function __construct($data) { parent::__construct($data); $this->type = $this->getU8(); } /** * @return int */ public function getType() { return $this->type; } } class Cam extends BinaryData { /** * Cam constructor. * @param string $filePath path to cam file */ public function __construct($filePath) { parent::__construct(file_get_contents($filePath)); } /** * @return array */ public function readTibiaCamAttributes() { $this->skip(8); // text: TIBIACAM $camVersion = $this->getU8(); $playerId = $this->getU32(); $playerName = $this->getString(); $protocolVersion = $this->getU16(); $timeCreated = $this->getU64(); return [$camVersion, $playerId, $playerName, $protocolVersion, $timeCreated]; } } $fileName = 'nowy.cam.ready'; $cam = new Cam($fileName); $camAttributes = $cam->readTibiaCamAttributes(); var_dump($camAttributes); while (!$cam->isEof()) { $packetTime = $cam->getU64(); var_dump($packetTime); $packet = new NetworkPacket($cam->getString()); var_dump($packet->getType()); var_dump($packet->getLength()); }