<?php
# th7 replay file perser for cmd.
# 
# Original sources:
#   http://touhou.csie.org/replay/ptsrply.php?source
#   piaip''s Touhou Series Replay Management system
#   Replay File Parser Module
# 
# Modified by dl.
# 
# License under Creative Commons License
# http://creativecommons.org/licenses/by-nc-sa/2.0/
# 
# Thanks to Chikei Lee and Piaip.
#


##########
# main
##########
$tmpfn = "";
if($argc >= 2){
	$tmpfn = $argv[1];
}
else{
	echo "invalid arguments.\n";
	usage();
	exit(1);
}

$rpydata = readReplayFile($tmpfn);
$infblk = getReplayFileInfo($rpydata);

$keys = array(
	"INFO_PLAYER",
	"INFO_VERSION",
	"INFO_TIME",
	"INFO_LEVEL",
	"INFO_CHAR",
	"INFO_SCORE",
	"INFO_DROP");

$isFirst = true;
foreach($keys as $key){
	if(!$isFirst){
		echo "\t";
	}
	else{
		$isFirst = false;
	}
	echo $infblk[$key];
}
exit(0);



##########
# Functions
##########
function usage(){
	echo "usage:\n";
	echo "  [path_to_php] getReplayInfoTH7.php -q -f [th7_replay_file_name]\n";
	echo "  returns: print [player(tab)version(tab)date(tab)level(tab)char(tab)score(drop)] to sysout.";
}

function trimDrop($drop){
	$index = strpos($drop, ".", 0);
	if(!$index){
		return $drop;
	}
	
	$len = strlen($drop);
	$ret = $drop;
	if($index >= 0 && $index+3 <= $len){
		$ret = substr($drop, 0, $index+3);
	}
	return $ret;
}

function readReplayFile($fn) {
    return file_get_contents($fn);
}

function getReplayFileInfo($dat) {
    $infblk = array();
    # thanks to Chikei Lee 2004/9/10
    $infblk = parseReplayFileInfo07($dat);
    return $infblk;
}

