diff --git a/application/core/GbxDataFetcher/gbxdatafetcher.inc.php b/application/core/GbxDataFetcher/gbxdatafetcher.inc.php
new file mode 100644
index 00000000..4585d239
--- /dev/null
+++ b/application/core/GbxDataFetcher/gbxdatafetcher.inc.php
@@ -0,0 +1,1431 @@
+
+ * Thanks to Electron for additional input & prototyping
+ * Based on information at http://en.tm-wiki.org/wiki/GBX,
+ * http://www.tm-forum.com/viewtopic.php?p=192817#p192817
+ * and http://en.tm-wiki.org/wiki/PAK#Header_versions_8.2B
+ *
+ * v2.5: Add lookback string Valley; strip optional digits from mood; fix empty
+ * thumbnail warning
+ * v2.4: Update GBXChallMapFetcher & GBXPackFetcher version-dependent processing;
+ * update lookback strings
+ * v2.3: Add UTF-8 decoding of parsed XML chunk's elements; strip UTF-8 BOM
+ * from various string fields
+ * v2.2: Add class GBXPackFetcher; limit loadGBXdata() to first 256KB
+ * v2.1: Add exception codes; add $thumbLen to GBXChallMapFetcher
+ * v2.0: Complete rewrite
+ */
+
+/**
+ * @class GBXBaseFetcher
+ * @brief The base GBX class with all common functionality
+ */
+class GBXBaseFetcher
+{
+ public $parseXml, $xml, $xmlParsed;
+
+ public $authorVer, $authorLogin, $authorNick, $authorZone, $authorEInfo;
+
+ private $_gbxdata, $_gbxlen, $_gbxptr, $_debug, $_error, $_endianess,
+ $_lookbacks, $_parsestack;
+
+ // supported class ID's
+ const GBX_CHALLENGE_TMF = 0x03043000;
+ const GBX_AUTOSAVE_TMF = 0x03093000;
+ const GBX_CHALLENGE_TM = 0x24003000;
+ const GBX_AUTOSAVE_TM = 0x2403F000;
+ const GBX_REPLAY_TM = 0x2407E000;
+
+ const MACHINE_ENDIAN_ORDER = 0;
+ const LITTLE_ENDIAN_ORDER = 1;
+ const BIG_ENDIAN_ORDER = 2;
+
+ const LOAD_LIMIT = 256; // KBs to read in loadGBXdata()
+
+ // initialise new instance
+ public function __construct()
+ {
+ $this->parseXml = false;
+ $this->xml = '';
+ $this->xmlParsed = array();
+ $this->_debug = false;
+ $this->_error = '';
+
+ list($endiantest) = array_values(unpack('L1L', pack('V', 1)));
+ if ($endiantest != 1)
+ $this->_endianess = self::BIG_ENDIAN_ORDER;
+ else
+ $this->_endianess = self::LITTLE_ENDIAN_ORDER;
+
+ $this->clearGBXdata();
+ $this->clearLookbacks();
+ $this->_parsestack = array();
+
+ $this->authorVer = 0;
+ $this->authorLogin = '';
+ $this->authorNick = '';
+ $this->authorZone = '';
+ $this->authorEInfo = '';
+ }
+
+ // enable debug logging
+ protected function enableDebug()
+ {
+ $this->_debug = true;
+ }
+
+ // disable debug logging
+ protected function disableDebug()
+ {
+ $this->_debug = false;
+ }
+
+ // print message to stderr if debugging
+ protected function debugLog($msg)
+ {
+ if ($this->_debug)
+ fwrite(STDERR, $msg."\n");
+ }
+
+ // set error message prefix
+ protected function setError($prefix)
+ {
+ $this->_error = (string)$prefix;
+ }
+
+ // exit with error exception
+ protected function errorOut($msg, $code = 0)
+ {
+ $this->clearGBXdata();
+ throw new Exception($this->_error . $msg, $code);
+ }
+
+ // read in raw GBX data
+ protected function loadGBXdata($filename)
+ {
+ $gbxdata = @file_get_contents($filename, false, null, 0, self::LOAD_LIMIT * 1024);
+ if ($gbxdata !== false)
+ $this->storeGBXdata($gbxdata);
+ else
+ $this->errorOut('Unable to read GBX data from ' . $filename, 1);
+ }
+
+ // store raw GBX data
+ protected function storeGBXdata($gbxdata)
+ {
+ $this->_gbxdata = & $gbxdata;
+ $this->_gbxlen = strlen($gbxdata);
+ $this->_gbxptr = 0;
+ }
+
+ // clear GBX data (to avoid print_r problems & reduce memory usage)
+ protected function clearGBXdata()
+ {
+ $this->storeGBXdata('');
+ }
+
+ // get GBX pointer
+ protected function getGBXptr()
+ {
+ return $this->_gbxptr;
+ }
+
+ // set GBX pointer
+ protected function setGBXptr($ptr)
+ {
+ $this->_gbxptr = (int)$ptr;
+ }
+
+ // move GBX pointer
+ protected function moveGBXptr($len)
+ {
+ $this->_gbxptr += (int)$len;
+ }
+
+ // read $len bytes from GBX data
+ protected function readData($len)
+ {
+ if ($this->_gbxptr + $len > $this->_gbxlen)
+ $this->errorOut(sprintf('Insufficient data for %d bytes at pos 0x%04X',
+ $len, $this->_gbxptr), 2);
+ $data = '';
+ while ($len-- > 0)
+ $data .= $this->_gbxdata[$this->_gbxptr++];
+ return $data;
+ }
+
+ // read signed byte from GBX data
+ protected function readInt8()
+ {
+ $data = $this->readData(1);
+ list(, $int8) = unpack('c*', $data);
+ return $int8;
+ }
+
+ // read signed short from GBX data
+ protected function readInt16()
+ {
+ $data = $this->readData(2);
+ if ($this->_endianess == self::BIG_ENDIAN_ORDER)
+ $data = strrev($data);
+ list(, $int16) = unpack('s*', $data);
+ return $int16;
+ }
+
+ // read signed long from GBX data
+ protected function readInt32()
+ {
+ $data = $this->readData(4);
+ if ($this->_endianess == self::BIG_ENDIAN_ORDER)
+ $data = strrev($data);
+ list(, $int32) = unpack('l*', $data);
+ return $int32;
+ }
+
+ // read string from GBX data
+ protected function readString()
+ {
+ $gbxptr = $this->getGBXptr();
+ $len = $this->readInt32();
+ $len &= 0x7FFFFFFF;
+ if ($len <= 0 || $len >= 0x12000) { // for large XML & Data blocks
+ if ($len != 0)
+ $this->errorOut(sprintf('Invalid string length %d (0x%04X) at pos 0x%04X',
+ $len, $len, $gbxptr), 3);
+ }
+ $data = $this->readData($len);
+ return $data;
+ }
+
+ // strip UTF-8 BOM from string
+ protected function stripBOM($str)
+ {
+ return str_replace("\xEF\xBB\xBF", '', $str);
+ }
+
+ // clear lookback strings
+ protected function clearLookbacks()
+ {
+ $this->_lookbacks = array();
+ }
+
+ // read lookback string from GBX data
+ protected function readLookbackString()
+ {
+ if (empty($this->_lookbacks)) {
+ $version = $this->readInt32();
+ if ($version != 3)
+ $this->errorOut('Unknown lookback strings version: ' . $version, 4);
+ }
+
+ // check index
+ $index = $this->readInt32();
+ if ($index == -1) {
+ // unassigned (empty) string
+ $str = '';
+ } elseif (($index & 0xC0000000) == 0) {
+ // use external reference string
+ switch ($index) {
+ case 11: $str = 'Valley';
+ break;
+ case 12: $str = 'Canyon';
+ break;
+ case 17: $str = 'TMCommon';
+ break;
+ case 202: $str = 'Storm';
+ break;
+ case 299: $str = 'SMCommon';
+ break;
+ case 10003: $str = 'Common';
+ break;
+ default: $str = 'UNKNOWN';
+ }
+ } elseif (($index & 0x3FFFFFFF) == 0) {
+ // read string & add to lookbacks
+ $str = $this->readString();
+ $this->_lookbacks[] = $str;
+ } else {
+ // use string from lookbacks
+ $str = $this->_lookbacks[($index & 0x3FFFFFFF) - 1];
+ }
+
+ return $str;
+ }
+
+ // XML parser functions
+ private function startTag($parser, $name, $attribs)
+ {
+ foreach ($attribs as $key => &$val)
+ $val = utf8_decode($val);
+ //echo 'startTag: ' . $name . "\n"; print_r($attribs);
+ array_push($this->_parsestack, $name);
+ if ($name == 'DEP') {
+ $this->xmlParsed['DEPS'][] = $attribs;
+ } elseif (count($this->_parsestack) <= 2) {
+ // HEADER, IDENT, DESC, TIMES, CHALLENGE/MAP, DEPS, CHECKPOINTS
+ $this->xmlParsed[$name] = $attribs;
+ }
+ }
+
+ private function charData($parser, $data)
+ {
+ //echo 'charData: ' . $data . "\n";
+ if (count($this->_parsestack) == 3)
+ $this->xmlParsed[$this->_parsestack[1]][$this->_parsestack[2]] = $data;
+ elseif (count($this->_parsestack) > 3)
+ $this->debugLog('XML chunk nested too deeply: ', print_r($this->_parsestack, true));
+ }
+
+ private function endTag($parser, $name)
+ {
+ //echo 'endTag: ' . $name . "\n";
+ array_pop($this->_parsestack);
+ }
+
+ protected function parseXMLstring()
+ {
+ // define a dedicated parser to handle the attributes
+ $xml_parser = xml_parser_create();
+ xml_set_object($xml_parser, $this);
+ xml_set_element_handler($xml_parser, 'startTag', 'endTag');
+ xml_set_character_data_handler($xml_parser, 'charData');
+
+ // escape '&' characters unless already a known entity
+ $xml = preg_replace('/&(?!(?:amp|quot|apos|lt|gt);)/', '&', $this->xml);
+
+ if (!xml_parse($xml_parser, utf8_encode($xml), true))
+ $this->errorOut(sprintf('XML chunk parse error: %s at line %d',
+ xml_error_string(xml_get_error_code($xml_parser)),
+ xml_get_current_line_number($xml_parser)), 12);
+
+ xml_parser_free($xml_parser);
+ }
+
+ /**
+ * Check GBX header, main class ID & header block
+ * @param Array $classes
+ * The main class IDs accepted for this GBX
+ * @return Size of GBX header block
+ */
+ protected function checkHeader(array $classes)
+ {
+ // check magic header
+ $data = $this->readData(3);
+ $version = $this->readInt16();
+ if ($data != 'GBX' || $version != 6)
+ $this->errorOut('No magic GBX header', 5);
+
+ // check header block (un)compression
+ $data = $this->readData(4);
+ if ($data[1] != 'U')
+ $this->errorOut('Compressed GBX header block not supported', 6);
+
+ // check main class ID
+ $mainClass = $this->readInt32();
+ if (!in_array($mainClass, $classes))
+ $this->errorOut(sprintf('Main class ID %08X not supported', $mainClass), 7);
+ $this->debugLog(sprintf('GBX main class ID: %08X', $mainClass));
+
+ // get header size
+ $headerSize = $this->readInt32();
+ if ($headerSize == 0)
+ $this->errorOut('No GBX header block', 8);
+
+ $this->debugLog(sprintf('GBX header block size: %d (%.1f KB)',
+ $headerSize, $headerSize / 1024));
+ return $headerSize;
+ } // checkHeader
+
+ /**
+ * Get list of chunks from GBX header block
+ * @param Int $headerSize
+ * Size of header block (chunks list & chunks data)
+ * @param array $chunks
+ * List of chunk IDs & names
+ * @return List of chunk offsets & sizes
+ */
+ protected function getChunksList($headerSize, array $chunks)
+ {
+ // get number of chunks
+ $numChunks = $this->readInt32();
+ if ($numChunks == 0)
+ $this->errorOut('No GBX header chunks', 9);
+
+ $this->debugLog('GBX number of header chunks: ' . $numChunks);
+ $chunkStart = $this->getGBXptr();
+ $this->debugLog(sprintf('GBX start of chunk list: 0x%04X', $chunkStart));
+ $chunkOffset = $chunkStart + $numChunks * 8;
+
+ // get list of all chunks
+ $chunksList = array();
+ for ($i = 0; $i < $numChunks; $i++)
+ {
+ $chunkId = $this->readInt32();
+ $chunkSize = $this->readInt32();
+
+ $chunkId &= 0x00000FFF;
+ $chunkSize &= 0x7FFFFFFF;
+
+ if (array_key_exists($chunkId, $chunks)) {
+ $name = $chunks[$chunkId];
+ $chunksList[$name] = array(
+ 'off' => $chunkOffset,
+ 'size' => $chunkSize
+ );
+ } else {
+ $name = 'UNKNOWN';
+ }
+ $this->debugLog(sprintf('GBX chunk %2d %-8s Id 0x%03X Offset 0x%06X Size %6d',
+ $i, $name, $chunkId, $chunkOffset, $chunkSize));
+ $chunkOffset += $chunkSize;
+ }
+
+ //$this->debugLog(print_r($chunksList, true));
+ $totalSize = $chunkOffset - $chunkStart + 4; // numChunks
+ if ($headerSize != $totalSize)
+ $this->errorOut(sprintf('Chunk list size mismatch: %d <> %d',
+ $headerSize, $totalSize), 10);
+
+ return $chunksList;
+ } // getChunksList
+
+ /**
+ * Initialize for a new chunk
+ * @param int $offset
+ */
+ protected function initChunk($offset)
+ {
+ $this->setGBXptr($offset);
+ $this->clearLookbacks();
+ }
+
+ /**
+ * Get XML chunk from GBX header block & optionally parse it
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getXMLChunk(array $chunksList)
+ {
+ if (!isset($chunksList['XML'])) return;
+
+ $this->initChunk($chunksList['XML']['off']);
+ $this->xml = $this->readString();
+
+ if ($chunksList['XML']['size'] != strlen($this->xml) + 4)
+ $this->errorOut(sprintf('XML chunk size mismatch: %d <> %d',
+ $chunksList['XML']['size'], strlen($this->xml) + 4), 11);
+
+ if ($this->parseXml && $this->xml != '')
+ $this->parseXMLstring();
+ } // getXMLChunk
+
+ /**
+ * Get Author fields from GBX header block
+ */
+ protected function getAuthorFields()
+ {
+ $this->authorVer = $this->readInt32();
+ $this->authorLogin = $this->readString();
+ $this->authorNick = $this->stripBOM($this->readString());
+ $this->authorZone = $this->stripBOM($this->readString());
+ $this->authorEInfo = $this->readString();
+ } // getAuthorFields
+
+ /**
+ * Get Author chunk from GBX header block
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getAuthorChunk(array $chunksList)
+ {
+ if (!isset($chunksList['Author'])) return;
+
+ $this->initChunk($chunksList['Author']['off']);
+ $version = $this->readInt32();
+ $this->debugLog('GBX Author chunk version: ' . $version);
+
+ $this->getAuthorFields();
+ } // getAuthorChunk
+
+} // class GBXBaseFetcher
+
+
+/**
+ * @class GBXChallMapFetcher
+ * @brief The class that fetches all GBX challenge/map info
+ */
+class GBXChallMapFetcher extends GBXBaseFetcher
+{
+ public $tnImage;
+
+ public $headerVersn, $bronzeTime, $silverTime, $goldTime, $authorTime,
+ $cost, $multiLap, $type, $typeName, $authorScore, $simpleEdit,
+ $nbChecks, $nbLaps;
+ public $uid, $envir, $author, $name, $kind, $kindName, $password,
+ $mood, $envirBg, $authorBg, $mapType, $mapStyle, $lightmap, $titleUid;
+ public $xmlVer, $exeVer, $exeBld, $validated, $songFile, $songUrl,
+ $modName, $modFile, $modUrl;
+ public $thumbLen, $thumbnail, $comment;
+
+ const IMAGE_FLIP_HORIZONTAL = 1;
+ const IMAGE_FLIP_VERTICAL = 2;
+ const IMAGE_FLIP_BOTH = 3;
+
+ /**
+ * Mirror (flip) an image across horizontal, vertical or both axis
+ * Source: http://www.php.net/manual/en/function.imagecopy.php#85992
+ * @param String $imgsrc
+ * Source image data
+ * @param Int $dir
+ * Flip direction from the constants above
+ * @return Flipped image data if successful, otherwise source image data
+ */
+ private function imageFlip($imgsrc, $dir)
+ {
+ $width = imagesx($imgsrc);
+ $height = imagesy($imgsrc);
+ $src_x = 0;
+ $src_y = 0;
+ $src_width = $width;
+ $src_height = $height;
+
+ switch ((int)$dir) {
+ case self::IMAGE_FLIP_HORIZONTAL:
+ $src_y = $height;
+ $src_height = -$height;
+ break;
+ case self::IMAGE_FLIP_VERTICAL:
+ $src_x = $width;
+ $src_width = -$width;
+ break;
+ case self::IMAGE_FLIP_BOTH:
+ $src_x = $width;
+ $src_y = $height;
+ $src_width = -$width;
+ $src_height = -$height;
+ break;
+ default:
+ return $imgsrc;
+ }
+
+ $imgdest = imagecreatetruecolor($width, $height);
+ if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $src_x, $src_y,
+ $width, $height, $src_width, $src_height)) {
+ return $imgdest;
+ }
+ return $imgsrc;
+ } // imageFlip
+
+ /**
+ * Instantiate GBX challenge/map fetcher
+ *
+ * @param Boolean $parsexml
+ * If true, the fetcher also parses the XML block
+ * @param Boolean $tnimage
+ * If true, the fetcher also extracts the thumbnail image;
+ * if GD/JPEG libraries are present, image will be flipped upright,
+ * otherwise it will be in the original upside-down format
+ * Warning: this is binary data in JPEG format, 256x256 pixels for
+ * TMU/TMF or 512x512 pixels for MP
+ * @param Boolean $debug
+ * If true, the fetcher prints debug logging to stderr
+ * @return GBXChallMapFetcher
+ * If GBX data couldn't be extracted, an Exception is thrown with
+ * the error message & code
+ */
+ public function __construct($parsexml = false, $tnimage = false, $debug = false)
+ {
+ parent::__construct();
+
+ $this->headerVersn = 0;
+ $this->bronzeTime = 0;
+ $this->silverTime = 0;
+ $this->goldTime = 0;
+ $this->authorTime = 0;
+
+ $this->cost = 0;
+ $this->multiLap = false;
+ $this->type = 0;
+ $this->typeName = '';
+
+ $this->authorScore = 0;
+ $this->simpleEdit = false;
+ $this->nbChecks = 0;
+ $this->nbLaps = 0;
+
+ $this->uid = '';
+ $this->envir = '';
+ $this->author = '';
+ $this->name = '';
+ $this->kind = 0;
+ $this->kindName = '';
+
+ $this->password = '';
+ $this->mood = '';
+ $this->envirBg = '';
+ $this->authorBg = '';
+
+ $this->mapType = '';
+ $this->mapStyle = '';
+ $this->lightmap = 0;
+ $this->titleUid = '';
+
+ $this->xmlVer = '';
+ $this->exeVer = '';
+ $this->exeBld = '';
+ $this->validated = false;
+ $this->songFile = '';
+ $this->songUrl = '';
+ $this->modName = '';
+ $this->modFile = '';
+ $this->modUrl = '';
+
+ $this->thumbLen = 0;
+ $this->thumbnail = '';
+ $this->comment = '';
+
+ $this->parseXml = (bool)$parsexml;
+ $this->tnImage = (bool)$tnimage;
+ if ((bool)$debug)
+ $this->enableDebug();
+
+ $this->setError('GBX map error: ');
+ } // __construct
+
+ /**
+ * Process GBX challenge/map file
+ *
+ * @param String $filename
+ * The challenge filename
+ */
+ public function processFile($filename)
+ {
+ $this->loadGBXdata((string)$filename);
+
+ $this->processGBX();
+ } // processFile
+
+ /**
+ * Process GBX challenge/map data
+ *
+ * @param String $gbxdata
+ * The challenge/map data
+ */
+ public function processData($gbxdata)
+ {
+ $this->storeGBXdata((string)$gbxdata);
+
+ $this->processGBX();
+ } // processData
+
+ // process GBX data
+ private function processGBX()
+ {
+ // supported challenge/map class IDs
+ $challclasses = array(
+ self::GBX_CHALLENGE_TMF,
+ self::GBX_CHALLENGE_TM,
+ );
+
+ $headerSize = $this->checkHeader($challclasses);
+ $headerStart = $headerEnd = $this->getGBXptr();
+
+ // desired challenge/map chunk IDs
+ $chunks = array(
+ 0x002 => 'Info', // TM, MP
+ 0x003 => 'String', // TM, MP
+ 0x004 => 'Version', // TM, MP
+ 0x005 => 'XML', // TM, MP
+ 0x007 => 'Thumbnl', // TM, MP
+ 0x008 => 'Author', // MP
+ );
+
+ $chunksList = $this->getChunksList($headerSize, $chunks);
+
+ $this->getInfoChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getStringChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getVersionChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getXMLChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getThumbnlChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getAuthorChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ if ($headerSize != $headerEnd - $headerStart)
+ $this->errorOut(sprintf('Header size mismatch: %d <> %d',
+ $headerSize, $headerEnd - $headerStart), 16);
+
+ if ($this->parseXml) {
+ if (isset($this->xmlParsed['HEADER']['VERSION']))
+ $this->xmlVer = $this->xmlParsed['HEADER']['VERSION'];
+ if (isset($this->xmlParsed['HEADER']['EXEVER']))
+ $this->exeVer = $this->xmlParsed['HEADER']['EXEVER'];
+ if (isset($this->xmlParsed['HEADER']['EXEBUILD']))
+ $this->exeBld = $this->xmlParsed['HEADER']['EXEBUILD'];
+ if ($this->lightmap == 0 && isset($this->xmlParsed['HEADER']['LIGHTMAP']))
+ $this->lightmap = (int)$this->xmlParsed['HEADER']['LIGHTMAP'];
+ if ($this->authorZone == '' && isset($this->xmlParsed['IDENT']['AUTHORZONE']))
+ $this->authorZone = $this->xmlParsed['IDENT']['AUTHORZONE'];
+ if ($this->envir == 'UNKNOWN' && isset($this->xmlParsed['DESC']['ENVIR']))
+ $this->envir = $this->xmlParsed['DESC']['ENVIR'];
+ if ($this->nbLaps == 0 && isset($this->xmlParsed['DESC']['NBLAPS']))
+ $this->nbLaps = (int)$this->xmlParsed['DESC']['NBLAPS'];
+ if (isset($this->xmlParsed['DESC']['VALIDATED']))
+ $this->validated = (bool)$this->xmlParsed['DESC']['VALIDATED'];
+ if (isset($this->xmlParsed['DESC']['MOD']))
+ $this->modName = $this->xmlParsed['DESC']['MOD'];
+
+ // extract optional song & mod filenames
+ if (!empty($this->xmlParsed['DEPS'])) {
+ for ($i = 0; $i < count($this->xmlParsed['DEPS']); $i++) {
+ if (preg_match('/ChallengeMusics\\\\(.+)/', $this->xmlParsed['DEPS'][$i]['FILE'], $path)) {
+ $this->songFile = $path[1];
+ if (isset($this->xmlParsed['DEPS'][$i]['URL']))
+ $this->songUrl = $this->xmlParsed['DEPS'][$i]['URL'];
+ } elseif (preg_match('/.+\\\\Mod\\\\.+/', $this->xmlParsed['DEPS'][$i]['FILE'], $path)) {
+ $this->modFile = $path[0];
+ if (isset($this->xmlParsed['DEPS'][$i]['URL']))
+ $this->modUrl = $this->xmlParsed['DEPS'][$i]['URL'];
+ }
+ }
+ }
+ }
+
+ $this->clearGBXdata();
+ } // processGBX
+
+ /**
+ * Get Info chunk from GBX header block
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getInfoChunk(array $chunksList)
+ {
+ if (!isset($chunksList['Info'])) return;
+
+ $this->initChunk($chunksList['Info']['off']);
+ $version = $this->readInt8();
+ $this->debugLog('GBX Info chunk version: ' . $version);
+
+ if ($version < 3) {
+ $this->uid = $this->readLookbackString();
+
+ $this->envir = $this->readLookbackString();
+ $this->author = $this->readLookbackString();
+
+ $this->name = $this->stripBOM($this->readString());
+ }
+ $this->moveGBXptr(4); // skip bool 0
+
+ if ($version >= 1) {
+ $this->bronzeTime = $this->readInt32();
+
+ $this->silverTime = $this->readInt32();
+
+ $this->goldTime = $this->readInt32();
+
+ $this->authorTime = $this->readInt32();
+
+ if ($version == 2)
+ $this->moveGBXptr(1); // skip unknown byte
+
+ if ($version >= 4) {
+ $this->cost = $this->readInt32();
+
+ if ($version >= 5) {
+ $this->multiLap = (bool)$this->readInt32();
+
+ if ($version == 6)
+ $this->moveGBXptr(4); // skip unknown bool
+
+ if ($version >= 7) {
+ $this->type = $this->readInt32();
+ switch ($this->type) {
+ case 0: $this->typeName = 'Race';
+ break;
+ case 1: $this->typeName = 'Platform';
+ break;
+ case 2: $this->typeName = 'Puzzle';
+ break;
+ case 3: $this->typeName = 'Crazy';
+ break;
+ case 4: $this->typeName = 'Shortcut';
+ break;
+ case 5: $this->typeName = 'Stunts';
+ break;
+ case 6: $this->typeName = 'Script';
+ break;
+ default: $this->typeName = 'UNKNOWN';
+ }
+
+ if ($version >= 9) {
+ $this->moveGBXptr(4); // skip int32 0
+
+ if ($version >= 10) {
+ $this->authorScore = $this->readInt32();
+
+ if ($version >= 11) {
+ $this->simpleEdit = (bool)$this->readInt32();
+
+ if ($version >= 12) {
+ $this->moveGBXptr(4); // skip bool 0
+
+ if ($version >= 13) {
+ $this->nbChecks = $this->readInt32();
+
+ $this->nbLaps = $this->readInt32();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } // getInfoChunk
+
+ /**
+ * Get String chunk from GBX header block
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getStringChunk(array $chunksList)
+ {
+ if (!isset($chunksList['String'])) return;
+
+ $this->initChunk($chunksList['String']['off']);
+ $version = $this->readInt8();
+ $this->debugLog('GBX String chunk version: ' . $version);
+
+ $this->uid = $this->readLookbackString();
+
+ $this->envir = $this->readLookbackString();
+ $this->author = $this->readLookbackString();
+
+ $this->name = $this->stripBOM($this->readString());
+
+ $this->kind = $this->readInt8();
+ switch ($this->kind) {
+ case 0: $this->kindName = '(internal)EndMarker';
+ break;
+ case 1: $this->kindName = '(old)Campaign';
+ break;
+ case 2: $this->kindName = '(old)Puzzle';
+ break;
+ case 3: $this->kindName = '(old)Retro';
+ break;
+ case 4: $this->kindName = '(old)TimeAttack';
+ break;
+ case 5: $this->kindName = '(old)Rounds';
+ break;
+ case 6: $this->kindName = 'InProgress';
+ break;
+ case 7: $this->kindName = 'Campaign';
+ break;
+ case 8: $this->kindName = 'Multi';
+ break;
+ case 9: $this->kindName = 'Solo';
+ break;
+ case 10: $this->kindName = 'Site';
+ break;
+ case 11: $this->kindName = 'SoloNadeo';
+ break;
+ case 12: $this->kindName = 'MultiNadeo';
+ break;
+ default: $this->kindName = 'UNKNOWN';
+ }
+
+ if ($version >= 1) {
+ $this->moveGBXptr(4); // skip locked
+
+ $this->password = $this->readString();
+
+ if ($version >= 2) {
+ $this->mood = $this->readLookbackString();
+ $this->mood = preg_replace('/([A-Za-z]+)\d*/', '\1', $this->mood);
+
+ $this->envirBg = $this->readLookbackString();
+ $this->authorBg = $this->readLookbackString();
+
+ if ($version >= 3) {
+ $this->moveGBXptr(8); // skip mapOrigin
+
+ if ($version >= 4) {
+ $this->moveGBXptr(8); // skip mapTarget
+
+ if ($version >= 5) {
+ $this->moveGBXptr(16); // skip unknown int128
+
+ if ($version >= 6) {
+ $this->mapType = $this->readString();
+ $this->mapStyle = $this->readString();
+
+ if ($version <= 8)
+ $this->moveGBXptr(4); // skip unknown bool
+
+ if ($version >= 8) {
+ $this->moveGBXptr(8); // skip lightmapCacheUID
+
+ if ($version >= 9) {
+ $this->lightmap = $this->readInt8();
+
+ if ($version >= 11) {
+ $this->titleUid = $this->readLookbackString();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } // getStringChunk
+
+ /**
+ * Get Version chunk from GBX header block
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getVersionChunk(array $chunksList)
+ {
+ if (!isset($chunksList['Version'])) return;
+
+ $this->initChunk($chunksList['Version']['off']);
+ $this->headerVersn = $this->readInt32();
+ } // getVersionChunk
+
+ /**
+ * Get Thumbnail/Comments chunk from GBX header block
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getThumbnlChunk(array $chunksList)
+ {
+ if (!isset($chunksList['Thumbnl'])) return;
+
+ $this->initChunk($chunksList['Thumbnl']['off']);
+ $version = $this->readInt32();
+ $this->debugLog('GBX Thumbnail chunk version: ' . $version);
+
+ if ($version == 1) {
+ $thumbSize = $this->readInt32();
+ $this->debugLog(sprintf('GBX Thumbnail size: %d (%.1f KB)',
+ $thumbSize, $thumbSize / 1024));
+
+ $this->moveGBXptr(strlen(''));
+ $this->thumbnail = $this->readData($thumbSize);
+ $this->thumbLen = strlen($this->thumbnail);
+ $this->moveGBXptr(strlen(''));
+
+ $this->moveGBXptr(strlen(''));
+ $this->comment = $this->stripBOM($this->readString());
+ $this->moveGBXptr(strlen(''));
+
+ // return extracted thumbnail image?
+ if ($this->tnImage && $this->thumbLen > 0) {
+ // check for GD/JPEG libraries
+ if (function_exists('imagecreatefromjpeg') &&
+ function_exists('imagecopyresampled')) {
+ // flip thumbnail via temporary file
+ $tmp = tempnam(sys_get_temp_dir(), 'gbxflip');
+ if (@file_put_contents($tmp, $this->thumbnail) !== false) {
+ if ($tn = @imagecreatefromjpeg($tmp)) {
+ $tn = $this->imageFlip($tn, self::IMAGE_FLIP_HORIZONTAL);
+ if (@imagejpeg($tn, $tmp)) {
+ if (($tn = @file_get_contents($tmp)) !== false) {
+ $this->thumbnail = $tn;
+ }
+ }
+ }
+ unlink($tmp);
+ }
+ }
+ } else {
+ $this->thumbnail = '';
+ }
+ }
+ } // getThumbnlChunk
+
+} // class GBXChallMapFetcher
+
+
+/**
+ * @class GBXChallengeFetcher
+ * @brief Wrapper class for backwards compatibility with the old GBXChallengeFetcher
+ * @deprecated Do not use for new development, use GBXChallMapFetcher instead
+ */
+class GBXChallengeFetcher extends GBXChallMapFetcher
+{
+ public $authortm, $goldtm, $silvertm, $bronzetm, $ascore, $azone, $multi, $editor,
+ $pub, $nblaps, $parsedxml, $xmlver, $exever, $exebld, $songfile, $songurl,
+ $modname, $modfile, $modurl;
+
+ /**
+ * Fetches a hell of a lot of data about a GBX challenge
+ *
+ * @param String $filename
+ * The challenge filename (must include full path)
+ * @param Boolean $parsexml
+ * If true, the script also parses the XML block
+ * @param Boolean $tnimage
+ * If true, the script also extracts the thumbnail image; if GD/JPEG
+ * libraries are present, image will be flipped upright, otherwise
+ * it will be in the original upside-down format
+ * Warning: this is binary data in JPEG format, 256x256 pixels for
+ * TMU/TMF or 512x512 pixels for MP
+ * @return GBXChallengeFetcher
+ * If $uid is empty, GBX data couldn't be extracted
+ */
+ public function __construct($filename, $parsexml = false, $tnimage = false)
+ {
+ parent::__construct($parsexml, $tnimage, false);
+
+ try
+ {
+ $this->processFile($filename);
+
+ $this->authortm = $this->authorTime;
+ $this->goldtm = $this->goldTime;
+ $this->silvertm = $this->silverTime;
+ $this->bronzetm = $this->bronzeTime;
+ $this->ascore = $this->authorScore;
+ $this->azone = $this->authorZone;
+ $this->multi = $this->multiLap;
+ $this->editor = $this->simpleEdit;
+ $this->pub = $this->authorBg;
+ $this->nblaps = $this->nbLaps;
+ $this->parsedxml = $this->xmlParsed;
+ $this->xmlver = $this->xmlVer;
+ $this->exever = $this->exeVer;
+ $this->exebld = $this->exeBld;
+ $this->songfile = $this->songFile;
+ $this->songurl = $this->songUrl;
+ $this->modname = $this->modName;
+ $this->modfile = $this->modFile;
+ $this->modurl = $this->modUrl;
+ }
+ catch (Exception $e)
+ {
+ $this->uid = '';
+ }
+ }
+
+} // class GBXChallengeFetcher
+
+
+/**
+ * @class GBXReplayFetcher
+ * @brief The class that fetches all GBX replay info
+ * @note The interface for GBXReplayFetcher has changed compared to the old class,
+ * but there is no wrapper because no third-party XASECO[2] plugins used that
+ */
+class GBXReplayFetcher extends GBXBaseFetcher
+{
+ public $uid, $envir, $author, $replay, $nickname, $login, $titleUid;
+ public $xmlVer, $exeVer, $exeBld, $respawns, $stuntScore, $validable,
+ $cpsCur, $cpsLap;
+
+ /**
+ * Instantiate GBX replay fetcher
+ *
+ * @param Boolean $parsexml
+ * If true, the fetcher also parses the XML block
+ * @param Boolean $debug
+ * If true, the fetcher prints debug logging to stderr
+ * @return GBXReplayFetcher
+ * If GBX data couldn't be extracted, an Exception is thrown with
+ * the error message & code
+ */
+ public function __construct($parsexml = false, $debug = false)
+ {
+ parent::__construct();
+
+ $this->uid = '';
+ $this->envir = '';
+ $this->author = '';
+ $this->replay = 0;
+ $this->nickname = '';
+ $this->login = '';
+ $this->titleUid = '';
+
+ $this->xmlVer = '';
+ $this->exeVer = '';
+ $this->exeBld = '';
+ $this->respawns = 0;
+ $this->stuntScore = 0;
+ $this->validable = false;
+ $this->cpsCur = 0;
+ $this->cpsLap = 0;
+
+ $this->parseXml = (bool)$parsexml;
+ if ((bool)$debug)
+ $this->enableDebug();
+
+ $this->setError('GBX replay error: ');
+ } // __construct
+
+ /**
+ * Process GBX replay file
+ *
+ * @param String $filename
+ * The replay filename
+ */
+ public function processFile($filename)
+ {
+ $this->loadGBXdata((string)$filename);
+
+ $this->processGBX();
+ } // processFile
+
+ /**
+ * Process GBX replay data
+ *
+ * @param String $gbxdata
+ * The replay data
+ */
+ public function processData($gbxdata)
+ {
+ $this->storeGBXdata((string)$gbxdata);
+
+ $this->processGBX();
+ } // processData
+
+ // process GBX data
+ private function processGBX()
+ {
+ // supported replay class IDs
+ $replayclasses = array(
+ self::GBX_AUTOSAVE_TMF,
+ self::GBX_AUTOSAVE_TM,
+ self::GBX_REPLAY_TM,
+ );
+
+ $headerSize = $this->checkHeader($replayclasses);
+ $headerStart = $headerEnd = $this->getGBXptr();
+
+ // desired replay chunk IDs
+ $chunks = array(
+ 0x000 => 'String', // TM, MP
+ 0x001 => 'XML', // TM, MP
+ 0x002 => 'Author', // MP
+ );
+
+ $chunksList = $this->getChunksList($headerSize, $chunks);
+
+ $this->getStringChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getXMLChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ $this->getAuthorChunk($chunksList);
+ $headerEnd = max($headerEnd, $this->getGBXptr());
+
+ if ($headerSize != $headerEnd - $headerStart)
+ $this->errorOut(sprintf('Header size mismatch: %d <> %d',
+ $headerSize, $headerEnd - $headerStart), 20);
+
+ if ($this->parseXml) {
+ if (isset($this->xmlParsed['HEADER']['VERSION']))
+ $this->xmlVer = $this->xmlParsed['HEADER']['VERSION'];
+ if (isset($this->xmlParsed['HEADER']['EXEVER']))
+ $this->exeVer = $this->xmlParsed['HEADER']['EXEVER'];
+ if (isset($this->xmlParsed['HEADER']['EXEBUILD']))
+ $this->exeBld = $this->xmlParsed['HEADER']['EXEBUILD'];
+ if (isset($this->xmlParsed['TIMES']['RESPAWNS']))
+ $this->respawns = (int)$this->xmlParsed['TIMES']['RESPAWNS'];
+ if (isset($this->xmlParsed['TIMES']['STUNTSCORE']))
+ $this->stuntScore = (int)$this->xmlParsed['TIMES']['STUNTSCORE'];
+ if (isset($this->xmlParsed['TIMES']['VALIDABLE']))
+ $this->validable = (bool)$this->xmlParsed['TIMES']['VALIDABLE'];
+ if (isset($this->xmlParsed['CHECKPOINTS']['CUR']))
+ $this->cpsCur = (int)$this->xmlParsed['CHECKPOINTS']['CUR'];
+ if (isset($this->xmlParsed['CHECKPOINTS']['ONELAP']))
+ $this->cpsLap = (int)$this->xmlParsed['CHECKPOINTS']['ONELAP'];
+ }
+
+ $this->clearGBXdata();
+ } // processGBX
+
+ /**
+ * Get String chunk from GBX header block
+ * @param array $chunksList
+ * List of chunk offsets & sizes
+ */
+ protected function getStringChunk(array $chunksList)
+ {
+ if (!isset($chunksList['String'])) return;
+
+ $this->initChunk($chunksList['String']['off']);
+ $version = $this->readInt32();
+ $this->debugLog('GBX String chunk version: ' . $version);
+
+ if ($version >= 2) {
+ $this->uid = $this->readLookbackString();
+
+ $this->envir = $this->readLookbackString();
+ $this->author = $this->readLookbackString();
+
+ $this->replay = $this->readInt32();
+
+ $this->nickname = $this->stripBOM($this->readString());
+
+ if ($version >= 6) {
+ $this->login = $this->readString();
+
+ if ($version >= 8) {
+ $this->moveGBXptr(1); // skip unknown byte
+
+ $this->titleUid = $this->readLookbackString();
+ }
+ }
+ }
+ } // getStringChunk
+
+} // class GBXReplayFetcher
+
+
+/**
+ * @class GBXPackFetcher
+ * @brief The class that fetches all GBX pack info
+ */
+class GBXPackFetcher extends GBXBaseFetcher
+{
+ public $headerVersn, $flags, $infoMlUrl, $creatDate, $comment, $titleId,
+ $usageSubdir, $buildInfo, $authorUrl, $exeVer, $exeBld, $xmlDate;
+
+ /**
+ * Read Windows FileTime and convert to Unix timestamp
+ * Filetime = 64-bit value with the number of 100-nsec intervals since Jan 1, 1601 (UTC)
+ * Based on http://www.mysqlperformanceblog.com/2007/03/27/integers-in-php-running-with-scissors-and-portability/
+ * @return Unix timestamp, or -1 on error
+ */
+ private function readFiletime()
+ {
+ // Unix epoch (1970-01-01) - Windows epoch (1601-01-01) in 100ns units
+ $EPOCHDIFF = '116444735995904000';
+ $UINT32MAX = '4294967296';
+ $USEC2SEC = 1000000;
+
+ $lo = $this->readInt32();
+ $hi = $this->readInt32();
+
+ // check for 64-bit platform
+ if (PHP_INT_SIZE >= 8) {
+ // use native math
+ if ($lo < 0) $lo += (1 << 32);
+ $date = ($hi << 32) + $lo;
+ $this->debugLog(sprintf('PAK CreationDate source: %016x', $date));
+ if ($date == 0) return -1;
+
+ // convert to Unix timestamp in usec
+ $stamp = ($date - (int)$EPOCHDIFF) / 10;
+ $this->debugLog(sprintf('PAK CreationDate 64-bit: %u.%06u',
+ $stamp / $USEC2SEC, $stamp % $USEC2SEC));
+ return (int)($stamp / $USEC2SEC);
+
+ // check for 32-bit platform
+ } elseif (PHP_INT_SIZE >= 4) {
+ $this->debugLog(sprintf('PAK CreationDate source: %08x%08x', $hi, $lo));
+ if ($lo == 0 && $hi == 0) return -1;
+
+ // workaround signed/unsigned braindamage on x32
+ $lo = sprintf('%u', $lo);
+ $hi = sprintf('%u', $hi);
+
+ // try and use GMP
+ if (function_exists('gmp_mul')) {
+ $date = gmp_add(gmp_mul($hi, $UINT32MAX), $lo);
+ // convert to Unix timestamp in usec
+ $stamp = gmp_div(gmp_sub($date, $EPOCHDIFF), 10);
+ $stamp = gmp_div_qr($stamp, $USEC2SEC);
+ $this->debugLog(sprintf('PAK CreationDate GNU MP: %u.%06u',
+ gmp_strval($stamp[0]), gmp_strval($stamp[1])));
+ return (int)gmp_strval($stamp[0]);
+ }
+
+ // try and use BC Math
+ if (function_exists('bcmul')) {
+ $date = bcadd(bcmul($hi, $UINT32MAX), $lo);
+ // convert to Unix timestamp in usec
+ $stamp = bcdiv(bcsub($date, $EPOCHDIFF), 10, 0);
+ $this->debugLog(sprintf('PAK CreationDate BCMath: %u.%06u',
+ bcdiv($stamp, $USEC2SEC), bcmod($stamp, $USEC2SEC)));
+ return (int)bcdiv($stamp, $USEC2SEC);
+ }
+
+ // compute everything manually
+ $a = substr($hi, 0, -5);
+ $b = substr($hi, -5);
+ // hope that float precision is enough
+ $ac = $a * 42949;
+ $bd = $b * 67296;
+ $adbc = $a * 67296 + $b * 42949;
+ $r4 = substr($bd, -5) + substr($lo, -5);
+ $r3 = substr($bd, 0, -5) + substr($adbc, -5) + substr($lo, 0, -5);
+ $r2 = substr($adbc, 0, -5) + substr($ac, -5);
+ $r1 = substr($ac, 0, -5);
+ while ($r4 >= 100000) { $r4 -= 100000; $r3++; }
+ while ($r3 >= 100000) { $r3 -= 100000; $r2++; }
+ while ($r2 >= 100000) { $r2 -= 100000; $r1++; }
+ $date = ltrim(sprintf('%d%05d%05d%05d', $r1, $r2, $r3, $r4), '0');
+
+ // convert to Unix timestamp in usec
+ $r3 = substr($date, -6) - substr($EPOCHDIFF, -6);
+ $r2 = substr($date, -12, 6) - substr($EPOCHDIFF, -12, 6);
+ $r1 = substr($date, -18, 6) - substr($EPOCHDIFF, -18, 6);
+ if ($r3 < 0) { $r3 += 1000000; $r2--; }
+ if ($r2 < 0) { $r2 += 1000000; $r1--; }
+ $stamp = substr(sprintf('%d%06d%06d', $r1, $r2, $r3), 0, -1);
+ $this->debugLog(sprintf('PAK CreationDate manual: %s.%s',
+ substr($stamp, 0, -6), substr($stamp, -6)));
+ return (int)substr($stamp, 0, -6);
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Instantiate GBX pack fetcher
+ *
+ * @param Boolean $parsexml
+ * If true, the fetcher also parses the XML block
+ * @param Boolean $debug
+ * If true, the fetcher prints debug logging to stderr
+ * @return GBXPackFetcher
+ * If GBX data couldn't be extracted, an Exception is thrown with
+ * the error message & code
+ */
+ public function __construct($parsexml = false, $debug = false)
+ {
+ parent::__construct();
+
+ $this->headerVersn = 0;
+ $this->flags = 0;
+ $this->infoMlUrl = '';
+ $this->creatDate = -1;
+ $this->comment = '';
+ $this->titleId = '';
+ $this->usageSubdir = '';
+ $this->buildInfo = '';
+ $this->authorUrl = '';
+ $this->exeVer = '';
+ $this->exeBld = '';
+ $this->xmlDate = '';
+
+ $this->parseXml = (bool)$parsexml;
+ if ((bool)$debug)
+ $this->enableDebug();
+
+ $this->setError('GBX pack error: ');
+ } // __construct
+
+ /**
+ * Process GBX pack file
+ *
+ * @param String $filename
+ * The pack filename
+ */
+ public function processFile($filename)
+ {
+ $this->loadGBXdata((string)$filename);
+
+ $this->processGBX();
+ } // processFile
+
+ /**
+ * Process GBX pack data
+ *
+ * @param String $gbxdata
+ * The pack data
+ */
+ public function processData($gbxdata)
+ {
+ $this->storeGBXdata((string)$gbxdata);
+
+ $this->processGBX();
+ } // processData
+
+ // process GBX data
+ private function processGBX()
+ {
+ // check magic header
+ $data = $this->readData(8);
+ if ($data != 'NadeoPak')
+ $this->errorOut('No magic NadeoPak header', 5);
+
+ $this->headerVersn = $this->readInt32();
+ if ($this->headerVersn < 6)
+ $this->errorOut(sprintf('Pack version %d not supported', $this->headerVersn), 24);
+
+ $this->moveGBXptr(32); // skip ContentsChecksum
+
+ $this->flags = $this->readInt32();
+
+ if ($this->headerVersn >= 7) {
+ $this->getAuthorFields();
+
+ if ($this->headerVersn < 9) {
+ $this->comment = $this->stripBOM($this->readString());
+
+ $this->moveGBXptr(16); // skip unused uint128
+
+ if ($this->headerVersn >= 8) {
+ $this->buildInfo = $this->readString();
+
+ $this->authorUrl = $this->readString();
+ }
+
+ } else { // >= 9
+ $this->infoMlUrl = $this->readString();
+
+ $this->creatDate = $this->readFiletime();
+
+ $this->comment = $this->stripBOM($this->readString());
+
+ if ($this->headerVersn >= 12) {
+ $this->xml = $this->readString();
+ $this->titleId = $this->readString();
+
+ if ($this->parseXml && $this->xml != '') {
+ $this->parseXMLstring();
+
+ if (isset($this->xmlParsed['HEADER']['EXEVER']))
+ $this->exeVer = $this->xmlParsed['HEADER']['EXEVER'];
+ if (isset($this->xmlParsed['HEADER']['EXEBUILD']))
+ $this->exeBld = $this->xmlParsed['HEADER']['EXEBUILD'];
+ if (isset($this->xmlParsed['IDENT']['CREATIONDATE']))
+ $this->xmlDate = $this->xmlParsed['IDENT']['CREATIONDATE'];
+ }
+ }
+
+ $this->usageSubdir = $this->readString();
+
+ $this->buildInfo = $this->readString();
+
+ $this->moveGBXptr(16); // skip unused uint128
+ // if ($this->headerVersn >= 10) // skip encrypted IncludedPacks
+ }
+ }
+
+ $this->clearGBXdata();
+ } // processGBX
+
+} // class GBXPackFetcher
+?>
diff --git a/application/core/maniaControlClass.php b/application/core/maniaControlClass.php
index 661ed5ac..9e427a6e 100644
--- a/application/core/maniaControlClass.php
+++ b/application/core/maniaControlClass.php
@@ -18,6 +18,9 @@ require_once __DIR__ . '/server.php';
require_once __DIR__ . '/settingManager.php';
require_once __DIR__ . '/settingConfigurator.php';
require_once __DIR__ . '/mapHandler.php';
+
+require_once __DIR__ . '/GbxDataFetcher/gbxdatafetcher.inc.php';
+
list($endiantest) = array_values(unpack('L1L', pack('V', 1)));
if ($endiantest == 1) {
require_once __DIR__ . '/PhpRemote/GbxRemote.inc.php';
diff --git a/application/core/map.php b/application/core/map.php
index b7769dbc..377f9965 100644
--- a/application/core/map.php
+++ b/application/core/map.php
@@ -1,10 +1,4 @@
name = 'undefined';
}
+ /*
+ * aseco trash:
+ * // obtain map's GBX data, MX info & records
+ $map_item->mx = findMXdata($map_item->uid, true);
+
+ // titleuid (is not in the GetMapInfos method..)
+ $map_item->titleuid = $map_item->gbx->titleUid;
+
+ // author Informations from the GBXBaseFetcher
+ $map_item->authorNick = $map_item->gbx->authorNick;
+ $map_item->authorZone = $map_item->gbx->authorZone;
+ $map_item->authorEInfo = $map_item->gbx->authorEInfo;
+ *
+ */
+ $mapFetcher = new \GBXChallMapFetcher(true);
+ try{
+ $mapFetcher->processFile($this->server->mapdir . $this->filename);
+ } catch (Exception $e)
+ {
+ trigger_error($e->getMessage(), E_USER_WARNING);
+ }
+ $this->authorNick = $mapFetcher->authorNick;
+ $this->authorEInfo = $mapFetcher->authorEInfo;
+ $this->authorZone = $mapFetcher->authorZone;
}
}
\ No newline at end of file