private function parseDocumentHeader($string) { $numByte = 44; // skip header and unknown stuff $numDeps = MPQFile::readByte($string, $numByte); // uncertain that this is the number of dependencies, might also be uint32 if it is $numByte += 3; while ($numDeps > 0) { while (MPQFile::readByte($string, $numByte) !== 0) { } $numDeps--; } $numAttribs = MPQFile::readUInt32($string, $numByte); $attribs = array(); while ($numAttribs > 0) { $keyLen = MPQFile::readUInt16($string, $numByte); $key = MPQFile::readBytes($string, $numByte, $keyLen); $numByte += 4; // always seems to be followed by ascii SUne $valueLen = MPQFile::readUInt16($string, $numByte); $value = MPQFile::readBytes($string, $numByte, $valueLen); $attribs[$key] = $value; $numAttribs--; } $this->author = $attribs["DocInfo/Author"]; $this->mapName = $attribs["DocInfo/Name"]; $this->description = $attribs["DocInfo/DescLong"]; $this->shortDescription = $attribs["DocInfo/DescShort"]; }
public static function parseSerializedData($string, &$numByte) { $dataType = MPQFile::readByte($string, $numByte); switch ($dataType) { case 0x2: // binary data $dataLen = MPQFile::parseVLFNumber($string, $numByte); return MPQFile::readBytes($string, $numByte, $dataLen); break; case 0x4: // simple array $array = array(); $numByte += 2; // skip 01 00 $numElements = MPQFile::parseVLFNumber($string, $numByte); while ($numElements > 0) { $array[] = MPQFile::parseSerializedData($string, $numByte); $numElements--; } return $array; break; case 0x5: // array with keys $array = array(); $numElements = MPQFile::parseVLFNumber($string, $numByte); while ($numElements > 0) { $index = MPQFile::parseVLFNumber($string, $numByte); $array[$index] = MPQFile::parseSerializedData($string, $numByte); $numElements--; } return $array; break; case 0x6: // number of one byte return MPQFile::readByte($string, $numByte); break; case 0x7: // number of four bytes return MPQFile::readUInt32($string, $numByte); break; case 0x9: // number in VLF return MPQFile::parseVLFNumber($string, $numByte); break; default: // if ($this->debug) $this->debug(sprintf("Unknown data type in function parseDetailsValue (%d)",$dataType)); return false; } }
private function parseAttributesFile($string) { if ($this->debug) { $this->debug("Parsing replay.attributes.events file"); } $numByte = 4; // skip the 4-byte header if ($this->build >= 17326) { $numByte += 1; } // 1 additional byte for these builds $numAttribs = MPQFile::readUInt32($string, $numByte); $attribArray = array(); $difficulties = array("VyEy" => 0, "Easy" => 1, "Medi" => 2, "Hard" => 3, "VyHd" => 4, "Insa" => 5); $gameSpeeds = array("Slor" => 0, "Slow" => 1, "Norm" => 2, "Fast" => 3, "Fasr" => 4); for ($i = 0; $i < $numAttribs; $i++) { $attribHeader = MPQFile::readUInt32($string, $numByte); $attributeId = MPQFile::readUInt32($string, $numByte); $playerId = MPQFile::readByte($string, $numByte); $attribVal = ""; // values are stored in reverse in the file, eg Terr becomes rreT. The following loop flips the value and removes excess null bytes for ($a = 0; $a < 4; $a++) { $b = ord(substr($string, $numByte + 3 - $a)); if ($b != 0) { $attribVal .= chr($b); } } $numByte += 4; $attribArray[$attributeId][$playerId] = $attribVal; if ($this->debug) { $this->debug(sprintf("Got attrib \"%04X\" for player %d, attribVal = \"%s\"", $attributeId, $playerId, $attribVal)); } } if ($numAttribs == 0) { return; } // map the player ids to actual player ids // assumes that the slots are populated from the lowest player id to highest player id if (isset($attribArray[0x1f4])) { $tmpPlayerArray = array(); for ($i = 1, $playerId = 1; isset($attribArray[0x1f4][$i]); $i++) { if ($attribArray[0x1f4][$i] == "Open") { continue; } $tmpPlayerArray[$i] = $this->players[$playerId]; $tmpPlayerArray[$i]['id'] = $i; $playerId++; } unset($this->players); $this->players = $tmpPlayerArray; $numSlots = $i; } else { $numSlots = 0; } // see which attribute id gives the correct team values switch ($attribArray[0x7d1][0x10]) { case "1v1": $teamAttrib = 0x7d2; break; case "2v2": $teamAttrib = 0x7d3; break; case "3v3": $teamAttrib = 0x7d4; break; case "4v4": $teamAttrib = 0x7d5; break; case "FFA": $teamAttrib = 0x7d6; break; default: if ($this->debug) { $this->debug(sprintf("Unknown game mode in replay.attributes.events: %s", $attribArray[0x10][0x7d1])); } } // custom games have different values (not tested with all values, algorithm may be wrong) switch ($attribArray[0x7d0][0x10]) { case 'Cust': if ($attribArray[0x3e9][0x10] == 'no') { $teamAttrib += 0x10; } break; // 0x7D3 -> 0x7E3 etc. // 0x7D3 -> 0x7E3 etc. default: } // populate the data structures with relevant values $teamArray = array(); for ($i = 1; $i < $numSlots; $i++) { if (!isset($this->players[$i])) { continue; } //$actualPlayerId = $playerIdArray[$i]; // handicap $this->players[$i]["handicap"] = $attribArray[0xbbb][$i]; // difficulty $this->players[$i]["difficulty"] = $difficulties[$attribArray[0xbbc][$i]]; // starting race $this->players[$i]["srace"] = $attribArray[0xbb9][$i]; // set player type $this->players[$i]["isComp"] = $attribArray[0x1f4][$i] == 'Comp' ? true : false; // set player colors $this->players[$i]["colorIndex"] = intval(substr($attribArray[0xbba][$i], 2)); $this->players[$i]["sColor"] = self::$colorIndices[intval(substr($attribArray[0xbba][$i], 2))]; // set team $team = intval(substr($attribArray[$teamAttrib][$i], 1)); if (isset($teamArray[$team])) { $teamArray[$team]++; } else { $teamArray[$team] = 1; } $this->players[$i]["team"] = $team; if (isset($this->players[$i]["won"]) && $this->players[$i]["won"] == 1) { $this->winnerTeam = $team; } } foreach ($teamArray as $team => $count) { if (isset($teamSizeString)) { $teamSizeString .= "v{$count}"; } else { $teamSizeString = "{$count}"; } } // if no team data, set a default value if (!isset($teamSizeString)) { $teamSizeString = "0v0"; } // if only one team is found for some weird reason, add v0 for completeness eg 4v0 or so if (strpos($teamSizeString, "v") === false) { $teamSizeString .= "v0"; } $this->realTeamSize = $teamSizeString; // set team size $this->teamSize = $attribArray[0x7d1][0x10]; // game speed $this->gameSpeed = $gameSpeeds[$attribArray[0xbb8][0x10]]; // game type, Amm for Automatchmaking, Priv for Private $this->gameType = $attribArray[0xbc1][0x10]; // the next field is a remnant, doesn't provide anything $this->gameType doesn't provide $this->gamePublic = $attribArray[0xbc1][0x10] == "Priv" ? false : true; }