private static byte[] GenerateDataSubpacketFrame( CRC32 crcCalculator, byte[] src, int offset, int chunkSize, ZDLESequence?forceZDLESequence = null) { // Slice data var dataSlice = src .Skip(offset) .Take(chunkSize) .ToArray(); var encodedDataSlice = ZDLEEncoder.EscapeControlCharacters(dataSlice); // If a ZDLESequence is forced we use that one. // Otherweise we take ZCRCQ and if the frame is the end frame, ZCRCE. var requiredSequence = forceZDLESequence.HasValue ? forceZDLESequence.Value : (offset + dataSlice.Length) < src.Length ? ZDLESequence.ZCRCG : ZDLESequence.ZCRCE; // Create data queue for the sliced data. var queue = new Queue <byte>(encodedDataSlice); // Create data for checksum var beforeCTC = dataSlice.Concat(new byte[] { (byte)requiredSequence }).ToArray(); // Compute hash var crc = crcCalculator.ComputeHash(beforeCTC); // Add control characters queue.Enqueue((byte)ControlBytes.ZDLE); queue.Enqueue((byte)requiredSequence); var dataSubpacketFrame = queue?.ToArray(); var encodedCRC = dataSubpacketFrame .Concat(ZDLEEncoder.EscapeControlCharacters(crc)) .ToArray(); return(encodedCRC); }
/// <summary> /// The sender then sends a ZFILE header with ZMODEM Conversion, Management, and Transport options[3] /// followed by a ZCRCW data subpacket containing the file name, file length, /// modification date, and other information identical to that used by YMODEM Batch. /// </summary> /// <param name="fileInfo"></param> /// <param name="crcCalculator"></param> private bool SendZFILEHeaderCommand(string filename, int length, DateTimeOffset lastWriteTimeUtc, CRC32 crcCalculator) { var result = default(bool); var isExtended = true; var zFileHeader = Utils.Build32BitBinHeader( HeaderType.ZFILE, ZFILEConversionOption.ZCBIN, ZFILEManagementOption.ZMNEWL, ZFILETransportOption.None, ZFILEExtendedOptions.None, crcCalculator ); var zFileHeaderQueue = new Queue <byte>(); foreach (var c in zFileHeader) { zFileHeaderQueue.Enqueue((byte)c); } // Send ZFILE header first - No response SendCommand(zFileHeaderQueue.ToArray()); var dataQueue = new Queue <byte>(); foreach (char c in filename) { dataQueue.Enqueue((byte)c); } if (isExtended) { dataQueue.Enqueue(0); // File length as decimal string var fileLength = length.ToString(); foreach (var c in fileLength) { dataQueue.Enqueue((byte)c); } // Space dataQueue.Enqueue(0x20); var utcTime = lastWriteTimeUtc.ToUnixTimeSeconds(); var octalString = Convert.ToString(utcTime, 8); // Modification date foreach (var c in octalString) { dataQueue.Enqueue((byte)c); } } else { // The file information is terminated by a null. // If only the pathname is sent, the pathname is terminated with two nulls. dataQueue.Enqueue(0); dataQueue.Enqueue(0); } byte[] data = dataQueue.Concat(new byte[] { (byte)ZDLESequence.ZCRCW }).ToArray(); dataQueue.Enqueue((byte)ControlBytes.ZDLE); dataQueue.Enqueue((byte)ZDLESequence.ZCRCW); var crc = crcCalculator.ComputeHash(data); var encodedCRC = dataQueue .Concat(ZDLEEncoder.EscapeControlCharacters(crc)) .ToArray(); var response = SendCommand(encodedCRC, true, HeaderType.ZRPOS); // We expect ZRPOS if (response?.ZHeader == HeaderType.ZRPOS) { result = true; } /* * The receiver may respond with a ZSKIP header, which makes the sender * proceed to the next file (if any) in the batch. * * A ZRPOS header from the receiver initiates transmission of the file * data starting at the offset in the file specified in the ZRPOS header. * Normally the receiver specifies the data transfer to begin begin at * offset 0 in the file. */ if (response?.ZHeader == HeaderType.ZSKIP || response?.ZHeader == HeaderType.ZRPOS || response?.ZHeader == HeaderType.ZCRC) { /* * The receiver has a file with the same name and length, may * respond with a ZCRC header with a byte count, which * requires the sender to perform a 32 bit CRC on the * specified number of bytes in the file and transmit the * complement of the CRC in an answering ZCRC header.the crc is * initialised to 0xfffffff; a byte count of 0 implies the entire * file the receiver uses this information to determine whether to * accept the file or skip it. This sequence may be triggered * by the ZMCRC Management Option. */ } return(result); }