private bool GetPieceInfoFromFile(CheckIntegrityCallback callback) { string pieceFilePath = Config.ApplicationDataDirectory + IO.Path.DirectorySeparatorChar + this.infofile.PieceFileName + ".piece"; IO.FileInfo pieceFileInfo = new IO.FileInfo(pieceFilePath); if (pieceFileInfo.Exists) { // before we rely on the piece file, do a simple check of file existance and lengths to make sure nothing's changed for (int i = 0; i < this.infofile.FileCount; ++i) { string path = this.infofile.GetFileName(i); int properLength = this.infofile.GetFileLength(i); // TODO: introduce modification time check - requires saving time data in piece file IO.FileInfo fileInfo = new IO.FileInfo(path); if (!fileInfo.Exists) { return(false); // a file doesn't exist, do a proper check } long realLength = fileInfo.Length; if (properLength != realLength) { return(false); // a file doesn't have a proper length, do a proper check } } IO.FileStream pieceFile = pieceFileInfo.OpenRead(); int numBytes = this.infofile.PieceCount / 8 + ((this.infofile.PieceCount % 8) > 0 ? 1 : 0); byte[] data = new byte[numBytes]; pieceFile.Read(data, 0, data.Length); pieceFile.Close(); this.piecesDownloaded.SetFromRaw(data, 0, data.Length); this.piecesDownloaded.SetLength(this.infofile.PieceCount); this.numBytesLeft = 0; for (int i = 0; i < this.infofile.PieceCount; ++i) { if (!this.piecesDownloaded[i]) { this.numBytesLeft += this.infofile.GetPieceLength(i); } } if (callback != null) { callback(this, this.infofile.PieceCount - 1, false, 100.0f); } return(true); } else { return(false); } }
/// <summary> /// This *must* be called before any operations are performed. This should be in the constructor, but as it can be a time-consuming process /// it was decided against. This analyzes the file to look for which pieces are downloaded, or if the torrent has just started it will create the /// new, empty files. /// </summary> /// <param name="callback">Callback delegate to inform the caller of the progress</param> public void CheckIntegrity(bool forced, CheckIntegrityCallback callback) { // if their already is a piece file in the application directory, load it from there. (unless it is forced where is has to do it) if (forced || !GetPieceInfoFromFile(callback)) { int dataPosition = 0, filePosition = 0; int i = 0; byte[] data = new byte[this.infofile.GetPieceLength(0)]; Crypto.SHA1 sha = Crypto.SHA1CryptoServiceProvider.Create(); for (int currentFile = 0; currentFile < this.infofile.FileCount; ++currentFile) { int fileLength = this.infofile.GetFileLength(currentFile); string path = this.infofile.GetFileName(currentFile); IO.FileInfo fileInfo = new IO.FileInfo(path); if (!fileInfo.Exists) { // create the file if it does not exist this.CreateEmptyFile(fileInfo, fileLength); int fileDataLeft = 0; if (dataPosition > 0) { // if dataPosition does not equal zero, meaning we have some of a piece from the last file. This automatically fails, // and we move onto the next piece. i++; fileDataLeft = fileLength - (this.infofile.GetPieceLength(0) - dataPosition); } else { fileDataLeft = fileLength - filePosition; } int numPieces = fileDataLeft / this.infofile.GetPieceLength(0); i += numPieces; if (fileDataLeft % this.infofile.GetPieceLength(0) > 0) { // set the next file's filePosition, and fail the next piece filePosition = this.infofile.GetPieceLength(i) - (fileDataLeft % this.infofile.GetPieceLength(i)); i++; } else { filePosition = 0; } dataPosition = 0; if (callback != null) { callback(this, i, false, ((float)(i + 1) / (float)this.infofile.PieceCount) * 100.0f); } // move onto next file continue; } else { // check the length, otherwise truncate it if (fileInfo.Length != fileLength) { this.TruncateFile(fileInfo, fileLength); } // open the file, start checking. IO.FileStream fstream = fileInfo.OpenRead(); while (filePosition < fileLength) { int dataToRead = System.Math.Min(fileLength - filePosition, this.infofile.GetPieceLength(i) - dataPosition); byte[] tempData = new byte[dataToRead]; fstream.Read(tempData, 0, tempData.Length); if (dataToRead + dataPosition >= this.infofile.GetPieceLength(i)) { // piece finished System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); sha.ComputeHash(data, 0, this.infofile.GetPieceLength(i)); ByteField20 final = new ByteField20(sha.Hash); bool good = final.Equals(this.infofile.GetSHADigest(i)); if (!good) // if piece is good we can subtract it from the bytes left to download { numBytesLeft += this.infofile.GetPieceLength(i); } this.piecesDownloaded.Set(i, good); if (callback != null) { callback(this, i, good, ((float)(i + 1) / (float)this.infofile.PieceCount) * 100.0f); } i++; dataPosition = 0; } else { System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); dataPosition += dataToRead; } filePosition += dataToRead; } filePosition = 0; fstream.Close(); } } } if (this.PercentChanged != null) { this.PercentChanged(this, this.PercentComplete); } }
/// <summary> /// This *must* be called before any operations are performed. This should be in the constructor, but as it can be a time-consuming process /// it was decided against. This analyzes the file to look for which pieces are downloaded, or if the torrent has just started it will create the /// new, empty files. /// </summary> /// <param name="callback">Callback delegate to inform the caller of the progress</param> public void CheckIntegrity( bool forced, CheckIntegrityCallback callback) { // if their already is a piece file in the application directory, load it from there. (unless it is forced where is has to do it) if ( forced || !GetPieceInfoFromFile(callback)) { int dataPosition = 0, filePosition = 0; int i = 0; byte[] data = new byte[this.infofile.GetPieceLength(0)]; Crypto.SHA1 sha = Crypto.SHA1CryptoServiceProvider.Create(); for (int currentFile=0; currentFile<this.infofile.FileCount; ++currentFile) { int fileLength = this.infofile.GetFileLength(currentFile); string path = this.infofile.GetFileName(currentFile); IO.FileInfo fileInfo = new IO.FileInfo(path); if (!fileInfo.Exists) { // create the file if it does not exist this.CreateEmptyFile(fileInfo, fileLength); int fileDataLeft = 0; if (dataPosition > 0) { // if dataPosition does not equal zero, meaning we have some of a piece from the last file. This automatically fails, // and we move onto the next piece. i++; fileDataLeft = fileLength - (this.infofile.GetPieceLength(0) - dataPosition); } else fileDataLeft = fileLength - filePosition; int numPieces = fileDataLeft / this.infofile.GetPieceLength(0); i += numPieces; if (fileDataLeft % this.infofile.GetPieceLength(0) > 0) { // set the next file's filePosition, and fail the next piece filePosition = this.infofile.GetPieceLength(i) - (fileDataLeft % this.infofile.GetPieceLength(i)); i++; } else { filePosition = 0; } dataPosition = 0; if (callback != null) callback(this, i, false, ((float)(i+1)/(float)this.infofile.PieceCount) * 100.0f); // move onto next file continue; } else { // check the length, otherwise truncate it if (fileInfo.Length != fileLength) this.TruncateFile(fileInfo, fileLength); // open the file, start checking. IO.FileStream fstream = fileInfo.OpenRead(); while (filePosition < fileLength) { int dataToRead = System.Math.Min(fileLength - filePosition, this.infofile.GetPieceLength(i) - dataPosition); byte[] tempData = new byte[dataToRead]; fstream.Read(tempData, 0, tempData.Length); if (dataToRead + dataPosition >= this.infofile.GetPieceLength(i)) { // piece finished System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); sha.ComputeHash(data, 0, this.infofile.GetPieceLength(i)); ByteField20 final = new ByteField20(sha.Hash); bool good = final.Equals( this.infofile.GetSHADigest(i) ); if (!good) // if piece is good we can subtract it from the bytes left to download numBytesLeft += this.infofile.GetPieceLength(i); this.piecesDownloaded.Set(i, good); if (callback != null) callback(this, i, good, ((float)(i+1)/(float)this.infofile.PieceCount) * 100.0f); i++; dataPosition = 0; } else { System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); dataPosition += dataToRead; } filePosition += dataToRead; } filePosition = 0; fstream.Close(); } } } if (this.PercentChanged != null) this.PercentChanged(this, this.PercentComplete); }
private bool GetPieceInfoFromFile(CheckIntegrityCallback callback) { string pieceFilePath = Config.ApplicationDataDirectory + IO.Path.DirectorySeparatorChar + this.infofile.PieceFileName + ".piece"; IO.FileInfo pieceFileInfo = new IO.FileInfo(pieceFilePath); if (pieceFileInfo.Exists) { // before we rely on the piece file, do a simple check of file existance and lengths to make sure nothing's changed for (int i=0; i<this.infofile.FileCount; ++i) { string path = this.infofile.GetFileName(i); int properLength = this.infofile.GetFileLength(i); // TODO: introduce modification time check - requires saving time data in piece file IO.FileInfo fileInfo = new IO.FileInfo(path); if (!fileInfo.Exists) return false; // a file doesn't exist, do a proper check long realLength = fileInfo.Length; if (properLength != realLength) return false; // a file doesn't have a proper length, do a proper check } IO.FileStream pieceFile = pieceFileInfo.OpenRead(); int numBytes = this.infofile.PieceCount/8 + ((this.infofile.PieceCount % 8) > 0 ? 1 : 0); byte[] data = new byte[numBytes]; pieceFile.Read(data, 0, data.Length); pieceFile.Close(); this.piecesDownloaded.SetFromRaw( data, 0, data.Length ); this.piecesDownloaded.SetLength(this.infofile.PieceCount); this.numBytesLeft = 0; for (int i=0; i<this.infofile.PieceCount; ++i) { if (!this.piecesDownloaded[i]) this.numBytesLeft += this.infofile.GetPieceLength(i); } if (callback != null) callback(this, this.infofile.PieceCount-1, false, 100.0f); return true; } else return false; }