PHP的分析文件程序,要翻译成C#
?php /* ************************************************************************* * AOC Recorded Games Analyzer * --------------------------- * begin : Monday, December 3, 2007 * copyright : (c) 2007-2008 biegleux * email : biegleux(at)gma
/* *************************************************************************
* AOC Recorded Games Analyzer
* ---------------------------
* begin : Monday, December 3, 2007
* copyright : (c) 2007-2008 biegleux
* email : biegleux(at)gmail(dot)com
*
* recAnalyst v0.9.1 2008/11/07
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Thanks to bari [aocai-lj(at)infoseek.jp] for sharing mgx file format
* description.
*
* Note: Code is not fully optimized, any suggestions appreciated.
************************************************************************* */
/**
* Defines RecAnalyst class.
*
* @package recAnalyst
* @version $Id: recAnalyst.class.php 5 2008-11-08 15:04:10Z biegleux $
* @author biegleux
* @copyright copyright (c) 2008 biegleux
* @license http://www.opensource.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3)
* @link http://recanalyst.sourceforge.net/
* @filesource
* @todo rar extension support
* @todo test for server zlib/zip extension support
* @todo implement method/member for holding analyze time
*/
// uncomment following line if we want to debug
//define ('RA__DEBUG', true);
if (defined ('RA__DEBUG'))
{
error_reporting (E_ALL | E_STRICT);
}
/**#@+
*
* @ignore
*/
// error codes
define ('RA__NO_ERROR', 0x00);
define ('RA__FILE_NOT_SPECIFIED', 0x01);
define ('RA__FORMAT_NOT_SUPPORTED', 0x02);
define ('RA__UNABLE_TO_OPEN_ZIP_ARCHIVE', 0x03);
define ('RA__ZIP_ARCHIVE_EMPTY', 0x04);
define ('RA__OLD_FORMAT_VER', 0x05);
define ('RA__EMPTY_HEADER_STREAM', 0x06);
define ('RA__ERROR_IN_UNCOMPRESSED_STREAM', 0x07);
define ('RA__RAR_EXTENSION_NOT_INSTALLED', 0x08);
define ('RA__RAR_NOT_IMPLEMENTED', 0x09);
define ('RA__MORE_FILES_PER_ARCHIVE', 0x0A);
define ('RA__NO_RECGAME_FOUND', 0x0B);
define ('RA__ERROR_IN_HEADER_STREAM', 0x0C);
require_once ('recAnalyst.config.php');
require_once ('recAnalyst.consts.php');
require_once ('gameSettings.class.php');
require_once ('player.class.php');
require_once ('parentList.class.php');
require_once ('playerList.class.php');
require_once ('team.class.php');
require_once ('teamList.class.php');
/**#@-*/
/**
* RecAnalyst class
*
* RecAnalyst implements analyzing of recorded games.
*
* @package recAnalyst
*/
class RecAnalyst
{
const mgxExt = '.mgx';
const rarExt = '.rar';
const zipExt = '.zip';
/**
* Internal storage for new members.
*
* @var array
*/
private $data;
/**
* Input filename we wish to analyze.
*
* @var string
*/
public $fileName;
/**
* Internal stream containing header information.
*
* @var string
*/
private $headerStream;
/**
* Internal stream containing body information.
*
* @var string
*/
private $bodyStream;
/**
* Holds a code of the recent error.
*
* @var int
*/
private $lastError;
/**
* An array containing map data.
*
* $var array
*/
private $mapData;
/**
* Map width.
*
* @var int
*/
private $mapWidth;
/**
* Map height.
*
* @var int
*/
private $mapHeight;
/**
* Game settings information.
*
* @var GameSettings
*/
public $gameSettings;
/**
* List of players in the game.
*
* @var PlayerList
*/
public $playerList;
/**
* List of teams in the game.
*
* @var TeamList
*/
public $teams;
/**
* An array containing pre-game chat.
*
* @var array
*/
public $pregameChat;
/**
* An array containing in-game chat.
*
* @var array
*/
public $ingameChat;
/**
* An associative array containing "unit_type_id - unit_num" pairs.
*
* @var array
*/
public $units;
/**
* An associative multi-dimesional array containing "building_type_id - building_num" pairs for each player.
*
* @var array
*/
public $buildings;
/**
* An associative multi-dimesional array containing information about trading.
*
* @var array
*/
public $trading;
/**
* Constructor.
*
*/
public function __construct ()
{
$this->data = array ();
$this->fileName = '';
$this->headerStream = '';
$this->bodyStream = '';
$this->lastError = RA__NO_ERROR;
$this->gameSettings = new GameSettings ();
$this->playerList = new PlayerList ();
$this->teams = new TeamList ();
$this->pregameChat = array ();
$this->ingameChat = array ();
$this->units = array ();
$this->buildings = array ();
$this->mapData = array ();
$this->mapWidth = $this->mapHeight = 0;
$this->trading = array ();
}
/**
* Destructor.
*
*/
public function __destruct ()
{
}
/**
* Callback method for setting a property.
*
* @param mixed $nm
* @param mixed $val
*/
public function __set ($nm, $val)
{
$this->data[$nm] = $val;
}
/**
* Callback method for getting a property.
*
* @param mixed $nm
*/
public function __get ($nm)
{
return $this->data[$nm];
}
/**
* Returns code of the recent error.
*
* @return int
*/
public function getLastError ()
{
return $this->lastError;
}
/**
* Represents the error code as a string information.
*
* @param int $errCode error code
* @static
* @return string error string
*/
public static function errorCodeToString ($errCode)
{
switch ($errCode)
{
case RA__NO_ERROR:
$errString = 'No error occured.';
break;
case RA__FILE_NOT_SPECIFIED:
$errString = 'No file has been specified for analyzing.';
break;
case RA__FORMAT_NOT_SUPPORTED:
$errString = 'File format is not supported.';
break;
case RA__UNABLE_TO_OPEN_ZIP_ARCHIVE:
$errString = 'Unable to open zip archive.';
break;
case RA__ZIP_ARCHIVE_EMPTY:
$errString = 'Empty zip archive.';
break;
case RA__MORE_FILES_PER_ARCHIVE:
$errString = 'Only one file per archive is supported for analyzing.';
break;
case RA__OLD_FORMAT_VER:
$errString = 'Old mgx file format.';
break;
case RA__EMPTY_HEADER_STREAM:
$errString = 'Empty header stream.';
break;
case RA__ERROR_IN_UNCOMPRESSED_STREAM:
$errString = 'Error in uncompressed stream.';
break;
case RA__RAR_EXTENSION_NOT_INSTALLED:
$errString = 'Rar extension is not installed on server.';
break;
case RA__RAR_NOT_IMPLEMENTED:
$errString = 'Support for rar archives is not implemented.';
break;
case RA__NO_RECGAME_FOUND:
$errString = 'No recorded game has been found in archive.';
break;
case RA__ERROR_IN_HEADER_STREAM:
$errString = 'Error in header stream.';
break;
default:
$errString = '';
break;
}
return ($errString);
}
/**
* Converts game's time to string representation.
*
* @param int $time game time
* @param string $format desired string format
* @static
* @return string time in formatted string
*/
public static function gameTimeToString ($time, $format = '%02d:%02d:%02d')
{
if ($time == 0)
return '-';
$hour = (int)($time / 1000 / 3600);
$minute = ((int)($time / 1000 / 60)) % 60;
$second = ((int)($time / 1000)) % 60;
return sprintf ($format, $hour, $minute, $second);
}
/**
* Extracts header and body streams from an archive.
*
* @return bool
*/
private function extractStreamsFromArchive ()
{
if (!$this->fileName)
{
$this->lastError = RA__FILE_NOT_SPECIFIED;
return false;
}
$ext = strrchr ($this->fileName, '.');
if (strcasecmp ($ext, self::rarExt) != 0 && strcasecmp ($ext, self::zipExt) != 0)
{
$this->lastError = RA__FORMAT_NOT_SUPPORTED;
return false;
}
if (strcasecmp ($ext, self::rarExt) == 0)
{
if (!function_exists ('rar_open'))
{
$this->lastError = RA__RAR_EXTENSION_NOT_INSTALLED;
return false;
}
// stub
$this->lastError = RA__RAR_NOT_IMPLEMENTED;
return false;
}
if (strcasecmp ($ext, self::zipExt) == 0)
{
$zip = new ZipArchive ();
if ($zip->open ($this->fileName) !== true)
{
$this->lastError = RA__UNABLE_TO_OPEN_ZIP_ARCHIVE;
return false;
}
if ($zip->numFiles == 0)
{
$zip->close ();
$this->lastError = RA__ZIP_ARCHIVE_EMPTY;
return false;
}
if ($zip->numFiles != 1)
{
$zip->close ();
$this->lastError = RA__MORE_FILES_PER_ARCHIVE;
return false;
}
$mgx_found = false;
for ($i = 0; false !== ($stat = $zip->statIndex ($i)); $i++)
{
// skip directories and 0-bytes files
if (!$stat['size'])
{
continue;
}
// skip non-mgx files
$ext = strrchr ($stat['name'], '.');
if (strcasecmp ($ext, self::mgxExt) != 0)
{
continue;
}
// get a file handler to the entry
if (!($fp = $zip->getStream ($stat['name'])))
{
continue;
}
$mgx_found = true;
// read data
$packed_data = fread ($fp, 4);
if ($packed_data === false || strlen ($packed_data) {
$zip->close ();
$this->lastError = RA__ERROR_IN_HEADER_STREAM;
return false;
}
$unpacked_data = unpack ("V", $packed_data);
$header_len = $unpacked_data[1];
if ($header_len == 0)
{
$zip->close ();
$this->lastError = RA__OLD_FORMAT_VER;
return false;
}
// skip next_pos
$packed_data = fread ($fp, 4);
$header_len -= 8;
// TODO: getMemoryLimit ()
if ($header_len > 1048576) // 1MB
{
$zip->close ();
$this->lastError = RA__ERROR_IN_HEADER_STREAM;
return false;
}
$read = 0;
while ($read {
$read += strlen ($buff);
$this->headerStream .= $buff;
}
$read = 0;
while (!feof ($fp))
{
$buff = fread ($fp, 1024 * 8);
$this->bodyStream .= $buff;
}
unset ($buff);
fclose ($fp);
$zip->close ();
return true;
} // endfor
$zip->close ();
if (!$mgx_found)
{
$this->lastError = RA__NO_RECGAME_FOUND;
return false;
}
} // end zip uncompression
}
/**
* Uncompresses header stream.
*
* @return string|bool uncompressed stream or false if an error occured
*/
private function uncompressHeaderStream ()
{
if (!$this->headerStream)
{
$this->lastError = RA__EMPTY_HEADER_STREAM;
return false;
}
//TODO: getMemoryLimit ()
$this->headerStream = @gzinflate ($this->headerStream, 4194304); // 4MB
if (!$this->headerStream)
{
$this->lastError = RA__ERROR_IN_HEADER_STREAM;
return false;
}
return true;
}
/**
* Analyzes header stream.
*
* @return bool true if analyzed successfully, otherwise false
*/
private function analyzeHeaderStream ()
{
global $RA__MAPS;
global $RA__GAME_STYLES;
global $RA__DIFFICULTY_LEVELS;
global $RA__GAME_TYPES;
global $RA__GAME_SPEEDS;
global $RA__CIVS;
// initialize variables
$constant2 = pack ('c*', 0x9A, 0x99, 0x99, 0x99, 0x99, 0x99, 0xF9, 0x3F);
$separator = pack ('c*', 0x9D, 0xFF, 0xFF, 0xFF);
$unknown_const2 = pack ('c*', 0x98, 0x9E, 0x00, 0x00, 0x02, 0x0B);
$trigger_info_pos = $game_setting_pos = 0;
$string_id = pack ('c*', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
$pos = 0;
$m_header_len = strlen ($this->headerStream);
$len = strlen ($constant2);
$pos = ($m_header_len - $len);
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
// TODO: use substr_compare
while ($pos > 0)
{
if (strcmp ($buff, $constant2) == 0)
{
$trigger_info_pos = $pos;
break;
}
$pos -= $len + 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
if ($trigger_info_pos == 0)
{
$this->lastError = RA__ERROR_IN_UNCOMPRESSED_STREAM;
return false;
}
// getting Game_settings position
$len = strlen ($separator);
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
while ($pos > 0)
{
if (strcmp ($buff, $separator) == 0)
{
$game_setting_pos = $pos;
break;
}
$pos -= $len + 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
if ($game_setting_pos == 0)
{
// not found
$this->lastError = RA__ERROR_IN_UNCOMPRESSED_STREAM;
return false;
}
/***************************************************************************************
note: Victory & Achievement data not found as described in mgx format description
obtaining Achievement data should be called after knowing num_player as it is required
// getting Victory position, first is Disables
$pos -= 8;
$victory_pos = 0;
$separator_first_pos = 0;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
while ($pos > 0)
{
if (strcmp ($buff, $separator) == 0)
break;
$pos -= $len + 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
if ($pos != 0)
{
$pos -= 8;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
while ($pos > 0)
{
if (strcmp ($buff, $separator) == 0)
{
$victory_pos = $pos;
break;
}
$pos -= $len + 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
}
if ($victory_pos != 0)
{
$pos -= 8;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
while ($pos > 0)
{
if (strcmp ($buff, $separator) == 0)
{
$separator_first_pos = $pos;
break;
}
$pos -= $len + 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
$pos = $victory_pos;
// here should be victory condition, but isn't
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$conquest = $unpacked_data[1];
if (conquest == 0)
{
$this->gameSettings->victory = 'custom';
$pos += 4;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$relics = $unpacked_data[1];
$pos += 4;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$explored = $unpacked_data[1];
$pos += 4;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$all = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$mode = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$score = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$time = $unpacked_data[1];
if ($relics != 0)
{
$this->gameSettings->victory = sprintf ('Gain %d relics', $relics);
}
elseif ($explored != 0)
{
$this->gameSettings->victory = sprintf ('Percentage of explored map: %d', $explored);
}
else
{
switch ($mode)
{
case 0:
$this->gameSettings->victory = 'Normal';
break;
case 1:
$this->gameSettings->victory = 'Conquest';
break;
case 2:
$this->gameSettings->victory = sprintf ('Score Limit: %d', $score);
break;
case 3:
$this->gameSettings->victory = sprintf ('Time Limit: %d', $time);
break;
}
}
}
elseif ($condition == 1)
{
$this->gameSettings->victory = 'Conquest';
}
}
if ($separator_first_pos != 0)
{
$string_id_pos = 0;
$len = strlen ($string_id);
$pos = $separator_first_pos - $len;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
while ($pos > 0)
{
if (strcmp ($buff, $string_id) == 0)
{
$string_id_pos = $pos;
break;
}
$pos -= $len + 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
if ($string_id_pos != 0)
{
$pos = $string_id_pos - $len;
$pos -= 4096;
$pos -= 4;
$pos -= 4;
// num_player is not known till now, need to be called after obtaining it
$pos -= 1473 * $num_player;
// here should start Achievement, but doesn't :-(
$pos += 13;
$packed_data = substr ($this->headerStream, $pos, 4);
$unpacked_data = unpack ("V", $packed_data);
$total_point = $unpacked_data[1];
//...
}
}
***************************************************************************************/
// getting Game_Settings data
// skip negative[2]
$pos = $game_setting_pos + 8;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$map_id = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$difficulty = $unpacked_data[1];
// skip unknown
$pos += 4;
if (array_key_exists ($map_id, $RA__MAPS))
{
$this->gameSettings->map = $RA__MAPS[$map_id][0];
$this->gameSettings->gameStyle = $RA__GAME_STYLES[0];
}
else
{
$this->gameSettings->gameStyle = $RA__GAME_STYLES[2];
}
if (array_key_exists ($difficulty, $RA__DIFFICULTY_LEVELS))
{
$this->gameSettings->difficultyLevel = $RA__DIFFICULTY_LEVELS[$difficulty];
}
// getting Player_info data
for ($i = 0; $i {
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$player_data_index = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$human = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$name_len = $unpacked_data[1];
$playername = substr ($this->headerStream, $pos, $name_len); $pos += $name_len;
// 0x00:invalid player, 0x02:human, 0x04:computer
// index 0 is GAIA player
if ($human == 0x00)
continue;
// sometimes very rarely index is 1
if ($human == 0x01)
continue;
if ($human != 0x00 && $i != 0)
{
$player = new Player ();
$player->name = $playername;
$player->index = $player_data_index;
$player->human = ($human == 0x02);
$this->playerList->addPlayer ($player);
}
} // endfor
// Trigger_info
$pos = $trigger_info_pos + 1;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$num_trigger = $unpacked_data[1];
if ($num_trigger == 0)
{
$this->gameSettings->isScenario = false;
// Other_data
for ($i = 0; $i {
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$team = $unpacked_data[1];
if (($i + 1) playerList->getCount ())
{
if ($player = $this->playerList->getPlayer ($i))
{
$player->team = $team - 1;
}
}
}
// skip unknown[13]
$pos += 13;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$pop_limit = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$game_type = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$lock_diplomacy = $unpacked_data[1];
$this->gameSettings->popLimit = $pop_limit;
$this->gameSettings->gameType = $RA__GAME_TYPES[$game_type];
$this->gameSettings->lockDiplomacy = ($lock_diplomacy == 0x01);
// here comes pre-game chat
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$num_chat = $unpacked_data[1];
for ($i = 0; $i {
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$chat_len = $unpacked_data[1];
// 0-length chat exists
if ($chat_len == 0)
{
continue;
}
$chat = substr ($this->headerStream, $pos, $chat_len); $pos += $chat_len;
if ($chat[0] == '@' && $chat[1] == '#' && $chat[2] >= '1' && $chat[2] {
$chat = rtrim ($chat); // throw null-termination character
$this->pregameChat[] = $chat;
}
}
unset ($chat);
}
else
{
$this->gameSettings->isScenario = true;
$this->gameSettings->map = '';
$this->gameSettings->gameType = $RA__GAME_TYPES[3];
$this->gameSettings->gameStyle = $RA__GAME_STYLES[2];
}
// skip AI_info if exists
$pos = 0x0C;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$include_ai = $unpacked_data[1];
if ($include_ai == 0x01)
{
$pos += 2;
$packed_data = substr ($this->headerStream, $pos, 2); $pos += 2;
$unpacked_data = unpack ("v", $packed_data);
$num_string = $unpacked_data[1];
$pos += 4;
for ($i = 0; $i {
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$string_length = $unpacked_data[1];
$pos += $string_length;
}
$pos += 6;
// AI_data
for ($i = 0; $i {
$pos += 10;
$packed_data = substr ($this->headerStream, $pos, 2); $pos += 2;
$unpacked_data = unpack ("v", $packed_data);
$num_rule = $unpacked_data[1];
$pos += 4;
$pos = $pos + (400 * $num_rule);
}
$pos += 5544;
}
// getting data
$pos += 4;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$game_speed = $unpacked_data[1];
$pos += 37;
$packed_data = substr ($this->headerStream, $pos, 2); $pos += 2;
$unpacked_data = unpack ("v", $packed_data);
$rec_player_ref = $unpacked_data[1];
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$num_player = $unpacked_data[1];
$rec_player_ref--; // 0 is GAIA, not appears in playerList
$num_player--;
$this->gameSettings->speed = $RA__GAME_SPEEDS[$game_speed];
if ($player = $this->playerList->getPlayer ($rec_player_ref))
{
$player->owner = true;
}
if ($num_player playerList->getCount ())
{
$this->gameSettings->inGameCoop = true;
}
// getting map
$pos += 62;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$map_size_x = $unpacked_data[1];
$this->mapWidth = $map_size_x;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$map_size_y = $unpacked_data[1];
$this->mapHeight = $map_size_y;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$num_unknown_data = $unpacked_data[1];
// unknown data
for ($i = 0; $i {
$pos += 1275;
$pos += $map_size_x * $map_size_y;
$packed_data = substr ($this->headerStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$num_float = $unpacked_data[1];
/*
if (num_float != 0x29)
{
// error = 1
break;
}
*/
$pos += 4 * $num_float;
$pos += 4;
}
$pos += 2;
// map data
// if error == 0 { do following + set flag map_loaded }
$x = $y = 1;
for ($i = 0; $i {
// terrain_id
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$terrain_id = $unpacked_data[1];
// elevation
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$elevation = $unpacked_data[1];
if ($x == $map_size_x + 1)
{
$x = 1;
$y++;
}
$this->mapData[$x][$y][0] = $terrain_id;
// TODO: elevation
// we do not get elevation (memory exhaustive)
// $this->mapData[$x][$y][1] = $elevation;
$x++;
}
$pos += 128;
$pos += $map_size_x * $map_size_y * 4;
$pos += 15;
/*
// TODO: test for behavior if there is a Computer
// getting Player_info position
$len = strlen ($unknown_const2);
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
while ($pos {
if (strcmp ($buff, $unknown_const2) == 0)
{
break;
}
$pos -= $len - 1;
$buff = substr ($this->headerStream, $pos, $len); $pos += $len;
}
*/
$pos += 5138;
foreach ($this->playerList as $player)
{
// skip cooping player, he/she has no data in Player_info
$player_ = $this->playerList->getPlayerByIndex ($player->index);
if ($player_ && ($player_ !== $player) && $player_->civId)
{
$player->civId = $player_->civId;
$player->civ = $player_->civ;
$player->colorId = $player_->colorId;
$player->isCooping = true;
continue;
}
$playerName = $player->name;
while ($pos {
$buff = substr ($this->headerStream, $pos, strlen ($playerName));
if (strcmp ($buff, $playerName) == 0)
{
break;
}
$pos++;
}
$pos += strlen ($playerName);
// skip data (first byte is null char player's name terminator)
$pos += 817;
// civilization
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$civilization = $unpacked_data[1];
$player->civId = $civilization;
$player->civ = $RA__CIVS[$civilization][0];
// skip unknown9[3]
$pos += 3;
// player_color
$packed_data = substr ($this->headerStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$player_color = $unpacked_data[1];
$player->colorId = $player_color;
$pos += 4299;
} // endfor
// initialize variables
$con1 = pack ('c*', 0x3A, 0x20);
$con2 = pack ('c*', 0xA1, 0x47);
$LANGUAGES = array (
'en' => pack ('c*', 0x4D, 0x61, 0x70, 0x20, 0x54, 0x79, 0x70, 0x65),
'cz' => pack ('c*', 0x54, 0x79, 0x70, 0x20, 0x6D, 0x61, 0x70, 0x79),
'jp' => pack ('c*', 0x83, 0x7D, 0x83, 0x62, 0x83, 0x76, 0x82, 0xCC, 0x8E, 0xED, 0x97, 0xDE),
'cn' => pack ('c*', 0xB5, 0xD8, 0xCD, 0xBC, 0xC0, 0xE0, 0xD0, 0xCD),
'sp' => pack ('c*', 0x54, 0x69, 0x70, 0x6F, 0x20, 0x64, 0x65, 0x20, 0x6D, 0x61, 0x70, 0x61),
'de' => pack ('c*', 0x4B, 0x61, 0x72, 0x74, 0x65, 0x6E, 0x74, 0x79, 0x70),
'cn2' => pack ('c*', 0xA6, 0x61, 0xB9, 0xCF, 0xC3, 0xFE, 0xA7, 0x4F),
'kr' => pack ('c*', 0xC7, 0xA5, 0xC1, 0xD8, 0x0A, 0xC0, 0xDA, 0xBF, 0xF8),
'fr' => pack ('c*', 0x54, 0x79, 0x70, 0x65, 0x20, 0x64, 0x65, 0x20, 0x63, 0x61, 0x72, 0x74, 0x65, 0xA0),
'it' => pack ('c*', 0x54, 0x69, 0x70, 0x6F, 0x20, 0x64, 0x69, 0x20, 0x6D, 0x61, 0x70, 0x70, 0x61),
'sp2' => pack ('c*', 0x54, 0x69, 0x70, 0x6F, 0x20, 0x64, 0x65, 0x20, 0x4D, 0x61, 0x70, 0x61),
'ur' => pack ('c*', 0xD2, 0xE8, 0xEF, 0x20, 0xCA, 0xE0, 0xF0, 0xF2, 0xFB));
// getting map name (only if map is custom (44) and game type is not scenario (game_type still $00 in scenarios)
// if map_id = 32 (Random Land Map), it is possible to obtain map if it's written in english
if (!array_key_exists ($map_id, $RA__MAPS) && $game_type != 0x03 && !$this->gameSettings->isScenario)
{
$pos = $game_setting_pos - 11520;
$mapFound = false;
$buff = substr ($this->headerStream, $pos, 2); $pos += 2;
// searching up to -100000 bytes, than stop
while ($pos > $game_setting_pos - 11520 - 100000 && !$mapFound)
{
if (strcmp ($buff, $con1) == 0 || strcmp ($buff, $con2) == 0)
{
$pos -= 2;
foreach ($LANGUAGES as $val)
{
$pos -= strlen ($val);
$map_name = substr ($this->headerStream, $pos, strlen ($val)); $pos += strlen ($val);
if (strcmp ($map_name, $val) == 0)
{
$mapName = '';
$pos += 2; // skip ': '
for ($i = 0; $i {
$buff = substr ($this->headerStream, $pos, 1); $pos += 1;
if ($buff != chr (0x0A))
{
$mapName .= $buff;
}
else
{
$mapFound = true;
break;
}
} // endfor
break;
} // endif
} // end foreach
} // endif
$pos -= 3;
$buff = substr ($this->headerStream, $pos, 2); $pos += 2;
} // endwhile
$this->gameSettings->map = ($mapFound) ? $mapName : $RA__GAME_STYLES[2];
} // endif
// build teams
foreach ($this->playerList as $player)
{
if ($player->team == 0)
{
$found = false;
foreach ($this->teams as $team)
{
if ($team->getIndex () != $player->team)
{
continue;
}
foreach ($team as $player_)
{
if ($player_->index == $player->index)
{
$team->addPlayer ($player);
$found = true;
break;
}
}
if ($found)
{
break;
}
}
if (!$found)
{
$team = new Team ();
$team->addPlayer ($player);
$this->teams->addTeam ($team);
}
}
else
{
if ($team = $this->teams->getTeamByIndex ($player->team))
{
$team->addPlayer ($player);
}
else
{
$team = new Team ();
$team ->addPlayer ($player);
$this->teams->addTeam ($team);
}
}
}
return true;
}
/**
* Analyzes body stream.
*
* @return bool true if successfully analyzed, otherwise false
*/
private function analyzeBodyStream ()
{
$time_cnt = $pos = $trading_cnt = 0;
$m_body_len = strlen ($this->bodyStream);
$age_flag = array (0, 0, 0, 0, 0, 0, 0, 0);
if (defined ('RA__DEBUG'))
{
global $RA__RESOURCES;
}
while ($pos {
$packed_data = substr ($this->bodyStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$type = $unpacked_data[1];
if (defined ('RA__DEBUG'))
{
printf (nl2br ("type: %d/n"), $type);
}
switch ($type)
{
case 4:
$packed_data = substr ($this->bodyStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$command = $unpacked_data[1];
if ($command == 0x01F4)
{
// Game_start
$pos += 20;
}
elseif ($command == -1)
{
// Chat
$packed_data = substr ($this->bodyStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$chat_len = $unpacked_data[1];
for ($i = 0; $i playerList->getCount (); $i++)
{
if (!($player = $this->playerList->getPlayer ($i)))
{
continue;
}
if ($player->feudalTime != 0 && $player->feudalTime {
$this->ingameChat[] = sprintf ('%d@#0%s advanced to Feudal Age', $player->feudalTime, $player->name);
$age_flag[$i] = 1;
}
if ($player->castleTime != 0 && $player->castleTime {
$this->ingameChat[] = sprintf ('%d@#0%s advanced to Castle Age', $player->castleTime, $player->name);
$age_flag[$i] = 1;
}
if ($player->imperialTime != 0 && $player->imperialTime {
$this->ingameChat[] = sprintf ('%d@#0%s advanced to Imperial Age', $player->imperialTime, $player->name);
$age_flag[$i] = 1;
}
}
$chat = substr ($this->bodyStream, $pos, $chat_len); $pos += $chat_len;
if ($chat[0] == '@' && $chat[1] == '#' && $chat[2] >= '1' && $chat[2] {
$chat = rtrim ($chat); // throw null-termination character
if (substr ($chat, 3, 2) == '--' && substr ($chat, -2) == '--')
{
}
else
{
$this->ingameChat[] = sprintf ('%d%s', $time_cnt, $chat);
}
}
}
break;
case 2:
// Sync
$packed_data = substr ($this->bodyStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$time_cnt += $unpacked_data[1]; // time_cnt is in miliseconds
$packed_data = substr ($this->bodyStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$unknown = $unpacked_data[1];
if ($unknown == 0)
{
$pos += 28;
}
$pos += 12;
break;
case 1:
// Command
$packed_data = substr ($this->bodyStream, $pos, 4); $pos += 4;
$unpacked_data = unpack ("V", $packed_data);
$length = $unpacked_data[1];
$packed_data = substr ($this->bodyStream, $pos, 1);
$unpacked_data = unpack ("C", $packed_data);
$command = $unpacked_data[1];
if (defined ('RA__DEBUG'))
{
$packed_data = substr ($this->bodyStream, $pos, 2);
$unpacked_data = unpack ("v", $packed_data);
$cmd = $unpacked_data[1];
$format = nl2br ("(%s): 0x%02x - 0x%02x -> len: %d/n");
printf ($format, self::gameTimeToString ($time_cnt), $cmd, $command, $length);
}
if (defined ('RA__DEBUG'))
{
$output = '';
for ($i = $pos + 1; $i {
$packed_data = substr ($this->bodyStream, $i, 1);
$unpacked_data = unpack ("C", $packed_data);
$c = $unpacked_data[1];
$output .= $c . '|';
}
print (nl2br ($output . "/n"));
}
switch ($command)
{
case 0x0B:
$pos += 1;
$packed_data = substr ($this->bodyStream, $pos, 1); $pos += 1;
$unpacked_data = unpack ("C", $packed_data);
$player_number = $unpacked_data[1];
$pos += 2;
if ($player = $this->playerList->getPlayerByIndex ($player_number))
{
$player->resignTime = $time_cnt;
$this->ingameChat[] = sprintf ('%d@#0%s resigned', $player->resignTime, $player->name);
if (defined ('RA__DEBUG'))
{
$format = nl2br ("(%s) resigned player #%d: %s/n");
printf ($format, self::gameTimeToString ($time_cnt), $player_number, $player->name);
}
}
$pos += $length - 4; // different from mgx format description
break;
case 0x65: // researches
$pos += 8;
// player_id
$packed_data = substr ($this->bodyStream, $pos, 2); $pos += 2;
$unpacked_data = unpack ("v", $packed_data);
$player_id = $unpacked_data[1];
// research_id
$packed_data = substr ($this->bodyStream, $pos, 2); $pos += 2;
$unpacked_data = unpack ("v", $packed_data);
$research_id = $unpacked_data[1];
if ($research_id == 101)
{
// feudal time
if ($player = $this->playerList->getPlayerByIndex ($player_id))
{
$player->feudalTime = $time_cnt + 130 * 1000; // + research time (2:10)
}
}
if ($research_id == 102)
{
// castle time
if ($player = $this->playerList->getPlayerByIndex ($player_id))
{
// persians?
$player->castleTime = ($player->civId == 0x08) ? $time_cnt + 144 * 1000 : $time_cnt + 160 * 1000;
}
}
if ($research_id == 103)
{
// imperial time
if ($player = $this->playerList->getPlayerByIndex ($player_id))
{
// persians?
$player->imperialTime = ($player->civId == 0x08) ? $time_cnt + 162 * 1000 : $time_cnt + 190 * 1000;
}
}
// else
{
if ($player = $this->playerList->getPlayerByIndex ($player_id))
{
$player->researches[$research_id] = $time_cnt;
}
}
if (defined ('RA__DEBUG'))
{
$format = nl2br ("#%d -> %d/n");
printf ($format, $player_id, $research_id);
}
$pos += ($length - 12);
break;
case 0x77
推荐阅读