public byte[] fileid = new byte[16]; // MD5hash of file_hash_16k, file_length, file_name #endregion Fields #region Methods public static FileVerificationPacket Create(PacketHeader header, byte[] bytes, int index) { FileVerificationPacket tmpPacket = new FileVerificationPacket(); tmpPacket.header = header; int offset = 0; Buffer.BlockCopy(bytes, index + offset, tmpPacket.fileid, 0, tmpPacket.fileid.Length * sizeof(byte)); offset += tmpPacket.fileid.Length * sizeof(byte); int nbEntries = ((int)header.length - header.GetSize() - (16 * sizeof(byte))) / FileVerificationEntry.GetSize(); tmpPacket.entries = new List<FileVerificationEntry>(); tmpPacket.blockcount = (ulong)((header.length - (ulong)tmpPacket.GetSize()) / (ulong)FileVerificationEntry.GetSize()); for (int i = 0; i < nbEntries; i++) { FileVerificationEntry entry = new FileVerificationEntry(); Buffer.BlockCopy(bytes, index + offset, entry.hash, 0, entry.hash.Length * sizeof(byte)); offset += entry.hash.Length * sizeof(byte); entry.crc = BitConverter.ToUInt32(bytes, index + offset); offset += sizeof(UInt32); tmpPacket.entries.Add(entry); } return tmpPacket; }
public static FileVerificationPacket Create(PacketHeader header, byte[] bytes, int index) { FileVerificationPacket tmpPacket = new FileVerificationPacket(); tmpPacket.header = header; int offset = 0; Buffer.BlockCopy(bytes, index + offset, tmpPacket.fileid, 0, tmpPacket.fileid.Length * sizeof(byte)); offset += tmpPacket.fileid.Length * sizeof(byte); int nbEntries = ((int)header.length - header.GetSize() - (16 * sizeof(byte))) / FileVerificationEntry.GetSize(); tmpPacket.entries = new List <FileVerificationEntry>(); tmpPacket.blockcount = (ulong)((header.length - (ulong)tmpPacket.GetSize()) / (ulong)FileVerificationEntry.GetSize()); for (int i = 0; i < nbEntries; i++) { FileVerificationEntry entry = new FileVerificationEntry(); Buffer.BlockCopy(bytes, index + offset, entry.hash, 0, entry.hash.Length * sizeof(byte)); offset += entry.hash.Length * sizeof(byte); entry.crc = BitConverter.ToUInt32(bytes, index + offset); offset += sizeof(UInt32); tmpPacket.entries.Add(entry); } return(tmpPacket); }
// Create a packet large enough for the specified number of blocks internal static FileVerificationPacket Create(ulong _blockcount) { // Allocate a packet large enough to hold the required number of verification entries. FileVerificationPacket tmpPacket = new FileVerificationPacket(); tmpPacket.blockcount = _blockcount; // Record everything we know in the packet. tmpPacket.header = new PacketHeader(); tmpPacket.header.magic = Par2FileReader.packet_magic; tmpPacket.header.hash = new byte[16]; tmpPacket.header.setid = new byte[16]; tmpPacket.header.type = Par2FileReader.fileverificationpacket_type; tmpPacket.fileid = new byte[16]; tmpPacket.entries = new List <FileVerificationEntry>((int)_blockcount); tmpPacket.header.length = (ulong)tmpPacket.GetSize() + (_blockcount * (ulong)FileVerificationEntry.GetSize()); return(tmpPacket); }
// Open the source file, compute the MD5 Hash of the whole file and the first // 16k of the file, and then compute the FileId and store the results // in a file description packet and a file verification packet. public bool Open(string filename, ulong blocksize, bool deferhashcomputation) { // Get the filename and filesize targetfilename = filename; filesize = (ulong)new FileInfo(filename).Length; // Work out how many blocks the file will be sliced into blockcount = (uint)((filesize + blocksize - 1) / blocksize); // Determine what filename to record in the PAR2 files parfilename = Path.GetFileName(filename); // Create the Description and Verification packets FileDescriptionPacket = Packets.FileDescriptionPacket.Create(parfilename, filesize); FileVerificationPacket = Packets.FileVerificationPacket.Create(blockcount); // Create the diskfile object targetfile = new DiskFile(); // Open the source file if (!targetfile.Open(targetfilename, filesize)) return false; // Do we want to defer the computation of the full file hash, and // the block crc and hashes. This is only permitted if there // is sufficient memory available to create all recovery blocks // in one pass of the source files (i.e. chunksize == blocksize) if (deferhashcomputation) { // Initialise a buffer to read the first 16k of the source file uint buffersize = Math.Min((uint)filesize, 16 * 1024); byte[] buffer = new byte[buffersize]; // Read the data from the file if (!targetfile.Read(0, buffer, buffersize)) { targetfile.Close(); return false; } // Compute the hash of the data read from the file // Store the hash in the descriptionpacket and compute the file id MD5 md5Hasher = MD5.Create(); FileDescriptionPacket.hash16k = md5Hasher.ComputeHash(buffer); // Compute the fileid and store it in the verification packet. FileDescriptionPacket.ComputeFileId(); FileVerificationPacket.fileid = (byte[])FileDescriptionPacket.fileid.Clone(); //// Allocate an MD5 context for computing the file hash //// during the recovery data generation phase contextfull = MD5.Create(); //contextfull = new MD5Context; } else { // Compute 16k MD5 hash using (BinaryReader br = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))) { MD5 md5Hasher = MD5.Create(); byte[] buffer16k = br.ReadBytes(16 * 1024); FileDescriptionPacket.hash16k = md5Hasher.ComputeHash(buffer16k); } // Compute the fileid and store it in the verification packet. FileDescriptionPacket.ComputeFileId(); FileVerificationPacket.fileid = (byte[])FileDescriptionPacket.fileid.Clone(); // Compute full file MD5 hash & block CRC32 and MD5 hashes //long readSize = 5 * (long)blocksize; long readSize = (long)blocksize; long fileSize = new FileInfo(filename).Length; long nbSteps = fileSize / readSize; long remaining = fileSize % readSize; uint blocknumber = 0; // TODO : switch to async filestream using (BinaryReader br = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))) { MD5 md5FullHasher = MD5.Create(); MD5 md5Hasher = MD5.Create(); //FastCRC32.FastCRC32 crc32 = new FastCRC32.FastCRC32(blocksize); FastCRC32.FastCRC32 crc32 = FastCRC32.FastCRC32.GetCRC32Instance(blocksize); byte[] blockHash = new byte[16]; uint blockCRC32 = 0; for (int i = 0; i < nbSteps + 1; ++i) { byte[] buffer = br.ReadBytes((int)(i == nbSteps ? remaining : readSize)); // Global MD5 hash if (i == nbSteps) md5FullHasher.TransformFinalBlock(buffer, 0, buffer.Length); else md5FullHasher.TransformBlock(buffer, 0, buffer.Length, null, 0); //for (uint j = 0; j < 5; ++j) //{ // Block MD5 hash & CRC32 uint length = (uint)blocksize; //if (i == nbSteps && j == 4) //if (i == nbSteps && (buffer.Length - (uint)(j * blocksize)) < (int)blocksize) if (i == nbSteps && remaining != 0) { // We need arry padding since calculation **MUST** always be done on blocksize-length buffers byte[] smallBuffer = buffer; buffer = new byte[blocksize]; Buffer.BlockCopy(smallBuffer, 0, buffer, 0, smallBuffer.Length); } blockCRC32 = crc32.CRCUpdateBlock(0xFFFFFFFF, (uint)blocksize, buffer, 0) ^ 0xFFFFFFFF; blockHash = md5Hasher.ComputeHash(buffer, 0, (int)blocksize); //Console.WriteLine("blocknumber:{0},hash:{1},crc32:{2}", blocknumber, blockHash, blockCRC32); FileVerificationPacket.SetBlockHashAndCRC(blocknumber++, blockHash, blockCRC32); //} } FileDescriptionPacket.hashfull = md5FullHasher.Hash; } } return true; }
// Create a packet large enough for the specified number of blocks internal static FileVerificationPacket Create(ulong _blockcount) { // Allocate a packet large enough to hold the required number of verification entries. FileVerificationPacket tmpPacket = new FileVerificationPacket(); tmpPacket.blockcount = _blockcount; // Record everything we know in the packet. tmpPacket.header = new PacketHeader(); tmpPacket.header.magic = Par2FileReader.packet_magic; tmpPacket.header.hash = new byte[16]; tmpPacket.header.setid = new byte[16]; tmpPacket.header.type = Par2FileReader.fileverificationpacket_type; tmpPacket.fileid = new byte[16]; tmpPacket.entries = new List<FileVerificationEntry>((int)_blockcount); tmpPacket.header.length = (ulong)tmpPacket.GetSize() + (_blockcount * (ulong)FileVerificationEntry.GetSize()); return tmpPacket; }