* Inspired by TMNDataFetcher & "Stats for TMN Aseco+RASP" * * v1.19: Allowed 24-char UIDs too * v1.18: Added 'replayurl' to the entries in $recordslist * v1.17: Allowed 25-char UIDs too * v1.16: Improved error reporting via $error; parsed more comment formatting * v1.15: Added User-Agent to the GET request * v1.14: Fixed PHP Strict level warning * v1.13: Fixed handling of empty API responses * v1.12: Renamed $worldrec into $custimg to correct its meaning; fixed * $replayid check * v1.11: Added another check for valid $replayid * v1.10: Added magic __set_state function to support var_export() * v1.9: Fixed fetch records run-time warnings * v1.8: Optimized get_file URL parsing * v1.7: Added $worldrec (boolean); changed $visible to boolean; renamed * $download to $dloadurl, $imgurl to $imageurl & $imgurlsmall to * $thumburl; minor tweaks * v1.6: Added check for valid $replayid & $replayurl * v1.5: Added TMNF compatibility * v1.4: Added get_file function to handle TMX site timeouts * v1.3: Added $awards, $comments, $replayid, $replayurl; renamed $comment to * $acomment (to better distinguish author comment from # of comments) * v1.2: Allowed 26-char UIDs too; added $pageurl * v1.1: Allowed TMX IDs too * v1.0: Initial release */ class TMXInfoFetcher { public $section, $prefix, $uid, $id, $records, $error, $name, $userid, $author, $uploaded, $updated, $visible, $type, $envir, $mood, $style, $routes, $length, $diffic, $lbrating, $awards, $comments, $custimg, $game, $acomment, $pageurl, $replayid, $replayurl, $imageurl, $thumburl, $dloadurl, $recordlist; /** * Fetches a hell of a lot of data about a TMX track * * @param String $game * TMX section for 'TMO', 'TMS', 'TMN', 'TMU', 'TMNF' * @param String $id * The challenge UID to search for (if a 24-27 char alphanum string), * otherwise the TMX ID to search for (if a number) * @param Boolean $records * If true, the script also returns the world records (max. 10) * @return TMXInfoFetcher * If $error is not an empty string, it's an error message */ public function TMXInfoFetcher($game, $id, $records) { $this->section = $game; switch ($game) { case 'TMO': $this->prefix = 'original'; break; case 'TMS': $this->prefix = 'sunrise'; break; case 'TMN': $this->prefix = 'nations'; break; case 'TMU': $this->prefix = 'united'; break; case 'TMNF': $this->prefix = 'tmnforever'; break; default: $this->prefix = ''; return; } $this->error = ''; $this->records = $records; // check for UID string if (preg_match('/^\w{24,27}$/', $id)) { $this->uid = $id; $this->getData(true); // check for TMX ID } elseif (is_numeric($id) && $id > 0) { $this->id = floor($id); $this->getData(false); } } // TMXInfoFetcher public static function __set_state($import) { $tmx = new TMXInfoFetcher('', 0, false); $tmx->section = $import['section']; $tmx->prefix = $import['prefix']; $tmx->uid = $import['uid']; $tmx->id = $import['id']; $tmx->records = $import['records']; $tmx->error = ''; $tmx->name = $import['name']; $tmx->userid = $import['userid']; $tmx->author = $import['author']; $tmx->uploaded = $import['uploaded']; $tmx->updated = $import['updated']; $tmx->visible = $import['visible']; $tmx->type = $import['type']; $tmx->envir = $import['envir']; $tmx->mood = $import['mood']; $tmx->style = $import['style']; $tmx->routes = $import['routes']; $tmx->length = $import['length']; $tmx->diffic = $import['diffic']; $tmx->lbrating = $import['lbrating']; $tmx->awards = $import['awards']; $tmx->comments = $import['comments']; $tmx->custimg = $import['custimg']; $tmx->game = $import['game']; $tmx->acomment = $import['acomment']; $tmx->pageurl = $import['pageurl']; $tmx->replayid = $import['replayid']; $tmx->replayurl = $import['replayurl']; $tmx->imageurl = $import['imageurl']; $tmx->thumburl = $import['thumburl']; $tmx->dloadurl = $import['dloadurl']; $tmx->recordlist = null; return $tmx; } // __set_state private function getData($isuid) { // get main track info $url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apitrackinfo&' . ($isuid ? 'u' : '') . 'id=' . ($isuid ? $this->uid : $this->id); $file = $this->get_file($url); if ($file === false) { $this->error = 'Connection or response error on ' . $url; return; } else if ($file === -1) { $this->error = 'Timed out while reading data from ' . $url; return; } else if ($file == '') { $this->error = 'No data returned from ' . $url; return; } // check for API error message if (strpos($file, chr(27)) !== false) { $this->error = 'Cannot decode main track info'; return; } // separate columns on Tabs $fields = explode(chr(9), $file); if ($isuid) $this->id = $fields[0]; $this->name = $fields[1]; $this->userid = $fields[2]; $this->author = $fields[3]; $this->uploaded = $fields[4]; $this->updated = $fields[5]; $this->visible = (strtolower($fields[6]) == 'true'); $this->type = $fields[7]; $this->envir = $fields[8]; $this->mood = $fields[9]; $this->style = $fields[10]; $this->routes = $fields[11]; $this->length = $fields[12]; $this->diffic = $fields[13]; $this->lbrating = ($fields[14] > 0 ? $fields[14] : 'Classic!'); $this->game = $fields[15]; $search = array(chr(31), '[b]', '[/b]', '[i]', '[/i]', '[u]', '[/u]', '[url]', '[/url]'); $replace = array('
', '', '', '', '', '', '', '', ''); $this->acomment = str_ireplace($search, $replace, $fields[16]); $this->acomment = preg_replace('/\[url=".*"\]/', '', $this->acomment); $this->pageurl = 'http://' . $this->prefix . '.tm-exchange.com/main.aspx?action=trackshow&id=' . $this->id; $this->imageurl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=trackscreen&id=' . $this->id; $this->thumburl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=trackscreensmall&id=' . $this->id; $this->dloadurl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=trackgbx&id=' . $this->id; $this->awards = 0; $this->comments = 0; $this->custimg = false; $this->replayid = 0; $this->replayurl = ''; // get misc. track info $url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apisearch&trackid=' . $this->id; $file = $this->get_file($url); if ($file === false || $file === -1 || $file == '') // no error message if main info was already fetched return; // check for API error message if (strpos($file, chr(27)) !== false) return; // separate columns on Tabs $fields = explode(chr(9), $file); // id = $fields[0]; // name = $fields[1]; // userid = $fields[2]; // author = $fields[3]; // type = $fields[4]; // envir = $fields[5]; // mood = $fields[6]; // style = $fields[7]; // routes = $fields[8]; // length = $fields[9]; // diffic = $fields[10]; // lbrating = ($fields[11] > 0 ? $fields[11] : 'Classic!'); $this->awards = $fields[12]; $this->comments = $fields[13]; $this->custimg = (strtolower($fields[14]) == 'true'); // game = $fields[15]; $this->replayid = $fields[16]; // unknown = $fields[17-21]; // uploaded = $fields[22]; // updated = $fields[23]; if ($this->replayid > 0) { $this->replayurl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=recordgbx&id=' . $this->replayid; } // fetch records too? $this->recordlist = array(); if ($this->records) { $url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apitrackrecords&id=' . $this->id; $file = $this->get_file($url); if ($file === false || $file === -1 || $file == '') // no error message if main info was already fetched return; $file = explode("\r\n", $file); $i = 0; while ($i < 10 && isset($file[$i]) && $file[$i] != '') { // separate columns on Tabs $fields = explode(chr(9), $file[$i]); $this->recordlist[$i++] = array( 'replayid' => $fields[0], 'userid' => $fields[1], 'name' => $fields[2], 'time' => $fields[3], 'replayat' => $fields[4], 'trackat' => $fields[5], 'approved' => $fields[6], 'score' => $fields[7], 'expires' => $fields[8], 'lockspan' => $fields[9], 'replayurl'=> 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=recordgbx&id=' . $fields[0], ); } } } // getData // Simple HTTP Get function with timeout // ok: return string || error: return false || timeout: return -1 private function get_file($url) { $url = parse_url($url); $port = isset($url['port']) ? $url['port'] : 80; $query = isset($url['query']) ? "?" . $url['query'] : ""; $fp = @fsockopen($url['host'], $port, $errno, $errstr, 4); if (!$fp) return false; fwrite($fp, 'GET ' . $url['path'] . $query . " HTTP/1.0\r\n" . 'Host: ' . $url['host'] . "\r\n" . 'User-Agent: TMXInfoFetcher (' . PHP_OS . ")\r\n\r\n"); stream_set_timeout($fp, 2); $res = ''; $info['timed_out'] = false; while (!feof($fp) && !$info['timed_out']) { $res .= fread($fp, 512); $info = stream_get_meta_data($fp); } fclose($fp); if ($info['timed_out']) { return -1; } else { if (substr($res, 9, 3) != '200') return false; $page = explode("\r\n\r\n", $res, 2); return trim($page[1]); } } // get_file } // class TMXInfoFetcher ?>