function parseReplayFileInfo07($dat) {
    # format and decode method provided to Chikei Lee 2004/9/10
    # rewritten as php by piaip
    $infblk = array();
    $dat2 = substr($dat, 0, 0x10);
    $min_info = 0x100;
    # decrypt
    $mask = ord(substr($dat, 0x0d, 1));
    for ($i = 0x10; $i < strlen($dat); $i++) {
        $dat2 .= chr((ord($dat[$i]) - $mask) & 0xff);
        $mask = ($mask + 7) & 0xff;
    }
    if (strlen($dat) < 1 || $dat2[strlen($dat)-1] != 0) {
        return array();
    }
    #print "Decrypt complete($i).<br>";
    $dat = $dat2;
    $dat2 = '';
    # decompress
    # -- vars
    $v04 = 0; $v1c = 0; $v30 = 0; 
    $v0c = 0; $v24 = 0; $v10 = 0; 
    $v2c = 0; $v28 = 0; $v34 = 1; 
    $v11 = 0x80; $v20 = 0;
    # reinit v20
    for ($i = 0; $i < 4; $i++) 
        $v20 = $v20 * 0x100 + ord($dat[0x17 - $i]);
    $v3c = $v20;
    $v4b = array(2048);
    for ($i = 0; $i < 2048; $i++) $v4b[$i] = 0;

    # not compressed until 0x54
    $ibase = 0x54;
    $i  = $ibase;    // input
    $dat2 = substr($dat,0,$ibase);
    while(strlen($dat2) < $min_info){
        $flStopDoLoop = 0;

        while(strlen($dat2) < $min_info) {
            $flFirstRun = 1;
            do {
                if($v11==0x80) {
                    $v04=ord($dat[$i]);
                    if($i-$ibase < $v20)
                        $i ++;
                    else
                        $v04 = 0;
                    $v28 += $v04;
                } // $v11 == 0x80

                if($flFirstRun) {
                    $v1c = $v04 & $v11;
                    $v11 = $v11 >> 1;

                    if ($v11 == 0) $v11=0x80;
                    if ($v1c == 0) {
                        $flStopDoLoop = 1;
                        break;
                    }
                    $v30 = 0x80;
                    $v1c = 0;
                    $flFirstRun = 0;
                } else {
                    if (($v11 & $v04)!=0)
                    $v1c=$v1c | $v30;

                    $v30=$v30 >> 1;
                    $v11=$v11 >> 1;

                    if($v11==0) $v11=0x80;
                }
            } while($v30!=0);
            if($flStopDoLoop)
                break;

            $dat2 .= chr($v1c);

            $v4b[$v34]=chr($v1c & 0xff);
            $v34 = ($v34+1) & 0x1FFF;
        } # 2nd. while(1)
        if (strlen($dat2) > $min_info) break;
        #print "*strlen=" . strlen($dat2) . "<br>";

        $v30=0x1000;
        $v1c=0;

        while($v30!=0) {
            if($v11==0x80) {
                    $v04=ord($dat[$i]);
                    if($i-$ibase < $v20)
                        $i ++;
                    else
                        $v04 = 0;
                    $v28 += $v04;
            } # $v11 == 0x80

            if(($v11 & $v04)!=0) $v1c = $v1c | $v30;
            $v30 = $v30>>1;
            $v11 = $v11>>1;
            if($v11==0) $v11 = 0x80;
        }

        $v0c = $v1c;
        if($v0c==0) break;

        $v30=8;
        $v1c=0;
        while ($v30 != 0) {
            if ($v11 == 0x80) {
                $v04=ord($dat[$i]);
                if($i-$ibase < $v20)
                    $i ++;
                else
                    $v04 = 0;
                $v28 += $v04;
            } # $v11 == 0x80
            if(($v11 & $v04)!=0) $v1c=$v1c|$v30;
            $v30=$v30>>1;
            $v11=$v11>>1; 
            if($v11==0) $v11=0x80;
        } # $v30 != 0
        $v24 = $v1c+2;
        $v10=0;
        while($v10<=$v24 && strlen($dat2) < $min_info)
        {
            $v2c=$v4b[($v0c+$v10)&0x1FFF];
            $dat2 .= $v2c;
            $v4b[$v34]=chr(ord($v2c) & 0xff);
            $v34=($v34+1) & 0x1FFF;
            $v10++;
        } # $v10 <= $v24
    } # 1st. level while(1)
    #print 'Decompress complete.<br>';
    #print 'Size: ' . strlen($dat2);
    $date = substr($dat2, 0x58, 6);
    $name = substr($dat2, 0x5e, 9);
    $char = ord(substr($dat2, 0x56, 1));
    $rank = ord(substr($dat2, 0x57, 1));
    $myver= substr($dat2, 0xe0, 5); # '0100a' etc
    $myver = substr($myver, 0, 2) . '.' . substr($myver, 2);
    if ($myver[0] == '0') $myver = substr($myver, 1);

    #print "<Br/>name: $name<br/>date:$date<br/>";
    #$infblk['INFO_DECODED'] = strlen($dat2);#$name; #mb_convert_encoding($name, 'UTF-8', 'SJIS');
    $infblk['INFO_PLAYER'] = $name; #mb_convert_encoding($name, 'UTF-8', 'SJIS');
    $infblk['INFO_VERSION'] = $myver;
    $infblk['INFO_TIME'] = $date;
    $levels = array("Easy","Normal","Hard","Lunatic","Extra","Phantasm");
    $infblk['INFO_LEVEL'] = $levels[$rank];
    $chars = array("ReimuA", "ReimuB", "MarisaA", "MarisaB", "SakuyaA", "SakuyaB");
    $infblk['INFO_CHAR'] = $chars[$char];
    # floats
    $score = 0;
    for ($fi = 0; $fi < 4; $fi ++) {
        $score = $score * 0x0100 + ord($dat2[0x6c + 3 - $fi]);
    }
    $infblk['INFO_SCORE'] = $score * 10;
    $drop = 0;
    #for ($fi = 0; $fi < 4; $fi ++) {
    #    $drop = $drop * 0x0100 + ord($dat2[0xcc + 3 - $fi]);
    #}
    $drop = cvt2float4(substr($dat2, 0xcc, 4));
    $infblk['INFO_DROP'] = trimDrop($drop);
    #print "<Br/>name: $name<br/>date:$date<br/>";
    return $infblk;
}

function cvt2float4($fltstr)
{
   // Parse the string
   $float0 = hexdec(bin2hex(substr($fltstr,0,1)));
   $float1 = hexdec(bin2hex(substr($fltstr,1,1)));
   $float2 = hexdec(bin2hex(substr($fltstr,2,1)));
   $float3 = hexdec(bin2hex(substr($fltstr,3,1)));
   // Convert to an integer representation
   $float4 = $float0*pow(2,0) + $float1*pow(2,8) + $float2*pow(2,16) + $float3*pow(2,24);
   // Get the Sign
   $sign    = ($float4 & 0x80000000) >> 31;
   // Get the exponent
   $exp    = (($float4 & 0x7F800000) >> 23) - 127;
   // Get the Mantissa
   $mantissa = $float4 & 0x007FFFFF;
   // Calculate the float
   $sum = 0.0;
   for ($i=-23;$i<0;$i++)
   {
       $value = $mantissa & 1;
       $sum = $sum + $value*pow(2,$i)*pow(2,$exp);       
       $mantissa = $mantissa >> 1;
   }
       $sum = $sum + 1*pow(2,0)*pow(2,$exp);

   if ($sign == 1)
       $float4 = $sum*-1;   
   else
       $float4 = $sum;
   return $float4;
}


?>