public string name; // Name of the file, padded with 1 to 3 zero bytes to reach #endregion Fields #region Methods public static FileDescriptionPacket Create(PacketHeader header, byte[] bytes, int index) { FileDescriptionPacket tmpPacket = new FileDescriptionPacket(); 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); Buffer.BlockCopy(bytes, index + offset, tmpPacket.hashfull, 0, tmpPacket.hashfull.Length * sizeof(byte)); offset += tmpPacket.hashfull.Length * sizeof(byte); Buffer.BlockCopy(bytes, index + offset, tmpPacket.hash16k, 0, tmpPacket.hash16k.Length * sizeof(byte)); offset += tmpPacket.hash16k.Length * sizeof(byte); tmpPacket.length = BitConverter.ToUInt64(bytes, index + offset); offset += sizeof(UInt64); // Name is specific to read since it's dependant of packet.length int name_offset = index + offset; int name_size = (int)header.length - header.GetSize() - tmpPacket.fileid.Length * sizeof(byte) - tmpPacket.hashfull.Length * sizeof(byte) - tmpPacket.hash16k.Length * sizeof(byte) - sizeof(UInt64); byte[] name = new byte[name_size]; Buffer.BlockCopy(bytes, name_offset, name, 0, name_size); tmpPacket.name = ToolKit.ByteArrayToString(name).TrimEnd('\0'); return tmpPacket; }
public static FileDescriptionPacket Create(PacketHeader header, byte[] bytes, int index) { FileDescriptionPacket tmpPacket = new FileDescriptionPacket(); 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); Buffer.BlockCopy(bytes, index + offset, tmpPacket.hashfull, 0, tmpPacket.hashfull.Length * sizeof(byte)); offset += tmpPacket.hashfull.Length * sizeof(byte); Buffer.BlockCopy(bytes, index + offset, tmpPacket.hash16k, 0, tmpPacket.hash16k.Length * sizeof(byte)); offset += tmpPacket.hash16k.Length * sizeof(byte); tmpPacket.length = BitConverter.ToUInt64(bytes, index + offset); offset += sizeof(UInt64); // Name is specific to read since it's dependant of packet.length int name_offset = index + offset; int name_size = (int)header.length - header.GetSize() - tmpPacket.fileid.Length * sizeof(byte) - tmpPacket.hashfull.Length * sizeof(byte) - tmpPacket.hash16k.Length * sizeof(byte) - sizeof(UInt64); byte[] name = new byte[name_size]; Buffer.BlockCopy(bytes, name_offset, name, 0, name_size); tmpPacket.name = ToolKit.ByteArrayToString(name).TrimEnd('\0'); return(tmpPacket); }
internal static FileDescriptionPacket Create(string filename, ulong filesize) { // Allocate some extra bytes for the packet in memory so that strlen() can // be used on the filename. The extra bytes do not get written to disk. string pad = filename.Length % 4 == 0 ? "" : new string(new char[4 - (filename.Length % 4)]); FileDescriptionPacket tmpPacket = new FileDescriptionPacket(); 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.filedescriptionpacket_type; tmpPacket.name = filename + pad; tmpPacket.fileid = new byte[16]; tmpPacket.hashfull = new byte[16]; tmpPacket.hash16k = new byte[16]; tmpPacket.length = filesize; tmpPacket.header.length = (ulong)tmpPacket.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; }
internal static FileDescriptionPacket Create(string filename, ulong filesize) { // Allocate some extra bytes for the packet in memory so that strlen() can // be used on the filename. The extra bytes do not get written to disk. string pad = filename.Length % 4 == 0 ? "" : new string(new char[4 - (filename.Length % 4)]); FileDescriptionPacket tmpPacket = new FileDescriptionPacket(); 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.filedescriptionpacket_type; tmpPacket.name = filename + pad; tmpPacket.fileid = new byte[16]; tmpPacket.hashfull = new byte[16]; tmpPacket.hash16k = new byte[16]; tmpPacket.length = filesize; tmpPacket.header.length = (ulong)tmpPacket.GetSize(); return tmpPacket; }