Jump to content

Something is wrong with this Upload Form...


Mesden

Recommended Posts

http://sc2x.net/test/upload_process.phpI've attempted to upload a file but whenever I do, it doesn't so what it's supposed to, instead it just shows me the Coding...These 3 pages are supposed to work together to upload the file:http://www.sc2x.net/test/mpqfile.phphttp://www.sc2x.net/test/sc2replay.phphttp://www.sc2x.net/test/upload_file.phpI've also uploaded a replay for anyone who wants to test it in actionhttp://www.sc2x.net/test/Decena(118).SC2Replay
Link to comment
Share on other sites

http://sc2x.net/test/upload_process.phpI've attempted to upload a file but whenever I do, it doesn't so what it's supposed to, instead it just shows me the Coding...These 3 pages are supposed to work together to upload the file:http://www.sc2x.net/test/mpqfile.phphttp://www.sc2x.net/test/sc2replay.phphttp://www.sc2x.net/test/upload_file.phpI've also uploaded a replay for anyone who wants to test it in actionhttp://www.sc2x.net/test/Decena(118).SC2Replay
All it does it take me straight to here when I hit "Upload":http://code.google.com/p/phpsc2replay/sour...php/mpqfile.php
Link to comment
Share on other sites

1. In what order do things occur?2. Why on earth do you have a copy of the Google code page for the MPQ reader? You're supposed to get the code for the reader itself, not for the page it's on.3. "I've also uploaded a replay"... wait.. isn't the problem the fact that the thing is NOT working? Or is that upload just for the sake of showing the expected result?

Link to comment
Share on other sites

You're going to need to post the code if you want specific answers, but it does really look like you just copied and pasted the source code of the Google code page. It even shows your email address as being signed in to Google.

Link to comment
Share on other sites

1. In what order do things occur?2. Why on earth do you have a copy of the Google code page for the MPQ reader? You're supposed to get the code for the reader itself, not for the page it's on.3. "I've also uploaded a replay"... wait.. isn't the problem the fact that the thing is NOT working? Or is that upload just for the sake of showing the expected result?
User visits the Uplaod Page:http://sc2x.net/test/upload_file.phpUser clicks on Browse, and then clicks UploadThe Upload then parses the Information, and then displays it on the Website. Like this:http://www.sc2x.net/test/show_replay/show.php?id=5034But instead, it's just sending me to a Google Code page... Which is really odd because I'm not linking to the Google Code page at all... These are the three Files I'm using and I've weened it down between the MPQFile.php and the SC2Replay.php.http://www.sc2x.net/test/MPQFile.phphttp://www.sc2x.net/test/SC2Replay.phphttp://www.sc2x.net/test/upload_File.php
Link to comment
Share on other sites

I can't look at your PHP code just from URLs, you have to actually paste the code here. It's not redirecting to a Google code page, the URL still says sc2x.net, not code.google.com. It appears that you have copied the source code for the Google code page instead of the code for the replay parser which is displayed on that page.

Link to comment
Share on other sites

I can't look at your PHP code just from URLs, you have to actually paste the code here. It's not redirecting to a Google code page, the URL still says sc2x.net, not code.google.com. It appears that you have copied the source code for the Google code page instead of the code for the replay parser which is displayed on that page.
Here's the Coding in the MPQ File:http://code.google.com/p/phpsc2replay/sour...php/mpqfile.phpHere's the Coding in the SC2Replay File:http://code.google.com/p/phpsc2replay/sour...p/sc2replay.phpAnd here's the Coding in the Upload_File.php Form
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>SC2X.Net: Your Premiere Source for Starcraft II!</title><meta name="keywords" content="Blizzard, Activision, Blizzard-Activision, SC, Starcraft, SC2, Starcraft 2, Starcraft II" /><meta name="description" content="SC2X.Net is a Fan Site for the much-anticipated Starcraft II. Our objective is to be your premiere source for Starcraft 2 Tournaments." /><link href="templatemo_style.css" rel="stylesheet" type="text/css" /><link href="css/jquery.ennui.contentslider.css" rel="stylesheet" type="text/css" media="screen,projection" /><script language="javascript" type="text/javascript">function clearText(field){	if (field.defaultValue == field.value) field.value = '';	else if (field.value == '') field.value = field.defaultValue;}</script></head><body><?phpinclude 'topbar.php';?><!-- end of templatemo_site_title_bar_wrapper --><?phpinclude 'middlebar.php';?><!-- end of templatemo_banner_wrapper_outter --><div id="templatemo_content">  <div class="section_w940">	  <div class="box margin_r_20 box_border">			<h2>Upload Replay(s)</h2>	<div class="box_image_wrapper">			  <img src="images/folder.png" alt="product 1" />	  </div>	Submit your Starcraft II Replay to our Replay Archive.	<p><br />	</p>				  <div class="cleaner_h10"></div>			<div class="button_01"><a href="/upload_file.php">More</a></div>						</div>				<div class="box margin_r_20 box_border">			<h2>Browse Replay(s)</h2>			<div class="box_image_wrapper">			  <img src="images/paint.png" alt="product 1" />			</div>			<p>Browse through our Starcraft II Replay Archive.			  <br />			  <br />		  </p>						<div class="cleaner_h10"></div>						<div class="button_01"><a href="http://sc2x.net/phpBB3/viewforum.php?f=15">More</a></div>						</div>				<div class="box">			<h2>Contact Us</h2>			<div class="box_image_wrapper">			  <img src="images/safe.png" alt="product 1" />			</div>			<p>Contact SC2X.Net for any Comments, Questions or Suggestions you might have.</p>						<div class="cleaner_h10"></div>						<div class="button_01"><a href="#">More</a></div>						</div>			<div class="cleaner"></div>	</div>		<div class="cleaner_h60"></div>		<div class="section_w940">	  <div class="section_w450 margin_r_40">				<form enctype="multipart/form-data" action="upload_process.php" method="post"  >	<h2>	  Upload a Replay		   	</h2>		Locate the Replay on your Hard Drive you want to upload. This can typically be located <br />	at<em> C:\Users\(Your Profile)\Documents\StarCraft II Beta\Replays</em>. <br />	<br />	<h2>  		<textarea name="textarea" id="textarea" cols="45" rows="7">Type in a Description of the Replay		</textarea>		<br />		<br />		<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $MAX_FILE_SIZE;?>" />		<input name="userfile" type="file" size="35" /><br /><br />		<input type="hidden" name ="debug" value="0" />		<input type="image" src="images/submit-button.jpg" />		<a href="/"><img src="images/cancel-button.jpg" width="84" height="25" longdesc="/" alt="Cancel"/></a> 	</h2>		</form>	<?php	if (isset($_GET['error']) && $_GET['error'] != '') {		echo urldecode($_GET['error']);	}	?>	  </div>		<div class="cleaner"></div></div>			<div class="cleaner"></div></div> <!-- end of templatemo_content --> <div id="templatemo_footer_wrapper"><br /><?phpinclude 'bottombar.php';?><!-- end of footer --></div> <!-- end of footer wrapper --></body></html>

And then the Upload Process.php as specified in the Upload Form:

