Location: PHPKode > scripts > Torrent Pieces Hash Verification > torrent-pieces-hash-verification/class.hashcheck.php
<?php 
require_once 'class.bdecode.php';
require_once 'class.bencode.php';
require_once 'class.torrentinfo.php';
require_once 'class.torrentfile.php';
require_once 'class.torrentpiece.php';

/**
 * Main Class, Check hashes
 * 
 * @author Charalampos Pournaris
 */
class HashCheck {
	/**
	 * Torrent file path
	 *
	 * @var string
	 */
	private $torrent_file = '';
	
	/**
	 * Target directory path
	 *
	 * @var string
	 */
	private $target_path = '';
	
	/**
	 * Torrent metadata file
	 *
	 * @var BDecode
	 */
	private $torrent = null;
	
	/**
	 * Torrent metadata information
	 *
	 * @var TorrentInfo
	 */
	private $torrent_info = null;
	
	/**
	 * Total data missing from $target_path
	 *
	 * @var integer
	 */
	private $total_data_missing = 0;
	
	// Hash check related
	
	/**
	 * Pieces that need to be re-downloaded
	 *
	 * @var array
	 */
	private $pieces_needed = array();
	
	/**
	 * Pieces index array
	 * 
	 * Each key corresponds to a piece number
	 *
	 * @var array
	 */
	private $pieces_index = array();
	
	/**
	 * Files that should be checked
	 *
	 * @var array
	 */
	private $filelist = array();

	/**
	 * Last piece's extra bytes counted
	 *
	 * @var unknown_type
	 */
	private $extra_bytes = 0;
	
	/**
	 * Object constructor. Initializes member data
	 *
	 * @param string $torrent_file
	 * @param string $target_path
	 */
	public function __construct($torrent_file, $target_path) {
		$this->torrent_file = $torrent_file;
		$this->target_path = $target_path;
		
		$this->torrent = new BDecode ( $torrent_file );

		if (! empty ( $this->torrent->result ['error'] ))
			printConsole ( "'$torrent_file' ".$this->torrent->result ['error'], true );
		
		$this->torrent_info = new TorrentInfo ( $this->torrent );		
		$this->filelist = $this->torrent_info->getFilePathList ($target_path);
	}
	
	/**
	 * Create the pieces list for each torrent file
	 *
	 */
	public function makePieces() {
		$piece_size = $this->torrent_info->getPieceLength();
		$previous_shared = null;
		$pieceid = 0;
		TorrentPiece::$piece_size = $piece_size;
		
		foreach ($this->filelist as &$file) {
			$pieces = array();
			$file_offset = 0;
			
			if (!is_null($previous_shared)) {
				
				if ($previous_shared->getRemaining() > $file['size']) {
					$remaining = $previous_shared->getRemaining();
				
					$previous_shared = new TorrentPiece($previous_shared->getPieceId(), $file_offset, $file['size'], false, true);					
					$previous_shared->setRemaining( $remaining - $file['size']);
					$this->pieces_index[$previous_shared->getPieceId()]['filenames'][] = $file['path'];
					
					$file['pieces'] = array($previous_shared);
					unset($pieces);
					continue;
				} else {
					$new_partial = new TorrentPiece($previous_shared->getPieceId(), $file_offset, $previous_shared->getRemaining(), false, true);
					$file_offset = $previous_shared->getRemaining();
					$new_partial->setRemaining(0);
					$this->pieces_index[$new_partial->getPieceId()]['filenames'][] = $file['path'];
					$pieces[] = $new_partial;
					$previous_shared = null;
					unset($new_partial);			
				}
			}
			
			while($file_offset + $piece_size <= $file['size']) {				
				++$pieceid;
				
				$p = new TorrentPiece($pieceid, $file_offset, $file_offset+$piece_size, true, false);
				$pieces[] = $p;
				$this->pieces_index[$p->getPieceId()] = array('piece'=>$p,'filenames'=>array($file['path']));
				$file_offset += $piece_size;
			}
			
			if ($file_offset < $file['size']) {
				++$pieceid;
				$previous_shared = new TorrentPiece($pieceid, $file_offset, $file['size'], false, true);
				$this->pieces_index[$previous_shared->getPieceId()] = array('piece'=>$previous_shared,'filenames'=>array($file['path']));
				$pieces[] = $previous_shared;
			} else {
				$previous_shared = null;
			}

			$file['pieces'] = $pieces;			
			
			unset($pieces);			
		}
		
		// Count the last piece as complete - looks ugly I know..
		$fl_last_element = count($this->filelist)-1;
		$lelem = $this->filelist[$fl_last_element]['pieces'][count($this->filelist[$fl_last_element]['pieces'])-1];
		$this->extra_bytes = $lelem->getRemaining(); 
		$lelem->setRemaining(0);
		unset($lelem);
	}
	
	/**
	 * Read the files, make the hashes and do the check
	 * 
	 * This method also prints results
	 *
	 */
	public function doCheck() {		
		$piece_size = $this->torrent_info->getPieceLength();
		$torrent_file = null;
		$partial_data = '';		
		
		TorrentFile::$piece_size = $piece_size;
		
		$this->makePieces();
		
		foreach ($this->filelist as $file_info) {
			$torrent_file = new TorrentFile($file_info['path'], $file_info['size'], $file_info['pieces']);
			
			if ($torrent_file->open()) {
				$torrent_file->read($partial_data);
				$torrent_file->close();
				
				$partial_data = $torrent_file->getPartialData();
			} else {
				foreach ($file_info['pieces'] as $piece) {
					if (!in_array($piece->getPieceId(), $this->pieces_needed)) {
						$this->pieces_needed[] = $piece->getPieceId();
					}
				}
			}
		}

		foreach (TorrentFile::$hashes as $piece_num=>$hash) {
			if (!$this->torrent_info->equalPieceHash($piece_num, $hash)) {
				
				if (!in_array($piece_num, $this->pieces_needed)) {
					$this->pieces_needed[] = $piece_num;
				}
			}
		}
		
		sort($this->pieces_needed);
		
		foreach ($this->pieces_needed as $pid) {
			$this->total_data_missing += $this->pieces_index[$pid]['piece']->getPieceLength();
			printConsole("Piece #$pid:");
			foreach ($this->pieces_index[$pid]['filenames'] as $file) {
				printConsole($file);
			}
			printConsole('');
		}		
		
		$total_file_size = $this->torrent_info->calculateLength();
		
		printConsole("File percent valid: ".round((( (($total_file_size + $this->extra_bytes) - (count($this->pieces_needed) * $piece_size)) / ($total_file_size))*100),2).'% ('.round((((count($this->pieces_needed) * $piece_size)/1024)/1024),2)." mb missing)" );
	}
}
?>
Return current item: Torrent Pieces Hash Verification