<?php

$games = array(
    'td',
    'ra',
    'ts',
    'dta',
    'yr',
    'd2',
);

header('Content-type: text/plain');

$game = isset($_POST['game']) ? $_POST['game'] : false;

if (!in_array($game, $games)) {
    header('400 Bad Request');
    echo 'Game not supported';
    exit;
}

if (count($_FILES) == 0) {
    header('400 Bad Request');
    echo 'Zip file missing.';
    exit;
}

$upload = array_pop($_FILES);

if ($upload['error']) {
    header('500 Internal Server Error');
    echo 'Something went wrong on server side while processing upload. The uploaded file could have been too big.';
    exit;
}

if ($upload['size'] > 1024 * 1024) {
    header('400 Bad Request');
    echo 'Uploaded file over size limit.';
    exit;
}

if (!preg_match('/^([a-z0-9]+).zip$/i', $upload['name'], $m)) {
    header('400 Bad Request');
    echo 'Zip file name not a valid hex value.';
    exit;
}

$sha1 = strtolower($m[1]);
$zipName = $game . '/' . $sha1 . '.zip';

if (file_exists($zipName)) {
    echo 'Map already uploaded, but thanks anyway.<br>';
    //exit;
}

// Creates new variable of zip archive type and opens .zip file of uploader.
$zip = new ZipArchive();
$res = $zip->open($upload['tmp_name']);

if ($res !== true) {
    header('400 Bad Request');
    echo 'Uploaded file not a valid Zip.';
    exit;
}

if ($game == 'd2') {
	
	// Extraction of temporary files to check validity.	
	$zip->extractTo($game);		
	$tempName = $game. "/" . $sha1;	
	$map = "$tempName" . ".map";	
	$ini = "$tempName" . ".ini";
	$mis = $game. "/_" . $sha1 . ".mis";		
	$handle = fopen($map, "rb");
	
	$height = unpack('s', fread($handle, 2))[1];
	$width = unpack('s', fread($handle, 2))[1];
	$cellCount = $height * $width;
	$misExists = false;
	
	// Check if .mis file is present in zip file and assign variable if true.
	if ($zip->locateName("_" . $sha1 . ".mis") == true)	{
		$misExists = true;
		
		// Check if .mis file size is 68066.
		if (filesize($mis) != 68066 ) {
			echo 'Mis file size incorrect.';
			InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);
		}		
	}
	
	// Check if height or width is valid.	
	if ($height > 128 || $width > 128)	{		
		echo 'Height or width data in map file incorrect.';
		InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);		
	}
	
	// Check if file size is valid.
	if (($height * $width * 4) + 4 != filesize($map))	{		
		echo 'Map filesize incorrect.';
		InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);
	}				
	
	// Check if all cells are valid.
	for ($iter = 1; $iter<= $cellCount; $iter++)	{
		$tile = unpack('s', fread($handle, 2))[1];
		
		//Check tile index value of the cell.
		if ($tile >= 800)		{			
			echo 'Tile value incorrect.';
			InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);
		}
		
		$special = unpack('s', fread($handle, 2))[1];
		
		//Check special index value of the cell.
		if ($special >= 1000)		{			
			echo 'Special tile value incorrect.';
			InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);
		}
	}
	
	// Check if the .ini file is less than 2kb ofsize.
	if (filesize($ini) > 2000 ) {
		echo 'Ini file too large.';
		InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);
	}
	
	// Check if the .ini file is of text type.
	if (strcmp(mime_content_type($ini),"text/plain") != 0) {		
		echo 'Ini file not a text file.';
		InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip);
	}
	
	// Removal of temporary files for file checks when conditions successfully passed.
	fclose($handle);
	unlink ($map);
	unlink ($ini);
	if ($misExists === true) {
		unlink ($mis);
	}
	
    $mapData = null;
    $iniData = null;
    $misData = null;

    for ($i = 0; $i < 3; $i++) {
        $tmp = $zip->statIndex($i);

        if ($tmp['size'] > 128 * 128 * 8) {
            header('400 Bad Request');
            echo 'Map file larger than expected.';
            exit;
        }
        // Reads file content into variable.
        if (is_array($tmp) && preg_match('/\.map$/i', $tmp['name'])) {
            $mapData = $zip->getFromIndex($i);            
        } else if (is_array($tmp) && preg_match('/\.ini$/i', $tmp['name'])) {
            $iniData = $zip->getFromIndex($i);
        } else if (is_array($tmp) && preg_match('/\.mis$/i', $tmp['name'])) {
            $misData = $zip->getFromIndex($i);
        }
    }

    if ($mapData === null) {
        header('400 Bad Request');
        echo 'Map file not found in Zip.';
        exit;
    }
    
    if ($iniData === null) {
        header('400 Bad Request');
        echo 'Map ini file not found in Zip.';
        exit;
    }

    $res = $zip->open($zipName, ZipArchive::CREATE);    
    if ($res !== true) {
        header('500 Internal Server Error');
        echo 'Server failed to save map zip, sorry.';
        exit;
    }
    
    // Adds files to the zip file and saves it.
    $zip->addFromString($sha1 . '.map', $mapData);
    $zip->addFromString($sha1 . '.ini', $iniData);
    
    if ($misData)
        $zip->addFromString('_' . $sha1 . '.mis', $misData);
            
    $zip->close();

    echo 'Upload succeeded!';
    
    
    exit;
}

header('500 Internal Server Error');
echo 'Request not handled for some reason';

// Function: If map format is invalid, delete temporary files and exit.
function InvalidFileExit($handle, $map, $ini, $misExists, $mis, $zip) {
	header('400 Bad Request');
	fclose($handle);
	unlink ($map);
	unlink ($ini);
	if ($misExists === true) {
		unlink ($mis);
	}
	$zip->close();
	exit;
}	

?>