<?phprequire 'upload_functions.php';require 'upload_file_settings.php';$MAX_FILE_SIZE = 1000000;if (isset($_FILES['userfile'])) {		$error = $_FILES['userfile']['error'];		$type = $_FILES['userfile']['type'];		$name = $_FILES['userfile']['name'];		$tmpname = $_FILES['userfile']['tmp_name'];		$size = $_FILES['userfile']['size'];		$err = false;		if ($size >= $MAX_FILE_SIZE) {				$errmsg = "Error: File is too large. Replay File must be below 1MB.<br />";				$err = true;		}		if ($error == UPLOAD_ERR_PARTIAL) {				$errmsg = "Error: The upload was not completed successfully. Please try again.<br />";				$err = true;		}		if ($error == UPLOAD_ERR_NO_FILE) {				$errmsg = "Error: Please select a File to upload.<br />";				$err = true;		}		if (!is_uploaded_file($tmpname)) {				$errmsg = "Error: Uploaded filename doesn't point to an uploaded file.<br />";				$err = true;		}		if (file_exists("./replay_files/" . $_FILES["userfile"]["name"]))		{			$newfilename = renameFile("./replay_files/", $_FILES["userfile"]["name"]);			if (!$newfilename) {			$errmsg = "Error: File already exists, unable to rename. Rename and try again.";			$err = true;			}			else {				$_FILES["userfile"]["name"] = $newfilename;				$name = $newfilename;			}		}		if ($err !== true) {				if (class_exists("MPQFile") || (include 'mpqfile.php')) {						$start = microtime_float();						if ($_POST['debug'] == 1) {								echo sprintf("<b>Debugging is on.</b><br />\n");						}						$a = new MPQFile($tmpname,true,(($_POST['debug'] == 1)?2:0));						$init = $a->getState();						if ($init == MPQ_ERR_NOTMPQFILE)								echo "Could not parse the Replay. Check to make sure it's a valid .SC2Replay File.<br />\n";						else if ($a->getVersion() < 9)								echo "Error: Invalid Version.<br />\n";						else {								// file is valid, save it								move_uploaded_file($_FILES["userfile"]["tmp_name"],"./replay_files/" . $_FILES["userfile"]["name"]);								// update database								//echo 'file uploaded';								$datetime = date('Y-m-d H:i:s');								$replay_query = "INSERT INTO `replays` (`id`, `description`, `file_name`, `datetime`) VALUES									(NULL, '".$_POST['textarea']."','".$_FILES["userfile"]["name"]."','".$datetime."')";								$insert = mysql_query($replay_query, $con) or die('could not insert replay file.');								$insert_id = mysql_insert_id();								if ($insert_id) {									$major_version = $a->getVersion();									$build = $a->getBuild();									$b = $a->parseReplay();									$map = $b->getMapName();									$game_length = $b->getFormattedGameLength();									$team_size = $b->getTeamSize();									$game_speed = $b->getGameSpeed();									//$game_speed = $b->getGameSpeedText();									// Read and save game data									$game_query = "INSERT INTO `games` (`id` , `replay_id`,											`major_version` ,`build` ,`map` ,`game_length` ,`team_size` ,											`game_speed`)											VALUES (NULL , '$insert_id', '$major_version', '$build', '$map', '$game_length', '$team_size', '$game_speed')";									$insert_game = mysql_query($game_query, $con) or die('Could not insert game record.');									//Read and save player data									$tmp = $b->getPlayers();									foreach($tmp as $value) {										$team = ($value['party'] > 0)?"Team ".$value['party']:"-";										$apm_avg = ($value['party'] > 0)?(round($value['apmtotal'] / ($b->getGameLength() / 60))):0;										$winner = (isset($value['won']))?$value['won']:(($value['party'] > 0)?"Unknown":"-");										$player_query = "INSERT INTO `players` (`id` ,`replay_id` ,													`name` ,`long_name` ,`race` ,`color` ,`team` ,`average_apm` ,													 `winner`) VALUES (NULL , '$insert_id', '".$value['sName']."', '".$value['lName']."', '".$value['race']."', '".$value['color']."', '".$team."', '$apm_avg', '$winner')";										$insert_player = mysql_query($player_query, $con) or die('could not insert player record');										if ($value['party'] > 0) {											$apmFileName = './apm_in/'.$value['id']."_".md5($name).".png";											$arAPMfilenames[] = $apmFileName;											createAPMImage($value['apm'],$b->getGameLength(),$apmFileName, $value['sName']);										}									}									mergeAPMFiles($arAPMfilenames, $name);								}								$messages = $b->getMessages();								if (count($messages) > 0) {										$data =  "<b>Messages:</b><br /><table border=\"1\"><tr><th>Time</th><th>Player</th><th>Target</th><th>Message</th></tr>\n";										foreach ($messages as $val)												$data .= sprintf("<tr><td>%d sec</td><td>%s</td><td>%s</td><td>%s</td></tr>\n",$val['time'],																		  $val['name'], ($val['target'] == 2)?"Alliance":"All",$val['message']);										$data .= "</table><br />\n";								}								$t = $b->getEvents();								if (isset($sc2_abilityCodes) || (include 'abilitycodes.php')) {										$data .= "<b>Production Chart:</b> <br /><table border=\"1\"><tr><th>Timecode</th>\n";										$pNum = count($tmp);										foreach ($tmp as $value) {										  if ($value['party'] > 0)												$data .= sprintf("<th>%s (%s)</th>",$value['sName'],$value['race']);										}										$data .= "</tr>\n";										foreach ($t as $value) {												$data .= sprintf("<tr><td>%d sec</td>",$value['t'] / 16);												foreach ($tmp as $value2) {														if ($value2['party'] == 0) continue;														if ($value['p'] == $value2['id'])																$data .= sprintf("<td>%s</td>",$b->getAbilityString($value['a']));														else																$data .= "<td></td>";												}												$data .= "</tr>\n";										}										$data .= "</table>";										$file=fopen("logs/".$name.".xls","w");										fputs($file,$data);										fclose($file);								}						}				}			 header('location: show_replay/'.$insert_id);			 exit();		}		else {			mysql_close($con);			header('location: upload_file.php?error='.urlencode($errmsg));			exit();		}}mysql_close($con);?>

And then the Upload_Functions.php as specified in the Upload_Process.php Form:

<?phpfunction createAPMImage($vals, $length, $fn, $name) {		$width = 300;		$height = 200;		$pixelsPerSecond = $width/ $length;		$pic = imagecreatetruecolor($width,$height);		$lineColor = imagecolorallocate($pic,0,0,0);		$lineColorGrey = imagecolorallocate($pic,192,192,192);		$bgColor = imagecolorallocate($pic,255,255,255);		$bgColorT = imagecolorallocatealpha($pic,255,255,255,127);		imagefill($pic,0,0,$bgColorT);		// first create x/y pairs		$xypair = array();		$maxapm = 0;		for ($x = 1;$x <= $width;$x++) {				$secs = ceil($x / $pixelsPerSecond);				$apm = 0;				if ($secs < 60) {						for ($tmp = 0;$tmp < $secs;$tmp++)								$apm += $vals[$tmp];						$apm = $apm / $secs * 60;				} else {						for ($tmp = $secs - 60;$tmp < $secs;$tmp++)								$apm += $vals[$tmp];						$apm = $apm;				}				if ($apm > $maxapm)						$maxapm = $apm;				$xypair[$x] = $apm;		}		// then draw them		if ($maxapm == 0)				return;		for ($i = 2;$i <= $width;$i++) {				imageline($pic,$i - 1,$xypair[$i - 1] / $maxapm * $height, $i, $xypair[$i] / $maxapm * $height,$lineColor);		}		$frame = imagecreatetruecolor($width +50,$height+50);		imagestring($frame,4,55,0,"Time (minutes) - ".$name,$lineColor);		imagefill($frame,0,0,$bgColor);		imagerectangle($frame,40,0,$width + 40,$height,$lineColor);		imageline($frame,40,$height / 2,$width + 40,$height / 2, $lineColorGrey);		imagestringup($frame,4,5,$height - 15,"APM -->",$lineColor);		imagestring($frame,4,55,$height + 20,"Time (minutes) ",$lineColor);		imagestring($frame,4,55,0,$name,$lineColor);		imagestring($frame,2,25,$height - 15,"0",$lineColor);		imagestring($frame,2,20,($height / 2),floor($maxapm / 2),$lineColor);		imagestring($frame,2,20,0,floor($maxapm),$lineColor);		$lengthMins = ($length / 60);		for ($i = 0;$i < $lengthMins;$i+=5) {				imagestring($frame,2,40+($width / ($lengthMins / 5) * ($i / 5)),$height + 5,$i,$lineColor);				if ($i > 0)						imageline($frame,40+($width / ($lengthMins / 5) * ($i / 5)),0,40+($width / ($lengthMins / 5) * ($i / 5)),$height, $lineColorGrey);		}		imagecopy($frame,$pic,40,0,0,0,$width,$height);		imagepng($frame,$fn);		imagedestroy($frame);		imagedestroy($pic);}function mergeAPMFiles($arAPMFiles, $name) {// Create image instances$src = array();for($i =0; $i < count($arAPMFiles); $i++) {	$src[$i] = imagecreatefrompng($arAPMFiles[$i]);}$height = 250 * count($arAPMFiles);$dest = imagecreatetruecolor(350, 1000);// Copyfor($i =0; $i < count($arAPMFiles); $i++) {	imagecopy($dest, $src[$i], 0, 250*$i, 0, 0, 350, 250);}// Output and free from memoryimagepng($dest, 'apm_joined/'.$name.'.png');imagedestroy($dest);for($i =0; $i < count($arAPMFiles); $i++) {	imagedestroy($src[$i]);}for($i =0; $i < count($arAPMFiles); $i++) {	unlink($arAPMFiles[$i]);}return true;}function renameFile($filepath, $filename){	$i = 0;	$file_parts = explode('.',$filename);   	$newfilename = $file_parts[0].'_'.$i.'.'.$file_parts[1];	while (file_exists($filepath.$newfilename)) {		$i++;		//echo "rejecting ".$newfilename."..."."\n";		$newfilename = $file_parts[0].'_'.$i.'.'.$file_parts[1];	}   	return $newfilename; }?>

As well as the PHP Code to connect to the MySQL Database:

<?php$con = mysql_connect("mysql913.ixwebhosting.com","Aaron21_Upload","hidden");if (!$con)  {  die('Could not connect to the Database: ' . mysql_error());  }mysql_select_db('Aaron21_Upload');// Site URL, Traling slash is required$site_url = 'http://www.sc2x.net/';?>

Link to comment
Share on other sites

I can't look at your PHP code just from URLs, you have to actually paste the code here. It's not redirecting to a Google code page, the URL still says sc2x.net, not code.google.com. It appears that you have copied the source code for the Google code page instead of the code for the replay parser which is displayed on that page.
Of course I'm not just linking to the Google Code website in the PHP Form, I just posted a link to seperate the everything. In reality I'm using the actual code that's posted on those two websites in two seperate files being MPQFile.php and SC2Replay.php
Link to comment
Share on other sites

Of course I'm not just linking to the Google Code website in the PHP Form, I just posted a link to seperate the everything. In reality I'm using the actual code that's posted on those two websites in two seperate files being MPQFile.php and SC2Replay.php
Anyone have any ideas?
Link to comment
Share on other sites

It really looks like the same thing I keep thinking. I see what appears to be your undernet email address on the copied Google page, but it's clear that I'm not logged in as you because all of the links on that page are relative links so they all point to your server still.My #1 suspicion is still this:1. You went to http://code.google.com/p/phpsc2replay/sour...php/mpqfile.php2. You went to View->Source in your browser3. You copied the code, and pasted it into mpqfile.php on your serverI asked you what was in the files you're running, but instead of pasting in the code from your mpqfile.php file that your server is actually executing, you linked me to the Google site. I understand what is supposed to be in mpqfile.php, but what I'm interested in is what is actually in the file being executed on your server. If you open your mpqfile.php and search for your email address and find it, that's a problem.

Link to comment
Share on other sites

It really looks like the same thing I keep thinking. I see what appears to be your undernet email address on the copied Google page, but it's clear that I'm not logged in as you because all of the links on that page are relative links so they all point to your server still.My #1 suspicion is still this:1. You went to http://code.google.com/p/phpsc2replay/sour...php/mpqfile.php2. You went to View->Source in your browser3. You copied the code, and pasted it into mpqfile.php on your serverI asked you what was in the files you're running, but instead of pasting in the code from your mpqfile.php file that your server is actually executing, you linked me to the Google site. I understand what is supposed to be in mpqfile.php, but what I'm interested in is what is actually in the file being executed on your server. If you open your mpqfile.php and search for your email address and find it, that's a problem.
If I went to that Google Code page you'd see it in the above coding =/
Link to comment
Share on other sites

The question is, if you go to YOUR page:http://www.sc2x.net/test/mpqfile.phpWith your FTP or whatever....and search the source....Can you find your email?If you can, that's the problem. The whole thing was copied in a wrong fashion.

Link to comment
Share on other sites

The question is, if you go to YOUR page:http://www.sc2x.net/test/mpqfile.phpWith your FTP or whatever....and search the source....Can you find your email?If you can, that's the problem. The whole thing was copied in a wrong fashion.
Huh? Are you saying theres a file on the FTP which has my E-mail Address? Why would it be in the file on the FTP but not in the coding above?
Link to comment
Share on other sites

Argh.You didn't show us the code in your actual file. I asked you what was in your mpqfile.php script, and you pointed me to the Google site. I don't care what's on the Google site. I care what is in the actual file on your server. Open up your copy of mpqfile.php, not the Google site, and paste the code from your actual copy of the file. It doesn't matter what's on the Google site because your server is not running code from the Google site. Your server is running code in your mpqfile.php file, that's what I want to see. Posting a link to the Google site does not mean that same code is in your PHP file.

Why would it be in the file on the FTP but not in the coding above?
Because the upload process file has this line:
if (class_exists("MPQFile") || (include 'mpqfile.php')) {

I want to know what code is in that file, the mpqfile.php that is actually being executed.

Link to comment
Share on other sites

Yep that seemed to fix the problem in which it was linking to the Google Page, but it presented something else now.Now it's just refreshing the Upload Form when I upload a file, rather than displaying the results. I'll post the old MPQFile.php and the old SC2Replay.php Files, as well as the two which replaced them. Here's the coding for the old files:

Link to comment
Share on other sites

MPQFILE.PHP (OLD)<?php/* 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/>.*/define("MPQ_HASH_TABLE_OFFSET", 0);define("MPQ_HASH_NAME_A", 1);define("MPQ_HASH_NAME_B", 2);define("MPQ_HASH_FILE_KEY", 3);define("MPQ_HASH_ENTRY_EMPTY", (0xFFFF << 16) | 0xFFFF);define("MPQ_HASH_ENTRY_DELETED", (0xFFFF << 16) | 0xFFFE);define("MPQ_NOT_PARSED", 2);define("MPQ_PARSE_OK", 1);define("MPQ_ERR_NOTMPQFILE", -1);class MPQFile { private $filename; private $fp; private $hashtable,$blocktable; private $hashTableSize, $blocKTableSize; private $headerOffset; private $init; private $verMajor; private $build; private $sectorSize; private $debug; private $debugNewline; private $gameLen; public static $cryptTable; function __construct($filename, $autoparse = true, $debug = 0) { $this->filename = $filename; $this->hashtable = NULL; $this->blocktable = NULL; $this->hashTableSize = 0; $this->blockTableSize = 0; $this->headerOffset = 0; $this->init = false; $this->verMajor = 0; $this->build = 0; $this->gameLen = 0; $this->sectorSize = 0; $this->debug = $debug; $this->debugNewline = "<br />\n"; if (!self::$cryptTable) self::initCryptTable(); if (file_exists($this->filename)) { $this->fp = fopen($this->filename, 'rb'); if ($this->debug && $this->fp === false) $this->debug("Error opening file $filename for reading"); } if ($autoparse) $this->parseHeader(); } function __destruct() { if ($this->fp !== FALSE) fclose($this->fp); } private function debug($message) { echo $message.($this->debugNewline); } function setDebugNewline($str) { $this->debugNewline = $str; } function setDebug($bool) { $this->debug = $bool; } function parseHeader() { if ($this->fp === FALSE) { return false; if ($this->debug) $this->debug("Invalid file pointer"); } $fp = $this->fp; $headerParsed = false; $headerOffset = 0; while (!$headerParsed) { $magic = unpack("c4",fread($fp,4)); // MPQ 1Bh or 1Ah if (($magic[1] != 0x4D) || ($magic[2] != 0x50) || ($magic[3] != 0x51)) { $this->init = MPQ_ERR_NOTMPQFILE; return false; } if ($magic[4] == 27) { // user data block (1Bh) if ($this->debug) $this->debug(sprintf("Found user data block at %08X",ftell($fp))); $uDataMaxSize = $this->readUInt32(); $headerOffset = $this->readUInt32(); $this->headerOffset = $headerOffset; $uDataSize = $this->readUInt32(); fseek($fp,24,SEEK_CUR); // skip Starcraft II replay 0x1B 0x32 0x01 0x00 $verMajor = $this->readUInt16(); $this->verMajor = $verMajor; $build = $this->readUInt32(true); $this->build = $build; $build2 = $this->readUInt32(true); fseek($fp,2,SEEK_CUR); // skip 02 00 $gameLen = $this->readUInt16(true) / 2; $this->gameLen = $gameLen; fseek($fp,$headerOffset); } else if ($magic[4] == 26) { // header (1Ah) if ($this->debug) $this->debug(sprintf("Found header at %08X",ftell($fp))); $headerSize = $this->readUInt32(); $archiveSize = $this->readUInt32(); $formatVersion = $this->readUInt16(); $sectorSizeShift = $this->readByte(); $sectorSize = 512 * pow(2,$sectorSizeShift); $this->sectorSize = $sectorSize; fseek($fp, 1, SEEK_CUR); $hashTableOffset = $this->readUInt32() + $headerOffset; $blockTableOffset = $this->readUInt32() + $headerOffset; $hashTableEntries = $this->readUInt32(); $this->hashTableSize = $hashTableEntries; $blockTableEntries = $this->readUInt32(); $this->blockTableSize = $blockTableEntries; $headerParsed = true; } else { if ($this->debug) $this->debug("Could not find MPQ header"); return false; } } // read and decode the hash table fseek($this->fp, $hashTableOffset); $hashSize = $hashTableEntries * 4; // hash table size in 4-byte chunks $tmp = array(); for ($i = 0;$i < $hashSize;$i++) $tmp[$i] = $this->readUInt32(); $hashTable = self::decryptStuff($tmp,self::hashStuff("(hash table)", MPQ_HASH_FILE_KEY)); if ($this->debug) { $this->debug("DEBUG: Hash table"); $this->debug("HashA, HashB, Language+platform, Fileblockindex"); $tmpnewline = $this->debugNewline; $this->debugNewline = ""; for ($i = 0;$i < $hashTableEntries;$i++) { $filehashA = $hashTable[$i*4]; $filehashB = $hashTable[$i*4 +1]; $lanplat = $hashTable[$i*4 +2]; $blockindex = $hashTable[$i*4 +3]; $this->debug(sprintf("<pre>%08X %08X %08X %08X</pre>",$filehashA, $filehashB, $lanplat, $blockindex)); } $this->debugNewline = $tmpnewline; } // read and decode the block table fseek($this->fp, $blockTableOffset); $blockSize = $blockTableEntries * 4; // block table size in 4-byte chunks $tmp = array(); for ($i = 0;$i < $blockSize;$i++) $tmp[$i] = $this->readUInt32(); $blockTable = self::decryptStuff($tmp,self::hashStuff("(block table)", MPQ_HASH_FILE_KEY)); $this->hashtable = $hashTable; $this->blocktable = $blockTable; if ($this->debug) { $this->debug("DEBUG: Block table"); $this->debug("Offset, Blocksize, Filesize, flags"); $tmpnewline = $this->debugNewline; $this->debugNewline = ""; for ($i = 0;$i < $blockTableEntries;$i++) { $blockIndex = $i * 4; $blockOffset = $this->blocktable[$blockIndex] + $this->headerOffset; $blockSize = $this->blocktable[$blockIndex + 1]; $fileSize = $this->blocktable[$blockIndex + 2]; $flags = $this->blocktable[$blockIndex + 3]; $this->debug(sprintf("<pre>%08X %8d %8d %08X</pre>",$blockOffset, $blockSize, $fileSize, $flags)); } $this->debugNewline = $tmpnewline; } $this->init = MPQFILE_PARSE_OK; return true; } // read little endian 32-bit integer private function readUInt32($bigendian = false) { if ($this->fp === FALSE) return false; $t = unpack(($bigendian === true)?"N":"V",fread($this->fp,4)); return $t[1]; } private function readUInt16($bigendian = false) { if ($this->fp === FALSE) return false; $t = unpack(($bigendian === true)?"n":"v",fread($this->fp,2)); return $t[1]; } private function readByte() { if ($this->fp === FALSE) return false; $t = unpack("C",fread($this->fp,1)); return $t[1]; } // read a byte from string and remove the read byte private function readSByte(&$string) { $t = unpack("C",substr($string,0,1)); $string = substr($string,1,strlen($string) -1); return $t[1]; } function getFileSize($filename) { if ($this->init !== MPQFILE_PARSE_OK) { if ($this->debug) $this->debug("Tried to use getFileSize without initializing"); return false; } $hashA = self::hashStuff($filename, MPQ_HASH_NAME_A); $hashB = self::hashStuff($filename, MPQ_HASH_NAME_:); $hashStart = self::hashStuff($filename, MPQ_HASH_TABLE_OFFSET) & ($this->hashTableSize - 1); $tmp = $hashStart; do { if (($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_DELETED) || ($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_EMPTY)) return false; if (($this->hashtable[$tmp*4] == $hashA) && ($this->hashtable[$tmp*4 + 1] == $hashB)) { // found file $blockIndex = ($this->hashtable[($tmp *4) + 3]) *4; $fileSize = $this->blocktable[$blockIndex + 2]; return $fileSize; } $tmp = ($tmp + 1) % $this->hashTableSize; } while ($tmp != $hashStart); if ($this->debug) $this->debug("Did not find file $filename in archive"); return false; } function readFile($filename) { if ($this->init !== MPQFILE_PARSE_OK) { if ($this->debug) $this->debug("Tried to use getFile without initializing"); return false; } $hashA = self::hashStuff($filename, MPQ_HASH_NAME_A); $hashB = self::hashStuff($filename, MPQ_HASH_NAME_:); $hashStart = self::hashStuff($filename, MPQ_HASH_TABLE_OFFSET) & ($this->hashTableSize - 1); $tmp = $hashStart; $blockSize = -1; do { if (($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_DELETED) || ($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_EMPTY)) return false; if (($this->hashtable[$tmp*4] == $hashA) && ($this->hashtable[$tmp*4 + 1] == $hashB)) { // found file $blockIndex = ($this->hashtable[($tmp *4) + 3]) *4; $blockOffset = $this->blocktable[$blockIndex] + $this->headerOffset; $blockSize = $this->blocktable[$blockIndex + 1]; $fileSize = $this->blocktable[$blockIndex + 2]; $flags = $this->blocktable[$blockIndex + 3]; break; } $tmp = ($tmp + 1) % $this->hashTableSize; } while ($tmp != $hashStart); if ($blockSize == -1) { if ($this->debug) $this->debug("Did not find file $filename in archive"); return false; } $flag_file = $flags & 0x80000000; $flag_checksums = $flags & 0x04000000; $flag_deleted = $flags & 0x02000000; $flag_singleunit = $flags & 0x01000000; $flag_hEncrypted = $flags & 0x00020000; $flag_encrypted = $flags & 0x00010000; $flag_compressed = $flags & 0x00000200; $flag_imploded = $flags & 0x00000100; if ($this->debug) $this->debug(sprintf("Found file $filename with flags %08X, block offset %08X, block size %d and file size %d", $flags, $blockOffset,$blockSize,$fileSize)); if (!$flag_file) return false; fseek($this->fp,$blockOffset); if ($flag_checksums) { for ($i = $fileSize;$i > 0;$i -= $this->sectorSize) { $sectors[] = $this->readUInt32(); $blockSize -= 4; } $sectors[] = $this->readUInt32(); $blockSize -= 4; } else { $sectors[] = 0; $sectors[] = $blockSize; } $c = count($sectors) - 1; $totDur = 0; for ($i = 0;$i < $c;$i++) { $sectorLen = $sectors[$i + 1] - $sectors[$i]; fseek($this->fp,$blockOffset + $sectors[$i],SEEK_SET); $sectorData = fread($this->fp,$sectorLen); if ($flag_compressed && (($flag_singleunit && ($blockSize < $fileSize)) || ($flag_checksums && ($sectorLen < $this->sectorSize)))) { $compressionType = $this->readSByte($sectorData); switch ($compressionType) { case 2: $output .= self::deflate_decompress($sectorData); break; default: if ($this->debug) $this->debug(sprintf("Unknown compression type: %d",$compressionType)); return false; } } else $output .= $sectorData; } if (strlen($output) != $fileSize) { if ($this->debug) $this->debug(sprintf("Decrypted/uncompressed file size(%d) does not match original file size(%d)", strlen($output),$fileSize)); return false; } return $output; } function parseReplay() { if ($this->init !== MPQFILE_PARSE_OK) { if ($this->debug) $this->debug("Tried to use parseReplay without initializing"); return false; } if (class_exists("SC2Replay") || (include 'sc2replay.php')) { $tmp = new SC2Replay(); if ($this->debug) $tmp->setDebug($this->debug); $tmp->parseReplay($this); return $tmp; } else { if ($this->debug) $this->debug("Unable to find or load class SC2Replay"); return false; } } function getState() { return $this->init; } function getBuild() { return $this->build; } function getVersion() { return $this->verMajor; } function getGameLength() { return $this->gameLen; } static function deflate_decompress($string) { if (function_exists("gzinflate")){ $tmp = gzinflate(substr($string,2,strlen($string) - 2)); return $tmp; } if ($this->debug) $this->debug("Function 'gzinflate' does not exist, is gzlib installed as a module?"); return false; } static function initCryptTable() { if (!self::$cryptTable) self::$cryptTable = array(); $seed = 0x00100001; $index1 = 0; $index2 = 0; for ($index1 = 0; $index1 < 0x100; $index1++) { for ($index2 = $index1, $i = 0; $i < 5; $i++, $index2 += 0x100) { $seed = (uPlus($seed * 125,3)) % 0x2AAAAB; $temp1 = ($seed & 0xFFFF) << 0x10; $seed = (uPlus($seed * 125,3)) % 0x2AAAAB; $temp2 = ($seed & 0xFFFF); self::$cryptTable[$index2] = ($temp1 | $temp2); } } } static function hashStuff($string, $hashType) { $seed1 = 0x7FED7FED; $seed2 = ((0xEEEE << 16) | 0xEEEE); $strLen = strlen($string); for ($i = 0;$i < $strLen;$i++) { $next = ord(strtoupper(substr($string, $i, 1))); $seed1 = self::$cryptTable[($hashType << 8) + $next] ^ (uPlus($seed1,$seed2)); $seed2 = uPlus(uPlus(uPlus(uPlus($next,$seed1),$seed2),$seed2 << 5),3); } return $seed1; } static function decryptStuff($data, $key) { $seed = ((0xEEEE << 16) | 0xEEEE); $datalen = count($data); for($i = 0;$i < $datalen;$i++) { $seed = uPlus($seed,self::$cryptTable[0x400 + ($key & 0xFF)]); $ch = $data[$i] ^ (uPlus($key,$seed)); $data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF); $key = (uPlus(((~$key) << 0x15), 0x11111111)) | (rShift($key,0x0B)); $seed = uPlus(uPlus(uPlus($ch,$seed),($seed << 5)),3); } return $data; }}function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec);}// function that adds up two integers without allowing them to overflow to floatsfunction uPlus($o1, $o2) { $o1h = ($o1 >> 16) & 0xFFFF; $o1l = $o1 & 0xFFFF; $o2h = ($o2 >> 16) & 0xFFFF; $o2l = $o2 & 0xFFFF; $ol = $o1l + $o2l; $oh = $o1h + $o2h; if ($ol > 0xFFFF) { $oh += (($ol >> 16) & 0xFFFF); } return ((($oh << 16) & (0xFFFF << 16)) | ($ol & 0xFFFF));}// right shift without preserving the sign(leftmost) bitfunction rShift($num,$bits) { return (($num >> 1) & 0x7FFFFFFF) >> ($bits - 1);}?>

Link to comment
Share on other sites

SC2REPLAY.PHP (OLD)<?php/* 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/>.*/define("MPQ_ERR_LOWREPLAYVERSION", -1);class SC2Replay { public static $gameSpeeds = array(0 => "Slower", 1=> "Slow", 2=> "Normal", 3=> "Fast", 4=> "Faster"); public static $gameSpeedCE = array(0 => 39, 1=> 44, 2=> 60, 3=> 64, 4=> 64); // estimates, weird values public static $colorIndices = array(1 => "Red", 2=> "Blue", 3=> "Teal", 4=> "Purple", 5=> "Yellow", 6 => "Orange", 7=> "Green", 8=> "Pink"); private $players; //array, indices: color, team, sname, lname, race, startRace, handicap, ptype private $gameLength; // game length in seconds private $mapName; private $gameSpeed; // game speed, number from 0-4. see $gameSpeeds array above private $teamSize; // team size in the format xvx, eg. 1v1 private $gamePublic; private $version; private $build; private $events; // contains an array of the events in replay.game.events file private $debug; // debug, currently true or false private $debugNewline; // contents are appended to the end of all debug messages private $messages; // contains an array of the chat log messages function __construct() { $this->players = array(); $this->gameLength = 0; $this->mapName = NULL; $this->gameSpeed = 0; $this->teamSize = NULL; $this->debug = false; $this->debugNewline = "<br />\n"; } // parameter needs to be an instance of MPQFile function parseReplay($mpqfile) { if (!is_a($mpqfile, "MPQFile")) return false; $this->gameLength = $mpqfile->getGameLength(); if ($mpqfile->getBuild() < 15097) { if ($this->debug) $this->debug("Too low replay version"); return MPQ_ERR_LOWREPLAYVERSION; //demo format changed at patch 9, no support for older ones } $this->version = $mpqfile->getVersion(); $this->build = $mpqfile->getBuild(); // first parse replay.details file $file = $mpqfile->readFile("replay.details"); if ($file !== false) { $this->parseDetailsFile($file); } else if ($this->debug) $this->debug("Error reading the replay.details file"); $file = $mpqfile->readFile("replay.attributes.events"); if ($file !== false) { $this->parseAttributesFile($file);// the following is fallback game length calculation, left here in case the value in mpq header ever breaks or goes away// $fs = $mpqfile->getFileSize("replay.sync.events");// if ($fs !== false) $this->gameLength = $fs / self::$gameSpeedCE[$this->gameSpeed]; // sync event is 4 bytes, with a sync window of 1/8th to 1/16th of a second <---- UNCERTAIN, a theory } else if ($this->debug) $this->debug("Error reading the replay.attributes.events file"); $file = $mpqfile->readFile("replay.game.events"); if (file !== false) $this->parseGameEventsFile($file); else if ($this->debug) $this->debug("Error reading the replay.game.events file"); $file = $mpqfile->readFile("replay.message.events"); if (file !== false) $this->parseChatLog($file); else if ($this->debug) $this->debug("Error reading the replay.message.events file"); } private function debug($message) { echo $message.($this->debugNewline); } function setDebugNewline($str) { $this->debugNewline = $str; } function setDebug($num) { $this->debug = $num; } function getPlayers() { return $this->players; } function getMapName() { return $this->mapName; } function getGameSpeed() { return $this->gameSpeed; } function getGameSpeedText() { return self::$gameSpeeds[$this->gameSpeed]; } function getTeamSize() { return $this->teamSize; } function getVersion() { return $this->version; } function getBuild() { return $this->build; } function getMessages() { return $this->messages; } // getFormattedGameLength returns the time in h hrs, m mins, s secs function getFormattedGameLength() { $hrs = floor($this->gameLength / 3600); $mins = floor($this->gameLength / 60); $secs = $this->gameLength % 60; if ($hrs > 0) $o = "$hrs hrs, "; if ($mins > 0) $o .= "$mins mins, "; $o .= "$secs secs"; return $o; } function getEvents() { return $this->events; } function getGameLength() { return $this->gameLength; } // parse replay.details file and add parsed stuff to the object // $string contains the contents of the file function parseDetailsFile($string) { if ($this->debug) $this->debug("Parsing replay.details file..."); $numByte = 0; $numByte += 6; $numPlayers = $this->readByte($string,$numByte) / 2; for ($i = 0; $i < $numPlayers;$i++) { $p = $this->parsePlayerStruct($string,$numByte); if ($p !== NULL) { $p['id'] = $i; $this->players[$i] = $p; } } $mapnameLen = $this->readByte($string,$numByte) / 2; $this->mapName = $this->readBytes($string,$numByte,$mapnameLen); $numByte += 2; // 04 02 $u1Len = $this->readByte($string,$numByte) / 2; if ($u1Len > 0) $this->readByte($string,$numByte,$u1Len); $numByte += 5; // 06 05 02 00 02 $minimapnameLen = $this->readByte($string,$numByte) / 2; $minimapName = $this->readBytes($string,$numByte,$minimapnameLen); } // parse a player struct in the replay.details file private function parsePlayerStruct($string,&$numByte) { $numByte += 4; $sNameLen = $this->readByte($string,$numByte) / 2; if ($sNameLen > 0) $sName = $this->readBytes($string,$numByte,$sNameLen); else $sName = NULL; $numByte += 5; // 02 05 08 00 09 $numByte += 4; // 00/04 02 07 00 $numByte += 3; // 00 00 00 // 00 53 32 (S2) $hadKey = true; $keys = array(); while ($hadKey) { $hadKey = false; $key = unpack("c2",$this->readBytes($string,$numByte,2)); if ($key[2] == 9) { $hadKey = true; $keys[$key[1]] = $this->parseKeyVal($string,$numByte); } else if ($key[1] == 6 && $key[2] == 2) { break; } } if ($this->debug) { foreach ($keys as $k => $v) $this->debug("Got pre-longname($sName) key: $k, value: $v"); } $lNameLen = $this->readByte($string,$numByte) / 2; if ($lNameLen > 0) $lName = $this->readBytes($string,$numByte,$lNameLen); else $lName = NULL; $numByte += 2; // 04 02 $raceLen = $this->readByte($string,$numByte) / 2; if ($raceLen > 0) $race = $this->readBytes($string,$numByte,$raceLen); else $race = NULL; $numByte += 3; // 06 05 08 $hadKey = true; while ($hadKey) { $keyVal = ""; $hadKey = false; $key = unpack("c2",$this->readBytes($string,$numByte,2)); if ($key[2] == 9) { $hadKey = true; $keyVal = $this->parseKeyVal($string,$numByte); if ($key[1] == 2) { $cR = $keyVal; } // red color if ($key[1] == 4) { $cG = $keyVal; } // green color if ($key[1] == 6) { $cB = $keyVal; } // blue color if ($key[1] == 16) { $party = $keyVal / 2; } // party number? if ($this->debug) $this->debug(sprintf("%s Key: %d, value: %d",$sName,$key[1], $keyVal)); } else if ($key[1] == 5 && $key[2] == 18) {$numByte -= 2; break; } // next player else if ($key[1] == 2 && $key[2] == 2) { break; } // end of player section } if (($sName === NULL) && ($lName === NULL)) { if ($this->debug) $this->debug("Got null player"); return NULL; } $p = array(); $p["sName"] = $sName; $p["lName"] = $lName; $p["race"] = $race; $p["party"] = $party; $p["color"] = sprintf("%02X%02X%02X",$cR,$cG,$cB); if ($this->debug) $this->debug(sprintf("Got player: %s (%s), Race: %s, Party: %s, Color: %s",$sName, $lName, $race, $party, $p["color"])); return $p; } // parameter is the contents of the replay.attributes.events file private function parseAttributesFile($string) { if ($this->debug) $this->debug("Parsing replay.attributes.events file"); $numByte = 4; // skip the 4-byte header $numAttribs = $this->readUInt32($string,$numByte); for ($i = 0;$i < $numAttribs;$i++) { $attribHeader = $this->readUInt16($string,$numByte); $numByte += 2; //skip the 00 00 bytes $attributeId = $this->readUInt16($string,$numByte); $numByte += 2; //skip another 00 00 bytes $playerId = $this->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($:); } $numByte += 4; if ($this->debug) $this->debug(sprintf("Got attrib \"%04X\" for player %d (%s), attribVal = \"%s\"", $attributeId,$playerId,(($playerId == 0x10)?"ALL":$this->players[$playerId]["sName"]),$attribVal)); switch ($attributeId) { case 0x07D3: // team, VERY uncertain because of gazillion other 0x07DX values $this->players[$playerId]["team"] = intval(substr($attribVal,1)); break; case 0x0BBB: // handicap $this->players[$playerId]["handicap"] = $attribVal; break; case 0x0BB8: // game speed if ($this->build >= 15449) { switch ($attribVal) { case "Fasr": $tmp = 4; break; case "Fast": $tmp = 3; break; default: $tmp = 2; } $this->gameSpeed = $tmp; } else { $this->gameSpeed = intval($attribVal); } break; case 0x01F4: // player type, Humn or Comp $this->players[$playerId]["ptype"] = $attribVal; break; case 0x0BB9: // initial race, Prot Terr Zerg or RAND $this->players[$playerId]["srace"] = $attribVal; break; case 0x07D1: // teamsizes $this->teamSize = $attribVal; break; case 0x0BC1: // game type, private(Priv)/open(Amm)? $this->gamePublic = (($attribVal == "Priv")?false:true); break; case 0x0BBA: // color index if ($this->build >= 15449) { $this->players[$playerId]["colorIndex"] = intval(substr($attribVal,2)); $this->players[$playerId]["sColor"] = self::$colorIndices[intval(substr($attribVal,2))]; } else { $this->players[$playerId]["colorIndex"] = intval($attribVal); $this->players[$playerId]["sColor"] = self::$colorIndices[intval($attribVal)]; } break; default: } } } // parse a key/value -pair struct in the replay.details file private function parseKeyVal($string, &$numByte) { $one = $this->readByte($string,$numByte); //$one[1]; if (($one & 192) > 0) { // check if value is two bytes $two = unpack("v",substr($string,$numByte -1,2)); $two = ($two[1] >> 2); // get rid of extra bits $numByte += 1; return $two; } return $one; } private function readByte($string, &$numByte) { $tmp = unpack("C",substr($string,$numByte,1)); $numByte++; return $tmp[1]; } private function readBytes($string, &$numByte, $length) { $tmp = substr($string,$numByte,$length); $numByte += $length; return $tmp; } private function readUInt16($string, &$numByte) { $tmp = unpack("v",substr($string,$numByte,2)); $numByte += 2; return $tmp[1]; } private function readUInt32($string, &$numByte) { $tmp = unpack("V",substr($string,$numByte,4)); $numByte += 4; return $tmp[1]; } private function readUnitTypeID($string,&$numByte) { return (($this->readByte($string,$numByte) << 16) | ($this->readByte($string,$numByte) << 8) | ($this->readByte($string,$numByte))); } private function readUnitAbility($string) { $bytes = unpack("C3",substr($string,4,3)); return (($bytes[1] << 16) | ($bytes[2] << 8) | ($bytes[3])); } // gets players who actually played in the game, meaning excludes observers and party members. public function getActualPlayers() { $tmp = array(); foreach ($this->players as $val) if ($val['party'] > 0) $tmp[] = $val; return $tmp; } // parameter is the contents of the replay.message.events -file private function parseChatLog($string) { $numByte = 0; $len = strlen($string); $messages = array(); $totTime = 0; while ($numByte < $len) { $timestamp = $this->parseTimeStamp($string,$numByte); $playerId = $this->readByte($string,$numByte); $opcode = $this->readByte($string,$numByte); $totTime += $timestamp; if ($opcode == 0x80) // header weird thingy? $numByte += 4; else if ($opcode == 0x00 || $opcode == 0x02 || $opcode == 0x0a) { // message $messageTarget = $opcode & 3; $messageLength = $this->readByte($string,$numByte); if ($opcode == 0x0a) $messageLength += 64; $message = $this->readBytes($string,$numByte,$messageLength); $messages[] = array('id' => $playerId, 'name' => $this->players[$playerId]['sName'], 'target' => $messageTarget, 'time' => floor($totTime / 16), 'message' => $message); } else if ($opcode == 0x83) { // ping on map? 9 bytes? $numByte += 9; } } $this->messages = $messages; } // parameter is the contents of the replay.game.events -file private function parseGameEventsFile($string) { $numByte = 0; $len = strlen($string); $playerLeft = array(); $events = array(); $time = 0; while ($numByte < $len) { $timeStamp = $this->parseTimeStamp($string,$numByte); $nextByte = $this->readByte($string,$numByte); $eventType = $nextByte >> 5; // 3 lowest bits $globalEventFlag = $nextByte & 16; // 4th bit $playerId = $nextByte & 15; // bits 5-8 $playerName = $this->players[$playerId]['sname']; $eventCode = $this->readByte($string,$numByte); $time += $timeStamp; // weird timestamp values mean that there's likely a problem with the alignment of the parse(too few/too many bytes read for an eventcode) if ($this->debug >= 2) { if ($len - $numByte > 24) { $bytes = unpack("C24",substr($string,$numByte,24)); $dataBytes = ""; for ($i = 1;$i <= 24;$i++) $dataBytes .= sprintf("%02X",$bytes[$i]); $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X, Data: %s<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte,$dataBytes)); } } switch ($eventType) { case 0x00: // initialization switch ($eventCode) { case 0x1B: // Player enters game case 0x0B: break; case 0x05: // game starts break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x01: // action switch ($eventCode) { case 0x09: // player quits the game if ($this->players[$playerId]['party'] > 0) // don't log observers/party members etc $playerLeft[] = $playerId; break; case 0x0B: // player uses an ability // at least 32 bytes $data = $this->readBytes($string,$numByte,32); $reqTarget = unpack("C",substr($data,7,1)); $reqTarget = $reqTarget[1]; $ability = $this->readUnitAbility($data); if ($ability != 0xFFFF0F) { $events[] = array('p' => $playerId, 't' => $time, 'a' => $ability); $this->events = $events; } // at least with attack, move, right-click, if the byte after unit ability bytes is // 0x30 or 0x50, the struct takes 1 extra byte. With build orders the struct seems to be 32 bytes // and this byte is 0x00. // might also be in some other way variable-length. if ($reqTarget == 0x30) $data .= $this->readByte($string,$numByte); if ($reqTarget == 0x50) $data .= $this->readByte($string,$numByte); // update apm array $this->players[$playerId]['apmtotal']++; $this->players[$playerId]['apm'][floor($time / 16)]++; break; case 0x2F: // player sends resources $numByte += 17; // data is 17 bytes long break; case 0x0C: // automatic update of hotkey? case 0x1C: case 0x2C: case 0x3C: // 01 01 01 01 11 01 03 02 02 38 00 01 02 3c 00 01 00 case 0x4C: // 01 02 02 01 0d 00 02 01 01 a8 00 00 01 case 0x5C: // 01 01 01 01 16 03 01 01 03 18 00 01 00 case 0x6C: // 01 04 08 01 03 00 02 01 01 34 c0 00 01 case 0x7C: // 01 05 10 01 01 10 02 01 01 1a a0 00 01 case 0x8C: case 0x9C: case 0xAC: // player changes selection $selFlags = $this->readByte($string,$numByte); $dsuCount = $this->readByte($string,$numByte); $dsuExtraBits = $dsuCount % 8; if ($dsuCount > 0) $dsuMap = $this->readBytes($string,$numByte,floor($dsuCount / 8)); if ($dsuExtraBits != 0) { // not byte-aligned $dsuMapLastByte = $this->readByte($string,$numByte); $nByte = $this->readByte($string,$numByte); $uTypesCount = (($dsuMapLastByte & (0xFF - ((1 << $dsuExtraBits) - 1))) | ($nByte & (0xFF >> (8 - $dsuExtraBits)))); for ($i = 1;$i <= $uTypesCount;$i++) { $n3Bytes = unpack("C3",$this->readBytes($string,$numByte,3)); $tmp = (($nByte & (0xFF - ((1 << $dsuExtraBits) - 1))) | ($nBytes[1] & (0xFF >> (8 - $dsuExtraBits)))); $tmp2 = (($nBytes[1] & (0xFF - ((1 << $dsuExtraBits) - 1))) | ($nBytes[2] & (0xFF >> (8 - $dsuExtraBits)))); $tmp3 = (($nBytes[2] & (0xFF - ((1 << $dsuExtraBits) - 1))) | ($nBytes[3] & (0xFF >> (8 - $dsuExtraBits)))); $tmp = ($tmp << 16) | ($tmp2 << 8) | $tmp3; $uType[$i]['id'] = $tmp; $nByte = $this->readByte($string,$numByte); $tmp = (($nBytes[2] & (0xFF - ((1 << $dsuExtraBits) - 1))) | ($nByte & (0xFF >> (8 - $dsuExtraBits)))); $uType[$i]['count'] = $tmp; } $lByte = $this->readByte($string,$numByte); $tmp = (($nByte & (0xFF - ((1 << $dsuExtraBits) - 1))) | ($lByte & (0xFF >> (8 - $dsuExtraBits)))); $totalUnits = $tmp; //unnecessary to parse unit ID values at this point, so skip them $numByte += $totalUnits * 4; } else { // byte-aligned $uTypesCount = $this->readByte($string,$numByte); for ($i = 1;$i <= $uTypesCount;$i++) { $uType[$i]['id'] = $this->readUnitTypeID($string,$numByte); $uType[$i]['count'] = $this->readByte($string,$numByte); } $totalUnits = $this->readByte($string,$numByte); //unnecessary to parse unit ID values at this point, so skip them $numByte += $totalUnits * 4; } //update apm fields if ($eventCode == 0xAC) { $this->players[$playerId]['apmtotal']++; $this->players[$playerId]['apm'][floor($time / 16)]++; } break; case 0x0D: // manually uses hotkey case 0x1D: case 0x2D: case 0x3D: case 0x4D: case 0x5D: case 0x6D: case 0x7D: case 0x8D: case 0x9D: $byte1 = $this->readByte($string,$numByte); $byte2 = $this->readByte($string,$numByte); $extraBytes = floor($byte1 / 8); $numByte += $extraBytes; $extraExtraByte = ((($byte1 & 4) == 4) && (($byte2 & 3) == 3))?1:0; $numByte += $extraExtraByte; // update apm $this->players[$playerId]['apmtotal']++; $this->players[$playerId]['apm'][floor($time / 16)]++; break; case 0x1F: // no idea $numByte += 17; // 84 00 00 0c 84 00 00 00 80 00 00 00 80 00 00 00 00 break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x02: // weird switch($eventCode) { case 0x06: $numByte += 8; // 00 00 00 04 00 00 00 04 break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x03: // replay switch ($eventCode) { case 0x81: // player moves screen $numByte += 20; // always 20 bytes break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x04: // inaction switch($eventCode) { case 0x00: //automatic synchronization $numByte += 4; break; case 0x16: $numByte += 24; break; case 0x18: $numByte += 4; break; case 0x1C: case 0x2C: // no data break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x05: // system switch($eventCode) { case 0x89: //automatic synchronization? $numByte += 4; break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } } // update winners based on $playerLeft -array $numLeft = count($playerLeft); $numActual = count($this->getActualPlayers()); $lastLeaver = -1; foreach ($playerLeft as $val) { // mark the previous leaver as a loser if ($lastLeaver != -1) $this->players[$val]['won'] = 0; $lastLeaver = $val; } // if the number of players who left is $numActual - 1, then everyone else except the recorder left and he is the winner // if the number of players who left is $numActual - 2, then whoever left after the recorder is the winner. can be determined if the recorder is known. // otherwise the winner cannot be determined, since any one of the players who left after the recorder could be the winner if ($numLeft == ($numActual - 1)) { if ($this->debug) $this->debug("Found winner"); $this->players[$lastLeaver]['won'] = 0; } else { if ($this->debug) $this->debug("Unable to parse winner"); return; } foreach ($this->players as $val) { if (($val['party'] > 0) && (!isset($val['won']))) $winteam = $val['party']; } foreach ($this->players as $val) { if ($val['party'] == $winteam) $this->players[$val['id']]['won'] = 1; else if ($val['party'] > 0) $this->players[$val['id']]['won'] = 0; } } private function parseTimeStamp($string, &$numByte) { $one = $this->readByte($string,$numByte); if (($one & 3) > 0) { // check if value is two bytes or more $two = $this->readByte($string,$numByte); $two = ((($one >> 2 ) << 8) | $two); if (($one & 3) >= 2) { $tmp = $this->readByte($string,$numByte); $two = (($two << 8) | $tmp); if (($one & 3) == 3) { $tmp = $this->readByte($string,$numByte); $two = (($two << 8) | $tmp); } } return $two; } return ($one >> 2); } // gets the literal string from the sc2_abilitycodes array based on the ability code // returns false if the variable doesn't exist or the file cannot be included function getAbilityString($num) { global $sc2_abilityCodes; if (isset($sc2_abilityCodes) || (include 'abilitycodes.php')) { if ($this->build >= 15449) { $num -= 0x400; if ((($num & 0x06FD00) == 0x06FD00) || (($num & 0x08F000) == 0x08F000)) $num -= 0x00F000; } if ($this->debug) return sprintf("%s (%06X)",$sc2_abilityCodes[$num],$num); else return $sc2_abilityCodes[$num]; } return false; }}?>

Link to comment
Share on other sites

MPQFILE.PHP (NEW)<?php/* 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/>.*/define("MPQ_HASH_TABLE_OFFSET", 0);define("MPQ_HASH_NAME_A", 1);define("MPQ_HASH_NAME_B", 2);define("MPQ_HASH_FILE_KEY", 3);define("MPQ_HASH_ENTRY_EMPTY", (0xFFFF << 16) | 0xFFFF);define("MPQ_HASH_ENTRY_DELETED", (0xFFFF << 16) | 0xFFFE);define("MPQ_NOT_PARSED", 2);define("MPQ_PARSE_OK", 1);define("MPQ_ERR_NOTMPQFILE", -1);define("MPQ_SC2REPLAYFILE", 1);define("MPQ_UNKNOWNFILE", 0);class MPQFile { private $filename; private $fp; private $hashtable,$blocktable; private $hashTableSize, $blockTableSize; private $hashTableOffset, $blockTableOffset; private $headerOffset; private $init; private $verMajor; private $build; private $sectorSize; private $debug; private $debugNewline; private $gameLen; private $versionString; public static $cryptTable; private $fileType; private $fileData; private $archiveSize; function __construct($filename, $autoparse = true, $debug = 0) { $this->filename = $filename; $this->hashtable = NULL; $this->blocktable = NULL; $this->hashTableSize = 0; $this->blockTableSize = 0; $this->headerOffset = 0; $this->init = false; $this->verMajor = 0; $this->build = 0; $this->gameLen = 0; $this->sectorSize = 0; $this->debug = $debug; $this->debugNewline = "<br />\n"; $this->versionString = "null"; $this->fileType = MPQ_UNKNOWNFILE; if (!self::$cryptTable) self::initCryptTable(); if (file_exists($this->filename)) { $fp = fopen($this->filename, 'rb'); $contents = fread($fp, filesize($this->filename)); if ($this->debug && $contents === false) $this->debug("Error opening file $filename for reading"); if ($contents !== false) $this->fileData = $contents; fclose($fp); } if ($autoparse) $this->parseHeader(); } private function debug($message) { echo $message.($this->debugNewline); } function setDebugNewline($str) { $this->debugNewline = $str; } function setDebug($bool) { $this->debug = $bool; } static function readByte($string, &$numByte) { $tmp = unpack("C",substr($string,$numByte,1)); $numByte++; return $tmp[1]; } static function readBytes($string, &$numByte, $length) { $tmp = substr($string,$numByte,$length); $numByte += $length; return $tmp; } static function readUInt16($string, &$numByte) { $tmp = unpack("v",substr($string,$numByte,2)); $numByte += 2; return $tmp[1]; } static function readUInt32($string, &$numByte) { $tmp = unpack("V",substr($string,$numByte,4)); $numByte += 4; return $tmp[1]; } function parseHeader() { $fp = 0; $headerParsed = false; $headerOffset = 0; while (!$headerParsed) { $magic = unpack("c4",self::readBytes($this->fileData,$fp,4)); // MPQ 1Bh or 1Ah if (($magic[1] != 0x4D) || ($magic[2] != 0x50) || ($magic[3] != 0x51)) { $this->init = MPQ_ERR_NOTMPQFILE; return false; } if ($magic[4] == 27) { // user data block (1Bh) if ($this->debug) $this->debug(sprintf("Found user data block at %08X",$fp)); $uDataMaxSize = self::readUInt32($this->fileData, $fp); $headerOffset = self::readUInt32($this->fileData, $fp); $this->headerOffset = $headerOffset; $uDataSize = self::readUInt32($this->fileData, $fp); $uDataStart = $fp; //fseek($fp,5,SEEK_CUR); // skip 05 08 00 02 2c //fseek($fp,24,SEEK_CUR); // skip Starcraft II replay 0x1B 0x32 0x01 0x00 //fseek($fp,25,SEEK_CUR); // skip Starcraft II replay 0x1B 0x31 0x31 0x02 0x05 0x0c //fseek($fp,30,SEEK_CUR); $fileTypeHeader = self::readUInt16($this->fileData, $fp); $fp -= 2; if ($uDataSize == 0 || $fileTypeHeader != 0x0805) { // file is not replay file, so skip following section if ($this->debug) $this->debug(sprintf("Unknown user data block(%04X), skipping...",$fileTypeHeader)); $fp = $headerOffset; continue; } if ($this->debug) $this->debug(sprintf("File seems to be a Starcraft 2 replay file.")); $this->fileType = MPQ_SC2REPLAYFILE; $fp += 30; $loop = 0; $versiontemp1 = 0; $versiontemp2 = 0; while ($fp < ($uDataSize + $uDataStart)) { $key = unpack("C2",self::readBytes($this->fileData,$fp,2)); $value = $this->parseKeyVal($this->fileData,$fp); if ($this->debug) $this->debug(sprintf("User header key: %02X %02X value: %d",$key[1],$key[2],$value)); if ($loop == 0) { switch ($key[1]) { case 0x02: // major version $this->verMajor = $value / 2; break; case 0x04: // minor version $versiontemp1 = $value / 2; break; case 0x06: // minorer? version $versiontemp2 = $value / 2; break; case 0x08; $this->build = $value / 2; $loop++; break; default: } } if ($loop == 1) { switch ($key[1]) { case 0x06: $this->gameLen = ceil($value / 32); break; default: } } } $this->versionString = sprintf("%d.%d.%d.%d",$this->verMajor,$versiontemp1,$versiontemp2,$this->build); /* $verMajor = $this->readUInt16(); $this->verMajor = $verMajor; $build = $this->readUInt32(true); $this->build = $build; $build2 = $this->readUInt32(true); fseek($fp,2,SEEK_CUR); // skip 02 00 $gameLen = $this->readUInt16(true) / 2; $this->gameLen = $gameLen; */ $fp = $headerOffset; } else if ($magic[4] == 26) { // header (1Ah) if ($this->debug) $this->debug(sprintf("Found header at %08X",$fp)); $headerSize = self::readUInt32($this->fileData, $fp); $archiveSize = self::readUInt32($this->fileData, $fp); $this->archiveSize = $archiveSize; $formatVersion = self::readUInt16($this->fileData, $fp); $sectorSizeShift = self::readByte($this->fileData, $fp); $sectorSize = 512 * pow(2,$sectorSizeShift); $this->sectorSize = $sectorSize; $fp++; $hashTableOffset = self::readUInt32($this->fileData, $fp) + $headerOffset; $this->hashTableOffset = $hashTableOffset; $blockTableOffset = self::readUInt32($this->fileData, $fp) + $headerOffset; $this->blockTableOffset = $blockTableOffset; if ($this->debug) $this->debug(sprintf("Hash table offset: %08X, Block table offset: %08X",$hashTableOffset, $blockTableOffset)); $hashTableEntries = self::readUInt32($this->fileData, $fp); $this->hashTableSize = $hashTableEntries; $blockTableEntries = self::readUInt32($this->fileData, $fp); $this->blockTableSize = $blockTableEntries; $headerParsed = true; } else { if ($this->debug) $this->debug("Could not find MPQ header"); return false; } } // read and decode the hash table $fp = $hashTableOffset; $hashSize = $hashTableEntries * 4; // hash table size in 4-byte chunks $tmp = array(); for ($i = 0;$i < $hashSize;$i++) $tmp[$i] = self::readUInt32($this->fileData, $fp); if ($this->debug) { $this->debug("Encrypted hash table:"); $this->printTable($tmp); } $hashTable = self::decryptStuff($tmp,self::hashStuff("(hash table)", MPQ_HASH_FILE_KEY)); if ($this->debug) { $this->debug("DEBUG: Hash table"); $this->debug("HashA, HashB, Language+platform, Fileblockindex"); $tmpnewline = $this->debugNewline; $this->debugNewline = ""; for ($i = 0;$i < $hashTableEntries;$i++) { $filehashA = $hashTable[$i*4]; $filehashB = $hashTable[$i*4 +1]; $lanplat = $hashTable[$i*4 +2]; $blockindex = $hashTable[$i*4 +3]; $this->debug(sprintf("<pre>%08X %08X %08X %08X</pre>",$filehashA, $filehashB, $lanplat, $blockindex)); } $this->debugNewline = $tmpnewline; } // read and decode the block table $fp = $blockTableOffset; $blockSize = $blockTableEntries * 4; // block table size in 4-byte chunks $tmp = array(); for ($i = 0;$i < $blockSize;$i++) $tmp[$i] = self::readUInt32($this->fileData, $fp); if ($this->debug) { $this->debug("Encrypted block table:"); $this->printTable($tmp); } $blockTable = self::decryptStuff($tmp,self::hashStuff("(block table)", MPQ_HASH_FILE_KEY)); $this->hashtable = $hashTable; $this->blocktable = $blockTable; if ($this->debug) { $this->debug("DEBUG: Block table"); $this->debug("Offset, Blocksize, Filesize, flags"); $tmpnewline = $this->debugNewline; $this->debugNewline = ""; for ($i = 0;$i < $blockTableEntries;$i++) { $blockIndex = $i * 4; $blockOffset = $this->blocktable[$blockIndex] + $this->headerOffset; $blockSize = $this->blocktable[$blockIndex + 1]; $fileSize = $this->blocktable[$blockIndex + 2]; $flags = $this->blocktable[$blockIndex + 3]; $this->debug(sprintf("<pre>%08X %8d %8d %08X</pre>",$blockOffset, $blockSize, $fileSize, $flags)); } $this->debugNewline = $tmpnewline; } $this->init = MPQ_PARSE_OK; return true; } function getFileSize($filename) { if ($this->init !== MPQ_PARSE_OK) { if ($this->debug) $this->debug("Tried to use getFileSize without initializing"); return false; } $hashA = self::hashStuff($filename, MPQ_HASH_NAME_A); $hashB = self::hashStuff($filename, MPQ_HASH_NAME_:); $hashStart = self::hashStuff($filename, MPQ_HASH_TABLE_OFFSET) & ($this->hashTableSize - 1); $tmp = $hashStart; do { if (($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_DELETED) || ($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_EMPTY)) return false; if (($this->hashtable[$tmp*4] == $hashA) && ($this->hashtable[$tmp*4 + 1] == $hashB)) { // found file $blockIndex = ($this->hashtable[($tmp *4) + 3]) *4; $fileSize = $this->blocktable[$blockIndex + 2]; return $fileSize; } $tmp = ($tmp + 1) % $this->hashTableSize; } while ($tmp != $hashStart); if ($this->debug) $this->debug("Did not find file $filename in archive"); return false; } function readFile($filename) { if ($this->init !== MPQ_PARSE_OK) { if ($this->debug) $this->debug("Tried to use getFile without initializing"); return false; } $hashA = self::hashStuff($filename, MPQ_HASH_NAME_A); $hashB = self::hashStuff($filename, MPQ_HASH_NAME_:); $hashStart = self::hashStuff($filename, MPQ_HASH_TABLE_OFFSET) & ($this->hashTableSize - 1); $tmp = $hashStart; $blockSize = -1; do { if (($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_DELETED) || ($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_EMPTY)) return false; if (($this->hashtable[$tmp*4] == $hashA) && ($this->hashtable[$tmp*4 + 1] == $hashB)) { // found file $blockIndex = ($this->hashtable[($tmp *4) + 3]) *4; $blockOffset = $this->blocktable[$blockIndex] + $this->headerOffset; $blockSize = $this->blocktable[$blockIndex + 1]; $fileSize = $this->blocktable[$blockIndex + 2]; $flags = $this->blocktable[$blockIndex + 3]; break; } $tmp = ($tmp + 1) % $this->hashTableSize; } while ($tmp != $hashStart); if ($blockSize == -1) { if ($this->debug) $this->debug("Did not find file $filename in archive"); return false; } $flag_file = $flags & 0x80000000; $flag_checksums = $flags & 0x04000000; $flag_deleted = $flags & 0x02000000; $flag_singleunit = $flags & 0x01000000; $flag_hEncrypted = $flags & 0x00020000; $flag_encrypted = $flags & 0x00010000; $flag_compressed = $flags & 0x00000200; $flag_imploded = $flags & 0x00000100; if ($this->debug) $this->debug(sprintf("Found file $filename with flags %08X, block offset %08X, block size %d and file size %d", $flags, $blockOffset,$blockSize,$fileSize)); if (!$flag_file) return false; $fp = $blockOffset; if ($flag_checksums) { for ($i = $fileSize;$i > 0;$i -= $this->sectorSize) { $sectors[] = self::readUInt32($this->fileData, $fp); $blockSize -= 4; } $sectors[] = self::readUInt32($this->fileData, $fp); $blockSize -= 4; } else { $sectors[] = 0; $sectors[] = $blockSize; } $c = count($sectors) - 1; $totDur = 0; $output = ""; for ($i = 0;$i < $c;$i++) { $sectorLen = $sectors[$i + 1] - $sectors[$i]; if ($sectorLen == 0) break; $fp = $blockOffset + $sectors[$i]; $sectorData = self::readBytes($this->fileData, $fp,$sectorLen); if ($this->debug) $this->debug(sprintf("Got %d bytes of sector data",strlen($sectorData))); if ($flag_compressed && (($flag_singleunit && ($blockSize < $fileSize)) || ($flag_checksums && ($sectorLen < $this->sectorSize)))) { $numByte = 0; $compressionType = self::readByte($sectorData,$numByte); $sectorData = substr($sectorData,1); switch ($compressionType) { case 0x02: if ($this->debug) $this->debug("Compression type: gzlib"); $output .= self::deflate_decompress($sectorData); break; case 0x10: if ($this->debug) $this->debug("Compression type: bzip2"); $output .= self::bzip2_decompress($sectorData); break; default: if ($this->debug) $this->debug(sprintf("Unknown compression type: %d",$compressionType)); return false; } } else $output .= $sectorData; } if (strlen($output) != $fileSize) { if ($this->debug) $this->debug(sprintf("Decrypted/uncompressed file size(%d) does not match original file size(%d)", strlen($output),$fileSize)); return false; } return $output; } function parseReplay() { if ($this->init !== MPQ_PARSE_OK) { if ($this->debug) $this->debug("Tried to use parseReplay without initializing"); return false; } if (class_exists("SC2Replay") || (include 'sc2replay.php')) { $tmp = new SC2Replay(); if ($this->debug) $tmp->setDebug($this->debug); $tmp->parseReplay($this); return $tmp; } else { if ($this->debug) $this->debug("Unable to find or load class SC2Replay"); return false; } } function isParsed() { return $this->init === MPQ_PARSE_OK; } function getState() { return $this->init; } function getFileType() { return $this->fileType; } function getBuild() { return $this->build; } function getVersion() { return $this->verMajor; } function getVersionString() { return $this->versionString; } function getHashTable() { return $this->hashtable; } function getBlockTable() { return $this->blocktable; } function getGameLength() { return $this->gameLen; } // prints block table or hash table, $data is the data in an array of UInt32s function printTable($data) { $this->debug("Hash table: HashA, HashB, Language+platform, Fileblockindex"); $this->debug("Block table: Offset, Blocksize, Filesize, flags"); $entries = count($data) / 4; $tmpnewline = $this->debugNewline; $this->debugNewline = ""; for ($i = 0;$i < $entries;$i++) { $blockIndex = $i * 4; $blockOffset = $data[$blockIndex] + $this->headerOffset; $blockSize = $data[$blockIndex + 1]; $fileSize = $data[$blockIndex + 2]; $flags = $data[$blockIndex + 3]; $this->debug(sprintf("<pre>%08X %08X %08X %08X</pre>",$blockOffset, $blockSize, $fileSize, $flags)); } $this->debugNewline = $tmpnewline; } // the following replaces a file in the archive, meaning a file with that filename must be present already. function replaceFile($filename, $filedata) { if ($this->getFileSize($filename) === false || strlen($filedata) == 0) return false; if ($this->init !== MPQ_PARSE_OK) { if ($this->debug) $this->debug("Tried to use replaceFile without initializing"); return false; } $hashA = self::hashStuff($filename, MPQ_HASH_NAME_A); $hashB = self::hashStuff($filename, MPQ_HASH_NAME_:); $hashStart = self::hashStuff($filename, MPQ_HASH_TABLE_OFFSET) & ($this->hashTableSize - 1); $tmp = $hashStart; $blockIndex = -1; do { if (($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_DELETED) || ($this->hashtable[$tmp*4 + 3] == MPQ_HASH_ENTRY_EMPTY)) return false; if (($this->hashtable[$tmp*4] == $hashA) && ($this->hashtable[$tmp*4 + 1] == $hashB)) { // found file $blockIndex = ($this->hashtable[($tmp *4) + 3]) *4; $blockOffset = $this->blocktable[$blockIndex] + $this->headerOffset; $blockSize = $this->blocktable[$blockIndex + 1]; $fileSize = $this->blocktable[$blockIndex + 2]; $flags = $this->blocktable[$blockIndex + 3]; break; } $tmp = ($tmp + 1) % $this->hashTableSize; } while ($tmp != $hashStart); if ($blockIndex == -1) return false; // fix block table offsets for ($i = 0;$i < $this->blockTableSize;$i++) { if ($i == $blockIndex) continue; if ($this->blocktable[$i*4] > ($blockOffset - $this->headerOffset)) $this->blocktable[$i*4] -= $blockSize; } if ($this->blockTableOffset > $blockOffset) { $this->blockTableOffset = $this->blockTableOffset - $this->headerOffset - $blockSize; $this->fileData = substr_replace($this->fileData, pack("V",$this->blockTableOffset),$this->headerOffset + 20,4); $this->blockTableOffset += $this->headerOffset; } if ($this->hashTableOffset > $blockOffset) { $this->hashTableOffset = $this->hashTableOffset - $this->headerOffset - $blockSize; $this->fileData = substr_replace($this->fileData, pack("V",$this->hashTableOffset),$this->headerOffset + 16,4); $this->hashTableOffset += $this->headerOffset; } // remove the original file contents $this->fileData = substr_replace($this->fileData,'',$blockOffset,$blockSize); //$this->fileData = substr_replace($this->fileData,str_repeat(chr(0),$blockSize),$blockOffset,$blockSize); $newFileSize = strlen($filedata); // attempt to use bzip2 compression $compressedData = chr(16) . bzcompress($filedata); //$compressedData = $filedata; $newBlockOffset = strlen($this->fileData) - $this->headerOffset; if (strlen($compressedData) >= $newFileSize) { $newFlags = (0x40000000 << 1) | 0x01000000; //$newFlags = 0x81000000; $compressedData = $filedata; $newBlockSize = $newFileSize; } else { $newFlags = (0x40000000 << 1) | 0x01000200; //$newFlags = 0x81000200; $newBlockSize = strlen($compressedData); } // fix archive size $this->fileData = substr_replace($this->fileData, pack("V",$this->archiveSize + $newBlockSize), $this->headerOffset + 8, 4); // populate variables $this->fileData .= $compressedData; $this->printTable($this->blocktable); $this->blocktable[$blockIndex] = $newBlockOffset; $this->blocktable[$blockIndex + 1] = $newBlockSize; $this->blocktable[$blockIndex + 2] = $newFileSize; $this->blocktable[$blockIndex + 3] = $newFlags; // encrypt the block table $resultBlockTable = self::encryptStuff($this->blocktable,self::hashStuff("(block table)", MPQ_HASH_FILE_KEY)); // replace the block table in fileData variable for ($i = 0;$i < $this->blockTableSize * 4;$i++) { //$this->fileData = substr($this->fileData, 0, $this->blockTableOffset + $i * 4) . pack("V",$resultBlockTable[$i]) . substr($this->fileData, $this->blockTableOffset + $i * 4 + 4); //$this->fileData = substr_replace($this->fileData, pack("v",$resultBlockTable[$i] & 0xFFFF), $this->blockTableOffset + $i * 4, 2); //$this->fileData = substr_replace($this->fileData, pack("v",($resultBlockTable[$i] >> 16) & 0xFFFF), $this->blockTableOffset + $i * 4 + 2, 2); $this->fileData = substr_replace($this->fileData, pack("V",$resultBlockTable[$i]), $this->blockTableOffset + $i * 4, 4); } return true; } // saves the mpq data as a file. function saveAs($filename, $overwrite = false) { if (file_exists($filename) && !$overwrite) return false; $fp = fopen($filename, "wb"); if (!$fp) return false; $result = fwrite($fp,$this->fileData); if (!$result) return false; fclose($fp); return true; } function insertChatLogMessage($newMessage, $player, $time) { if ($this->init !== MPQ_PARSE_OK || $this->getFileSize("replay.message.events") == 0) return false; if (!is_numeric($player)) return false; //$playerId = $this->addFakeObserver($player); else $playerId = $player; if ($playerId <= 0) return false; $string = $this->readFile("replay.message.events"); $numByte = 0; $time = $time * 16; $fileSize = strlen($string); $messageSize = strlen($newMessage); if ($messageSize >= 256) return false; $totTime = 0; while ($numByte < $fileSize) { $pastHeaders = true; $start = $numByte; $timestamp = SC2Replay::parseTimeStamp($string,$numByte); $pid = self::readByte($string,$numByte); $opcode = self::readByte($string,$numByte); $totTime += $timestamp; if ($opcode == 0x80) { $numByte += 4; $pastHeaders = false; } else if (($opcode & 0x80) == 0) { // message $messageTarget = $opcode & 3; $messageLength = self::readByte($string,$numByte); if (($opcode & 8) == 8) $messageLength += 64; if (($opcode & 16) == 16) $messageLength += 128; $message = self::readBytes($string,$numByte,$messageLength); } else if ($opcode == 0x83) { // ping on map? 8 bytes? $numByte += 8; } if ($pastHeaders && ($totTime >= $time)) { $opcode = 0; if ($messageSize >= 128) { $opcode = $opcode | 16; $messageSize -= 128; } if ($messageSize >= 64) { $opcode = $opcode | 8; $messageSize -= 64; } break; } } $messageString = pack("c4", 4, $playerId, $opcode, $messageSize). $newMessage; $newData = substr_replace($string, $messageString, $start, 0); $this->replaceFile("replay.message.events", $newData); return true; } // $obsName is the fake observer name, $string is the contents of replay.initData file // DOES NOT WORK CURRENTLY! function addFakeObserver($obsName) { return false; // this function does not work currently so DO NOT USE! if ($this->init !== MPQ_PARSE_OK || $this->getFileSize("replay.initData") == 0) return false; $string = $this->readFile("replay.initData"); $numByte = 0; $numPlayers = MPQFile::readByte($string,$numByte); $playerAdded = false; $playerId = 0; for ($i = 1;$i <= $numPlayers;$i++) { $nickLen = MPQFile::readByte($string,$numByte); if ($nickLen > 0) { $numByte += $nickLen; $numByte += 5; } elseif (!$playerAdded) { // first empty slot $playerAdded = true; $numByte--; if ($i == $numPlayers) $len = 5; else $len = 6; // add the player to the initdata file $obsNameLength = strlen($obsName); $repString = chr($obsNameLength) . $obsName . str_repeat(chr(0),5); $newData = substr($string,0,$numByte) . $repString . substr($string,$numByte - $len + strlen($repString)); $numByte += strlen($repString); //$this->replaceFile("replay.initData", $newData); $playerId = $i; $string = $newData; // skip the next null part because it is 1 byte shorter than normal if ($i < $numPlayers) { $i++; $numByte += 4; } } else { $numByte += 5; } } if ($this->debug) $this->debug(sprintf("Got past first player loop, counter = $i, numbyte: %04X",$numByte)); if ($playerId == 0) return false; $numByte += 25; $accountIdentifierLength = MPQFile::readByte($string,$numByte); if ($accountIdentifierLength > 0) $accountIdentifier = MPQFile::readBytes($string,$numByte,$accountIdentifierLength); $numByte += 684; // length seems to be fixed, data seems to vary at least based on number of players while (true) { $str = MPQFile::readBytes($string,$numByte,4); if ($str != 's2ma') { $numByte -= 4; break; } $numByte += 2; // 0x00 0x00 $realm = MPQFile::readBytes($string,$numByte,2); $this->realm = $realm; $numByte += 32; } // start of variable length data portion $numByte += 2; $numPlayers = MPQFile::readByte($string,$numByte); // need to increment numplayers by 1 $string = substr_replace($string, pack("c",$numPlayers + 1), $numByte - 1, 1); $numByte += 4; for ($i = 1;$i <= $numPlayers;$i++) { $firstByte = MPQFile::readByte($string,$numByte); $secondByte = MPQFile::readByte($string,$numByte); if ($this->debug) $this->debug(sprintf("Function addFakeObserver: numplayer: %d, first byte: %02X, second byte: %02X",$i,$firstByte,$secondByte)); switch ($firstByte) { case 0xca: switch ($secondByte) { case 0x20: // player $numByte += 20; break; case 0x28: // player $numByte += 24; break; case 0x04: case 0x02: // spectator case 0x00: // computer $numByte += 4; break; } break; case 0xc2: switch ($secondByte) { case 0x04: $tmp = MPQFile::readByte($string,$numByte); if ($tmp == 0x05) $numByte += 4; $numByte += 20; break; case 0x24: case 0x44: $numByte += 5; break; } break; default: if ($this->debug) $this->debug(sprintf("Function addFakeObserver: Unknown byte at byte offset %08X, got %02X",$numByte,$firstByte)); return false; } } // insert join game event and initial camera event for the newly created player $string = $this->readFile("replay.game.events"); $string = substr_replace($string, pack("c3",0,$playerId,0x0B),0,0); $tmpByte = $i * 3 + 5; $camerastring = pack("c2", 0, (0x60 | $playerId)) .MPQFile::readBytes($string,$tmpByte,11); $string = substr_replace($string, $camerastring,$tmpByte,0); $this->replaceFile("replay.game.events", $string); return $playerId; } private function parseKeyVal($string, &$numByte) { $one = unpack("C",substr($string,$numByte,1)); $one = $one[1]; $retVal = $one & 0x7F; $shift = 1; $numByte++; while (($one & 0x80) > 0) { $one = unpack("C",substr($string,$numByte,1)); $one = $one[1]; $retVal = (($one & 0x7F) << $shift*7) | $retVal; $shift++; $numByte++; } return $retVal; } function deflate_decompress($string) { if (function_exists("gzinflate")){ $tmp = gzinflate(substr($string,2,strlen($string) - 2)); return $tmp; } if ($this->debug) $this->debug("Function 'gzinflate' does not exist, is gzlib installed as a module?"); return false; } function bzip2_decompress($string) { if (function_exists("bzdecompress")){ $tmp = bzdecompress($string); if (is_numeric($tmp) && $this->debug) { $this->debug(sprintf("Bzip2 returned error code: %d",$tmp)); } return $tmp; } if ($this->debug) $this->debug("Function 'bzdecompress' does not exist, is bzip2 installed as a module?"); return false; } static function initCryptTable() { if (!self::$cryptTable) self::$cryptTable = array(); $seed = 0x00100001; $index1 = 0; $index2 = 0; for ($index1 = 0; $index1 < 0x100; $index1++) { for ($index2 = $index1, $i = 0; $i < 5; $i++, $index2 += 0x100) { $seed = (uPlus($seed * 125,3)) % 0x2AAAAB; $temp1 = ($seed & 0xFFFF) << 0x10; $seed = (uPlus($seed * 125,3)) % 0x2AAAAB; $temp2 = ($seed & 0xFFFF); self::$cryptTable[$index2] = ($temp1 | $temp2); } } } static function hashStuff($string, $hashType) { $seed1 = 0x7FED7FED; $seed2 = ((0xEEEE << 16) | 0xEEEE); $strLen = strlen($string); for ($i = 0;$i < $strLen;$i++) { $next = ord(strtoupper(substr($string, $i, 1))); $seed1 = self::$cryptTable[($hashType << 8) + $next] ^ (uPlus($seed1,$seed2)); $seed2 = uPlus(uPlus(uPlus(uPlus($next,$seed1),$seed2),$seed2 << 5),3); } return $seed1; } static function decryptStuff($data, $key) { $seed = ((0xEEEE << 16) | 0xEEEE); $datalen = count($data); for($i = 0;$i < $datalen;$i++) { $seed = uPlus($seed,self::$cryptTable[0x400 + ($key & 0xFF)]); $ch = $data[$i] ^ (uPlus($key,$seed)); $key = (uPlus(((~$key) << 0x15), 0x11111111)) | (rShift($key,0x0B)); $seed = uPlus(uPlus(uPlus($ch,$seed),($seed << 5)),3); $data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF); } return $data; } static function encryptStuff($data, $key) { $seed = ((0xEEEE << 16) | 0xEEEE); $datalen = count($data); for($i = 0;$i < $datalen;$i++) { $seed = uPlus($seed,self::$cryptTable[0x400 + ($key & 0xFF)]); $ch = $data[$i] ^ (uPlus($key,$seed)); $key = (uPlus(((~$key) << 0x15), 0x11111111)) | (rShift($key,0x0B)); $seed = uPlus(uPlus(uPlus($data[$i],$seed),($seed << 5)),3); $data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF); } return $data; }}function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec);}// function that adds up two integers without allowing them to overflow to floatsfunction uPlus($o1, $o2) { $o1h = ($o1 >> 16) & 0xFFFF; $o1l = $o1 & 0xFFFF; $o2h = ($o2 >> 16) & 0xFFFF; $o2l = $o2 & 0xFFFF; $ol = $o1l + $o2l; $oh = $o1h + $o2h; if ($ol > 0xFFFF) { $oh += (($ol >> 16) & 0xFFFF); } return ((($oh << 16) & (0xFFFF << 16)) | ($ol & 0xFFFF));}// right shift without preserving the sign(leftmost) bitfunction rShift($num,$bits) { return (($num >> 1) & 0x7FFFFFFF) >> ($bits - 1);}?>

Link to comment
Share on other sites

SC2REPLAY.PHP (NEW)<?php/* 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/>.*/class SC2Replay { public static $gameSpeeds = array(0 => "Slower", 1=> "Slow", 2=> "Normal", 3=> "Fast", 4=> "Faster"); public static $difficultyLevels = array(0 => "Very easy", 1=> "Easy", 2=> "Medium", 3=> "Hard", 4=> "Very Hard", 5 => "Insane"); public static $gameSpeedCE = array(0 => 39, 1=> 44, 2=> 60, 3=> 64, 4=> 64); // estimates, weird values public static $colorIndices = array(1 => "Red", 2=> "Blue", 3=> "Teal", 4=> "Purple", 5=> "Yellow", 6 => "Orange", 7=> "Green", 8=> "Pink"); private $players; //array, indices: color, team, sname, lname, race, startRace, handicap, ptype private $gameLength; // game length in seconds private $mapName; private $gameSpeed; // game speed, number from 0-4. see $gameSpeeds array above private $teamSize; // team size in the format xvx, eg. 1v1 private $gamePublic; private $realm; private $version; private $build; private $events; // contains an array of the events in replay.game.events file private $debug; // debug, currently true or false private $debugNewline; // contents are appended to the end of all debug messages private $messages; // contains an array of the chat log messages private $winnerKnown; private $unitsDict; function __construct() { $this->players = array(); $this->gameLength = 0; $this->mapName = NULL; $this->gameSpeed = 0; $this->teamSize = NULL; $this->debug = false; $this->debugNewline = "<br />\n"; $this->winnerKnown = false; $this->unitsDict = array(); } // parameter needs to be an instance of MPQFile function parseReplay($mpqfile) { if (!($mpqfile instanceof MPQFile) || !$mpqfile->isParsed()) return false; // include utility class if it is available and not loaded already if (!class_exists('SC2ReplayUtils') && (file_exists('sc2replayutils.php'))) { include 'sc2replayutils.php'; } $this->gameLength = $mpqfile->getGameLength(); $this->version = $mpqfile->getVersion(); $this->build = $mpqfile->getBuild(); // then parse replay.details file $file = $mpqfile->readFile("replay.details"); $start = microtime_float(); if ($file !== false) { $this->parseDetailsFile($file); } else if ($this->debug) $this->debug("Error reading the replay.details file"); if ($this->debug) $this->debug(sprintf("Parsed replay.details file in %d ms.",(microtime_float() - $start)*1000)); $file = $mpqfile->readFile("replay.initData"); $start = microtime_float(); if ($file !== false) { $this->parseInitDataFile($file); } else if ($this->debug) $this->debug("Error reading the replay.initData file"); if ($this->debug) $this->debug(sprintf("Parsed replay.initData file in %d ms.",(microtime_float() - $start)*1000)); $file = $mpqfile->readFile("replay.attributes.events"); $start = microtime_float(); if ($file !== false) { $this->parseAttributesFile($file); } else if ($this->debug) $this->debug("Error reading the replay.attributes.events file"); if ($this->debug) $this->debug(sprintf("Parsed replay.attributes.events file in %d ms.",(microtime_float() - $start)*1000)); $num = 0; $file = $mpqfile->readFile("replay.game.events"); $start = microtime_float(); if ($file !== false) $num = $this->parseGameEventsFile($file); else if ($this->debug) $this->debug("Error reading the replay.game.events file"); if ($this->debug) $this->debug(sprintf("Parsed replay.game.events file in %d ms, found $num events.",(microtime_float() - $start)*1000)); $file = $mpqfile->readFile("replay.message.events"); $start = microtime_float(); if ($file !== false) $this->parseChatLog($file); else if ($this->debug) $this->debug("Error reading the replay.message.events file"); if ($this->debug) $this->debug(sprintf("Parsed replay.message.events file in %d ms.",(microtime_float() - $start)*1000)); } private function debug($message) { echo $message.($this->debugNewline); } function setDebugNewline($str) { $this->debugNewline = $str; } function setDebug($num) { $this->debug = $num; } function isWinnerKnown() { return $this->winnerKnown; } function getPlayers() { return $this->players; } function getMapName() { return $this->mapName; } function getGameSpeed() { return $this->gameSpeed; } function getGameSpeedText() { return self::$gameSpeeds[$this->gameSpeed]; } function getTeamSize() { return $this->teamSize; } function getVersion() { return $this->version; } function getBuild() { return $this->build; } function getMessages() { return $this->messages; } function getRealm() { return $this->realm; } // getFormattedGameLength returns the time in h hrs, m mins, s secs function getFormattedGameLength() { return $this->getFormattedSecs($this->gameLength); } function getFormattedSecs($secs) { $o = ""; $hrs = floor($secs / 3600); $mins = floor($secs / 60) % 60; $secs = $secs % 60; if ($hrs > 0) $o = "$hrs hrs, "; if ($mins > 0) $o .= "$mins mins, "; $o .= "$secs secs"; return $o; } function getUnits() { return $this->unitsDict; } function getEvents() { return $this->events; } function getGameLength() { return $this->gameLength; } // parse replay.initData file function parseInitDataFile($string) { $numByte = 0; $numPlayers = MPQFile::readByte($string,$numByte); $nullName = false; $playerNames = array(); foreach ($this->players as $player) $playerNames[] = $player['name']; $tmpArray = array(); for ($i = 1;$i <= $numPlayers;$i++) { $nickLen = MPQFile::readByte($string,$numByte); if ($nickLen > 0) { $name = MPQFile::readBytes($string,$numByte,$nickLen); $tmpArray[$i] = array( "name" => $name, "isObs" => TRUE, "id" => $i, "isComp" => FALSE, "team" => 0 ); // set initial values $numByte += 5; } else { if (!$nullName) { $nullName = true; $numByte--; } $numByte += 5; } } $numByte += 6; // skip 6 bytes, fixed length, unknown what it means $numByte += 4; // skip literal string 'Dflt' $numByte += 15; // skip unknown 15 bytes $accountIdentifierLength = MPQFile::readByte($string,$numByte); // present at least in My Documents\Starcraft 2\Accounts\<value> if ($accountIdentifierLength > 0) $accountIdentifier = MPQFile::readBytes($string,$numByte,$accountIdentifierLength); $numByte += 684; // length seems to be fixed, data seems to vary at least based on number of players while (true) { $str = MPQFile::readBytes($string,$numByte,4); if ($str != 's2ma') { $numByte -= 4; break; } $numByte += 2; // 0x00 0x00 $realm = MPQFile::readBytes($string,$numByte,2); $this->realm = $realm; $depHash = unpack("H*", MPQFile::readBytes($string,$numByte,32)); $depHash = $depHash[1]; $str = "Unknown"; if ((class_exists("SC2ReplayUtils") || (include 'sc2replayutils.php')) && isset(SC2ReplayUtils::$depHashes[$depHash])) { $str = SC2ReplayUtils::$depHashes[$depHash]['name']; if (SC2ReplayUtils::$depHashes[$depHash]['type'] == SC2_DEPMAP) $this->mapName = $str; } if ($this->debug) $this->debug(sprintf("Got debug hash $depHash (%s)",$str)); } // start of variable length data portion $numByte += 2; $numPlayers = MPQFile::readByte($string,$numByte); $i = $numPlayers; foreach ($tmpArray as $player) { if (in_array($player['name'],$playerNames)) continue; $player['id'] = $i; $this->players[$i] = $player; $i--; } $numByte += 4; // player-specific data starts } // parse replay.details file and add parsed stuff to the object // $string contains the contents of the file function parseDetailsFile($string) { if ($this->debug) $this->debug("Parsing replay.details file..."); $numByte = 0; $numByte += 6; $numPlayers = MPQFile::readByte($string,$numByte) / 2; for ($i = 1; $i <= $numPlayers;$i++) { $p = $this->parsePlayerStruct($string,$numByte,$i); } $mapnameLen = MPQFile::readByte($string,$numByte) / 2; $mapName = MPQFile::readBytes($string,$numByte,$mapnameLen); $this->mapName = $mapName; $numByte += 2; // 04 02 $u1Len = MPQFile::readByte($string,$numByte) / 2; if ($u1Len > 0) MPQFile::readByte($string,$numByte,$u1Len); $numByte += 5; // 06 05 02 00 02 $minimapnameLen = MPQFile::readByte($string,$numByte) / 2; $minimapName = MPQFile::readBytes($string,$numByte,$minimapnameLen); } // parse a player struct in the replay.details file private function parsePlayerStruct($string,&$numByte,$id) { $numByte += 4; $sNameLen = MPQFile::readByte($string,$numByte) / 2; if ($sNameLen > 0) $sName = MPQFile::readBytes($string,$numByte,$sNameLen); else $sName = NULL; $numByte += 5; // 02 05 08 00 09 $numByte += 4; // 00/04 02 07 00 $numByte += 3; // 00 00 00 // 00 53 32 (S2) $hadKey = true; $keys = array(); while ($hadKey) { $hadKey = false; $key = unpack("c2",MPQFile::readBytes($string,$numByte,2)); if ($key[2] == 9) { $hadKey = true; $keys[$key[1]] = $this->parseKeyVal($string,$numByte); } else if ($key[1] == 4 && $key[2] == 2) { break; } } if ($this->debug) { foreach ($keys as $k => $v) $this->debug("Got pre-race($sName) key: $k, value: $v"); } $raceLen = MPQFile::readByte($string,$numByte) / 2; if ($raceLen > 0) $race = MPQFile::readBytes($string,$numByte,$raceLen); else $race = NULL; $numByte += 3; // 06 05 08 $hadKey = true; while ($hadKey) { $keyVal = ""; $hadKey = false; $key = unpack("c2",MPQFile::readBytes($string,$numByte,2)); if ($key[2] == 9) { $hadKey = true; $keyVal = $this->parseKeyVal($string,$numByte); if ($key[1] == 2) { $cR = $keyVal / 2; } // red color if ($key[1] == 4) { $cG = $keyVal / 2; } // green color if ($key[1] == 6) { $cB = $keyVal / 2; } // blue color if ($key[1] == 16) { $party = $keyVal / 2; } // party number? if ($this->debug) $this->debug(sprintf("%s Key: %d, value: %d",$sName,$key[1], $keyVal)); } else if ($key[1] == 5 && $key[2] == 18) {$numByte -= 2; break; } // next player else if ($key[1] == 2 && $key[2] == 2) { break; } // end of player section } if (($sName === NULL)) { if ($this->debug) $this->debug("Got null player"); return; } // $this->players[$id]["sName"] = $sName; // deprecated array value before there was only a short name $this->players[$id]["name"] = $sName; // player name $this->players[$id]["lrace"] = $race; // locale-specific player race $this->players[$id]["race"] = ""; // player race in english, populated by checking which workers they build $this->players[$id]["id"] = $id; $this->players[$id]["party"] = $party; $this->players[$id]["team"] = 0; $this->players[$id]["color"] = sprintf("%02X%02X%02X",$cR,$cG,$cB); $this->players[$id]["apmtotal"] = 0; $this->players[$id]["apm"] = array(); $this->players[$id]["firstevents"] = array(); $this->players[$id]["numevents"] = array(); $this->players[$id]["ptype"] = ""; $this->players[$id]["handicap"] = 0; $this->players[$id]["isComp"] = false; $this->players[$id]["uid"] = $keys[8] / 2; $this->players[$id]["isObs"] = false; // all players present in replay.details file are not observers if ($this->debug) $this->debug(sprintf("Got player: %s, Race: %s, Party: %s, Color: %s",$sName, $race, $party, $this->players[$id]["color"])); return; } // parameter is the contents of the replay.attributes.events file private function parseAttributesFile($string) { if ($this->debug) $this->debug("Parsing replay.attributes.events file"); $numByte = 4; // skip the 4-byte header $numAttribs = MPQFile::readUInt32($string,$numByte); $attribArray = array(); 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($:); } $numByte += 4; $attribArray[$playerId][$attributeId] = $attribVal; if ($this->debug) $this->debug(sprintf("Got attrib \"%04X\" for player %d (%s), attribVal = \"%s\"", $attributeId,$playerId,(($playerId == 0x10)?"ALL":$this->players[$playerId]["name"]),$attribVal)); switch ($attributeId) { // FFA/* case 0x07D6: break; // 4v4 case 0x07D5: break; // 3v3 case 0x07D4: break; // 2v2 case 0x07D3: // my hypothesis is that every 0x07D<X> value is what the teams would // be if the game type/team size was changed. // for example if you changed the dropdownbox from 3v3 to FFA, the values // under 0x07D6 would be the initial values that you could edit. // why this is included in replay files is weird to say the least break; // 1v1 case 0x07D2: break;*/ case 0x0BBB: // handicap $this->players[$playerId]["handicap"] = $attribVal; break; case 0x0BBC: // difficulty level (of computer player, Medi for humans) switch ($attribVal) { case "Insa": $tmp = 5; break; case "VyHd": $tmp = 4; break; case "Hard": $tmp = 3; break; case "Medi": $tmp = 2; break; case "Easy": $tmp = 1; break; case "VyEy": $tmp = 0; break; default: $tmp = 2; } $this->players[$playerId]["difficulty"] = $tmp; break; case 0x0BB8: // game speed switch ($attribVal) { case "Fasr": $tmp = 4; break; case "Fast": $tmp = 3; break; case "Norm": $tmp = 2; break; case "Slow": $tmp = 1; break; case "Slor": $tmp = 0; break; default: $tmp = 2; } $this->gameSpeed = $tmp; break; case 0x01F4: // player type, Humn or Comp $this->players[$playerId]["ptype"] = $attribVal; // deprecated $this->players[$playerId]["isComp"] = ($attribVal == 'Comp')?true:false; break; case 0x0BB9: // initial race, Prot Terr Zerg or RAND $this->players[$playerId]["srace"] = $attribVal; break; case 0x07D1: // teamsizes $this->teamSize = $attribVal; break; case 0x0BC1: // game type, private(Priv)/open(Amm)? $this->gamePublic = (($attribVal == "Priv")?false:true); break; case 0x0BBA: // color index $this->players[$playerId]["colorIndex"] = intval(substr($attribVal,2)); $this->players[$playerId]["sColor"] = self::$colorIndices[intval(substr($attribVal,2))]; break; default: } } switch ($attribArray[0x10][0x07D1]) { case "1v1": $attrib = 0x07D2; break; case "2v2": $attrib = 0x07D3; break; case "3v3": $attrib = 0x07D4; break; case "4v4": $attrib = 0x07D5; break; case "FFA": $attrib = 0x07D6; break; default: if ($this->debug) $this->debug(sprintf("Unknown game mode in replay.attributes.events: %s",$attribArray[0x10][0x07D1])); return; } foreach ($attribArray as $playerId => $values) { if ($playerId == 0x10) continue; $this->players[$playerId]["team"] = intval(substr($values[$attrib],1)); } } // parse a key/value -pair struct in the replay.details file private function parseKeyVal($string, &$numByte) { $one = unpack("C",substr($string,$numByte,1)); $one = $one[1]; $retVal = $one & 0x7F; $shift = 1; $numByte++; while (($one & 0x80) > 0) { $one = unpack("C",substr($string,$numByte,1)); $one = $one[1]; $retVal = (($one & 0x7F) << $shift*7) | $retVal; $shift++; $numByte++; } return $retVal; } private function readUnitTypeID($string,&$numByte) { return ((MPQFile::readByte($string,$numByte) << 16) | (MPQFile::readByte($string,$numByte) << 8) | (MPQFile::readByte($string,$numByte))); } private function readUnitAbility($string) { $bytes = unpack("C3",substr($string,4,3)); return (($bytes[1] << 16) | ($bytes[2] << 8) | ($bytes[3])); } // gets players who actually played in the game, meaning excludes observers and party members. public function getActualPlayers() { $tmp = array(); foreach ($this->players as $val) if ($val['team'] > 0) $tmp[] = $val; return $tmp; } // parameter is the contents of the replay.message.events -file private function parseChatLog($string) { $numByte = 0; $len = strlen($string); $messages = array(); $totTime = 0; while ($numByte < $len) { $timestamp = self::parseTimeStamp($string,$numByte); $playerId = MPQFile::readByte($string,$numByte); $opcode = MPQFile::readByte($string,$numByte); $totTime += $timestamp; if ($opcode == 0x80) // header weird thingy? $numByte += 4; else if (($opcode & 0x80) == 0) { // message $messageTarget = $opcode & 3; $messageLength = MPQFile::readByte($string,$numByte); if (($opcode & 8) == 8) $messageLength += 64; if (($opcode & 16) == 16) $messageLength += 128; $message = MPQFile::readBytes($string,$numByte,$messageLength); $messages[] = array('id' => $playerId, 'name' => $this->players[$playerId]['name'], 'target' => $messageTarget, 'time' => floor($totTime / 16), 'message' => $message); } else if ($opcode == 0x83) { // ping on map? 8 bytes? $numByte += 8; } } $this->messages = $messages; } // parameter is the contents of the replay.game.events -file private function parseGameEventsFile($string) { $numByte = 0; $len = strlen($string); $playerLeft = array(); $events = array(); $time = 0; $numEvents = 0; while ($numByte < $len) { $timeStamp = self::parseTimeStamp($string,$numByte); $nextByte = MPQFile::readByte($string,$numByte); $eventType = $nextByte >> 5; // 3 lowest bits $globalEventFlag = $nextByte & 16; // 4th bit $playerId = $nextByte & 15; // bits 5-8 if (isset($this->players[$playerId])) $playerName = $this->players[$playerId]['name']; else $playerName = ""; $eventCode = MPQFile::readByte($string,$numByte); $time += $timeStamp; $numEvents++; // weird timestamp values mean that there's likely a problem with the alignment of the parse(too few/too many bytes read for an eventcode) if ($this->debug >= 2) {// if ($len - $numByte > 24) {// $bytes = unpack("C24",substr($string,$numByte,24));// $dataBytes = "";// for ($i = 1;$i <= 24;$i++) $dataBytes .= sprintf("%02X",$bytes[$i]); $this->debug(sprintf("DEBUG L2: Timestamp: %d, Frames: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", floor($time / 16),$timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte));// } } switch ($eventType) { case 0x00: // initialization switch ($eventCode) { case 0x1B: // Player enters game case 0x0B: break; case 0x05: // game starts break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x01: // action switch ($eventCode) { case 0x09: // player quits the game if ($this->players[$playerId]['team'] > 0) // don't log observers/party members etc $playerLeft[] = $playerId; break; case 0x0B: // player uses an ability // at least 32 bytes $data = MPQFile::readBytes($string,$numByte,32); $reqTarget = unpack("C",substr($data,7,1)); $reqTarget = $reqTarget[1]; $ability = $this->readUnitAbility($data); if ($ability != 0xFFFF0F) { $events[] = array('p' => $playerId, 't' => $time, 'a' => $ability); $this->events = $events; // populate non-locale-specific race strings based on worker type if (!$this->players[$playerId]['isObs'] && $this->players[$playerId]['race'] == "") { switch ($ability) { case 0x080A00: //SCV $this->players[$playerId]['race'] = "Terran"; break; case 0x090E00: //probe $this->players[$playerId]['race'] = "Protoss"; break; case 0x0B0000: //drone $this->players[$playerId]['race'] = "Zerg"; break; } } } // at least with attack, move, right-click, if the byte after unit ability bytes is // 0x30 or 0x50, the struct takes 1 extra byte. With build orders the struct seems to be 32 bytes // and this byte is 0x00. // might also be in some other way variable-length. if ($reqTarget == 0x30) $data .= MPQFile::readByte($string,$numByte); if ($reqTarget == 0x50) $data .= MPQFile::readByte($string,$numByte); // update apm array $this->addPlayerAction($playerId, floor($time / 16)); $this->addPlayerAbility($playerId, ceil($time /16), $ability); break; case 0x2F: // player sends resources $numByte += 17; // data is 17 bytes long break; case 0x0C: // automatic update of hotkey? case 0x1C: case 0x2C: case 0x3C: // 01 01 01 01 11 01 03 02 02 38 00 01 02 3c 00 01 00 case 0x4C: // 01 02 02 01 0d 00 02 01 01 a8 00 00 01 case 0x5C: // 01 01 01 01 16 03 01 01 03 18 00 01 00 case 0x6C: // 01 04 08 01 03 00 02 01 01 34 c0 00 01 case 0x7C: // 01 05 10 01 01 10 02 01 01 1a a0 00 01 case 0x8C: case 0x9C: case 0xAC: // player changes selection $selFlags = MPQFile::readByte($string,$numByte); $dsuCount = MPQFile::readByte($string,$numByte); if($this->debug){ $this->debug("Selection Change"); $this->debug(sprintf("Player %s", $playerId)); $this->debug(sprintf("Time %d", $time)); $this->debug(sprintf("Deselected Count: %d", $dsuCount)); } $dsuExtraBits = $dsuCount % 8; $uType = array(); if ($dsuCount > 0) $dsuMap = MPQFile::readBytes($string,$numByte,floor($dsuCount / 8)); if ($dsuExtraBits != 0) { // not byte-aligned $dsuMapLastByte = MPQFile::readByte($string,$numByte); $nByte = MPQFile::readByte($string,$numByte); //Recalculating these is excessive. //ex: For extra = 2 $offsetTailMask = (0xFF >> (8-$dsuExtraBits)); //ex: 00000011 $offsetHeadMask = (~$offsetTailMask) & 0xFF; //ex: 11111100 $offsetWTailMask = 0xFF >> $dsuExtraBits; //ex: 00111111 $offsetWHeadMask = (~$offsetWTailMask) & 0xFF; //ex: 11000000 $uTypesCount = ($dsuMapLastByte & $offsetHeadMask) | ($nByte & $offsetTailMask); if($this->debug){ $this->debug(sprintf("Number of New Unit Types %d", $uTypesCount)); } for ($i = 1;$i <= $uTypesCount;$i++) { $nBytes = unpack("C3",MPQFile::readBytes($string,$numByte,3)); $byte1 = ( $nByte & $offsetHeadMask) | (($nBytes[1] & $offsetWHeadMask) >> (8 - $dsuExtraBits)); $byte2 = (($nBytes[1] & $offsetWTailMask) << $dsuExtraBits) | ( $nBytes[2] & $offsetTailMask); $byte3 = (($nBytes[2] & $offsetHeadMask) << $dsuExtraBits) | ( $nBytes[3] & $offsetTailMask); //Byte3 is almost invariably 0x01 $uType[$i]['id'] = (($byte1 << 16) | ($byte2 << 8) | $byte3) & 0xFFFFFF; $nByte = MPQFile::readByte($string,$numByte); $uType[$i]['count'] = ($nBytes[3] & $offsetHeadMask) | ($nByte & $offsetTailMask); if($this->debug){ $this->debug(sprintf(" %d x 0x%06X", $uType[$i]['count'], $uType[$i]['id'])); } } $lByte = MPQFile::readByte($string,$numByte); $totalUnits = ($nByte & $offsetHeadMask) | ($lByte & $offsetTailMask); if($this->debug){ $this->debug(sprintf("TOTAL: %d", $totalUnits)); } //Populate the unitsDict foreach($uType as $unitType){ for($i = 1; $i <= $unitType['count']; $i++){ $nBytes = unpack("C4", MPQFile::readBytes($string, $numByte,4)); $byte1 = ($lByte & $offsetHeadMask) | (($nBytes[1] & $offsetWHeadMask) >> (8 - $dsuExtraBits)); $byte2 = (($nBytes[1] & $offsetWTailMask) << $dsuExtraBits) | (($nBytes[2] & $offsetWHeadMask) >> (8 - $dsuExtraBits)); $byte3 = (($nBytes[2] & $offsetWTailMask) << $dsuExtraBits) | (($nBytes[3] & $offsetWHeadMask) >> (8 - $dsuExtraBits)); $byte4 = (($nBytes[3] & $offsetWTailMask) << $dsuExtraBits) | ( $nBytes[4] & $offsetTailMask); $uid = ($byte1 << 8) | $byte2; //Bytes 3 + 4 contain Flag Info $this->addSelectedUnit($uid, $unitType['id'], $playerId, floor($time / 16)); if($this->debug){ $this->debug(sprintf(" 0x%06X -> 0x%02X", $unitType['id'], $uid)); } $lByte = $nBytes[4]; //For looping. } } } else { // byte-aligned $uTypesCount = MPQFile::readByte($string,$numByte); if($this->debug){ $this->debug(sprintf("Number of New Unit Types %d", $uTypesCount)); } for ($i = 1;$i <= $uTypesCount;$i++) { $uType[$i]['id'] = $this->readUnitTypeID($string,$numByte); $uType[$i]['count'] = MPQFile::readByte($string,$numByte); if($this->debug){ $this->debug(sprintf(" %d x 0x%06X", $uType[$i]['count'], $uType[$i]['id'])); } } $totalUnits = MPQFile::readByte($string,$numByte); if($this->debug){ $this->debug(sprintf("TOTAL: %d", $totalUnits)); } //Populate the Units Dict foreach($uType as $unitType){ for($i = 1; $i <= $unitType['count']; $i++){ $nBytes = unpack("C4", MPQFile::readBytes($string, $numByte, 4)); $uid = ($nBytes[1] << 8) | $nBytes[2]; $this->addSelectedUnit($uid, $unitType['id'], $playerId, floor($time / 16)); if($this->debug){ $this->debug(sprintf(" 0x%06X -> 0x%02X", $unitType['id'], $uid)); } } } } //update apm fields if ($eventCode == 0xAC) { $this->addPlayerAction($playerId, floor($time / 16)); } break; case 0x0D: // manually uses hotkey case 0x1D: case 0x2D: case 0x3D: case 0x4D: case 0x5D: case 0x6D: case 0x7D: case 0x8D: case 0x9D: $byte1 = MPQFile::readByte($string,$numByte); if ($numByte < $len) { $byte2 = MPQFile::readByte($string,$numByte); $numByte--; } $extraBytes = floor($byte1 / 8); $numByte += $extraBytes; if ($byte1 & 4 && ($this->debug)) $this->debug("Found candidate hotkey event!"); if (($byte1 & 4) && (($byte2 & 6) == 6)) $numByte += 2; else if ($byte1 & 4) $numByte += 1; // update apm $this->addPlayerAction($playerId, floor($time / 16)); break; case 0x1F: // send resources case 0x2F: case 0x3F: case 0x4F: case 0x5F: case 0x6F: case 0x7F: case 0x8F: $numByte++; // 0x84 $sender = $playerId; $receiver = ($eventCode & 0xF0) >> 4; // sent minerals $bytes = MPQFile::readBytes($string,$numByte,4); $mBytes = unpack("C4",$bytes); $mineralValue = ((($mBytes[1] << 20) | ($mBytes[2] << 12) | ($mBytes[3] << 4)) >> 1) + ($mBytes[4] & 0x0F); // sent gas $bytes = MPQFile::readBytes($string,$numByte,4); $mBytes = unpack("C4",$bytes); $gasValue = ((($mBytes[1] << 20) | ($mBytes[2] << 12) | ($mBytes[3] << 4)) >> 1) + ($mBytes[4] & 0x0F); // last 8 bytes are unknown $numByte += 8; break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x02: // weird switch($eventCode) { case 0x06: $numByte += 8; // 00 00 00 04 00 00 00 04 break; case 0x07: $numByte += 4; break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x03: // replay switch ($eventCode) { case 0x87: $numByte += 8; break; case 0x01: // camera movement case 0x11: case 0x21: case 0x31: case 0x41: case 0x51: case 0x61: case 0x71: case 0x81: case 0x91: case 0xA1: case 0xB1: case 0xC1: case 0xD1: case 0xE1: case 0xF1: $numByte += 3; $nByte = MPQFile::readByte($string,$numByte); switch (($nByte & 0x70)) { case 0x10: // zoom camera up or down case 0x30: // only 0x10 matters, but due to 0x70 mask in comparison, check for this too case 0x50: $numByte++; $nByte = MPQFile::readByte($string,$numByte); case 0x20: if (($nByte & 0x20) > 0) { // zooming, if comparison is 0 max/min zoom reached $numByte++; $nByte = MPQFile::readByte($string,$numByte); } if (($nByte & 0x40) == 0) break; // if non-zero (as in 0x40), rotate segment(2 bytes) follows case 0x40: // rotate camera $numByte += 2; } break; default: if ($this->debug) $this->debug(sprintf("DEBUG: Timestamp: %d, Type: %d, Global: %d, Player ID: %d (%s), Event code: %02X Byte: %08X<br />\n", $timeStamp, $eventType, $globalEventFlag,$playerId,$playerName,$eventCode,$numByte)); } break; case 0x04: // inaction switch($eventCode) { case 0x

Link to comment
Share on other sites

It's really difficult to read that code, you should put it in a code box. It's not really necessary though, you need to debug the upload processing script. Use either echo statements or logging to print out what the script is doing so that you can follow it. Print things like whether or not it's going in an if statement, what the current values of variables are, etc. I see two redirects in the processing file, but only one that goes back to the form.If you want to use an error log to record everything, which will be better if you're redirecting, add this to the top of the script:ini_set('log_errors', 1);ini_set('error_log', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'error.log');ini_set('html_errors', 0);ini_set('display_errors', 0);error_reporting(E_ALL);After that, you can use the error_log function to send messages to a file called error.log in the same directory as the script, e.g.:error_log('on line number ' . __LINE__);

Link to comment
Share on other sites

This one?

<?phprequire 'upload_functions.php';require 'upload_file_settings.php';$MAX_FILE_SIZE = 1000000;if (isset($_FILES['userfile'])) {		$error = $_FILES['userfile']['error'];		$type = $_FILES['userfile']['type'];		$name = $_FILES['userfile']['name'];		$tmpname = $_FILES['userfile']['tmp_name'];		$size = $_FILES['userfile']['size'];		$err = false;		if ($size >= $MAX_FILE_SIZE) {				$errmsg = "Error: File is too large. Replay File must be below 1MB.<br />";				$err = true;		}		if ($error == UPLOAD_ERR_PARTIAL) {				$errmsg = "Error: The upload was not completed successfully. Please try again.<br />";				$err = true;		}		if ($error == UPLOAD_ERR_NO_FILE) {				$errmsg = "Error: Please select a Replay to upload.<br />";				$err = true;		}		if (!is_uploaded_file($tmpname)) {				$errmsg = "Error: Uploaded filename doesn't point to an uploaded file.<br />";				$err = true;		}		if (file_exists("./replay_files/" . $_FILES["userfile"]["name"]))		{			$newfilename = renameFile("./replay_files/", $_FILES["userfile"]["name"]);			if (!$newfilename) {			$errmsg = "Error: File already exists, unable to rename. Rename and try again.";			$err = true;			}			else {				$_FILES["userfile"]["name"] = $newfilename;				$name = $newfilename;			}		}		if ($err !== true) {				if (class_exists("MPQFile") || (include 'mpqfile.php')) {						$start = microtime_float();						if ($_POST['debug'] == 1) {								echo sprintf("<b>Debugging is on.</b><br />\n");						}						$a = new MPQFile($tmpname,true,(($_POST['debug'] == 1)?2:0));						$init = $a->getState();						if ($init == MPQ_ERR_NOTMPQFILE)								echo "Could not parse the Replay. Check to make sure it's a valid .SC2Replay File.<br />\n";						else if ($a->getVersion() < 9)								echo "Error: Invalid Version.<br />\n";						else {								// file is valid, save it								move_uploaded_file($_FILES["userfile"]["tmp_name"],"./replay_files/" . $_FILES["userfile"]["name"]);								// update database								//echo 'file uploaded';								$datetime = date('Y-m-d H:i:s');								$replay_query = "INSERT INTO `replays` (`id`, `description`, `file_name`, `datetime`) VALUES									(NULL, '".$_POST['textarea']."','".$_FILES["userfile"]["name"]."','".$datetime."')";								$insert = mysql_query($replay_query, $con) or die('could not insert replay file.');								$insert_id = mysql_insert_id();								if ($insert_id) {									$major_version = $a->getVersion();									$build = $a->getBuild();									$b = $a->parseReplay();									$map = $b->getMapName();									$game_length = $b->getFormattedGameLength();									$team_size = $b->getTeamSize();									$game_speed = $b->getGameSpeed();									//$game_speed = $b->getGameSpeedText();									// Read and save game data									$game_query = "INSERT INTO `games` (`id` , `replay_id`,											`major_version` ,`build` ,`map` ,`game_length` ,`team_size` ,											`game_speed`)											VALUES (NULL , '$insert_id', '$major_version', '$build', '$map', '$game_length', '$team_size', '$game_speed')";									$insert_game = mysql_query($game_query, $con) or die('Could not insert game record.');									//Read and save player data									$tmp = $b->getPlayers();									foreach($tmp as $value) {										$team = ($value['party'] > 0)?"Team ".$value['party']:"-";										$apm_avg = ($value['party'] > 0)?(round($value['apmtotal'] / ($b->getGameLength() / 60))):0;										$winner = (isset($value['won']))?$value['won']:(($value['party'] > 0)?"Unknown":"-");										$player_query = "INSERT INTO `players` (`id` ,`replay_id` ,													`name` ,`long_name` ,`race` ,`color` ,`team` ,`average_apm` ,													 `winner`) VALUES (NULL , '$insert_id', '".$value['sName']."', '".$value['lName']."', '".$value['race']."', '".$value['color']."', '".$team."', '$apm_avg', '$winner')";										$insert_player = mysql_query($player_query, $con) or die('could not insert player record');										if ($value['party'] > 0) {											$apmFileName = './apm_in/'.$value['id']."_".md5($name).".png";											$arAPMfilenames[] = $apmFileName;											createAPMImage($value['apm'],$b->getGameLength(),$apmFileName, $value['sName']);										}									}									mergeAPMFiles($arAPMfilenames, $name);								}								$messages = $b->getMessages();								if (count($messages) > 0) {										$data =  "<b>Messages:</b><br /><table border=\"1\"><tr><th>Time</th><th>Player</th><th>Target</th><th>Message</th></tr>\n";										foreach ($messages as $val)												$data .= sprintf("<tr><td>%d sec</td><td>%s</td><td>%s</td><td>%s</td></tr>\n",$val['time'],																		  $val['name'], ($val['target'] == 2)?"Alliance":"All",$val['message']);										$data .= "</table><br />\n";								}								$t = $b->getEvents();								if (isset($sc2_abilityCodes) || (include 'abilitycodes.php')) {										$data .= "<b>Production Chart:</b> <br /><table border=\"1\"><tr><th>Timecode</th>\n";										$pNum = count($tmp);										foreach ($tmp as $value) {										  if ($value['party'] > 0)												$data .= sprintf("<th>%s (%s)</th>",$value['sName'],$value['race']);										}										$data .= "</tr>\n";										foreach ($t as $value) {												$data .= sprintf("<tr><td>%d sec</td>",$value['t'] / 16);												foreach ($tmp as $value2) {														if ($value2['party'] == 0) continue;														if ($value['p'] == $value2['id'])																$data .= sprintf("<td>%s</td>",$b->getAbilityString($value['a']));														else																$data .= "<td></td>";												}												$data .= "</tr>\n";										}										$data .= "</table>";										$file=fopen("logs/".$name.".xls","w");										fputs($file,$data);										fclose($file);								}						}				}			 header('location: show_replay/show.php?id='.$insert_id);			 exit();		}		else {			mysql_close($con);			header('location: upload_file.php?error='.urlencode($errmsg));			exit();		}}mysql_close($con);?>

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...