// Zmodem.cpp: implementation of the Zmodem class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Zmodem.h" #include "CtrlProtocolDef.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif extern SYSTEMTIME g_sysCurTime; extern int g_iUILanguage; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// // // Zmodem::Zmodem() // { // // } // Zmodem::~Zmodem() { //str = NULL; } Zmodem::Zmodem( CSComPort *rs232_port ) { port = rs232_port; // str = NULL; // m_BuffCount = 0; // m_BufReadIndex = 0; ClearReadBuf(); file_count = 0; file_name[ 0 ] = '\0'; file_length = -1L; file = NULL; receiver_buffer_length = 16384; wake_up_sender_header_type = ZRINIT; memset(m_WriteBuff, 0, 8192); m_WriteBuffLength = 0; m_ByteTransfer = 0; m_FileSize = 0; nIndex = 0; // nTestID = 123456; } // The public Send functionsends a batch of files one at a time // via the SendSingleFile function. When a normal completion // occurs, it is flagged with a ZFIN frame. BOOL Zmodem::Send( char *files[] ) { //log zmodemLog = fopen("log\\zmodemSZLog.txt","ab"); if (!zmodemLog) { zmodemLog = fopen("zmodemSZLog.txt","ab"); } dwBegin = GetTickCount(); memset(cReadLog,0,2048); nLogLength = sprintf(cReadLog,"\r\n"); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); nLogLength = sprintf(cReadLog,*files); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); nLogLength = sprintf(cReadLog,"\r\n"); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); //log PackLongIntoHeader( 0L ); SendHexHeader(4, ZRQINIT, transmitted_header); GetRinitHeader(); byte_count = -1; while ( *files ) { int n = SendSingleFile( *files ); if ( n == ZERROR ) { if (file) { //log nLogLength = sprintf(cReadLog,"SendSingleFile = %d\r\n",n); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); //log fclose(file); fclose(zmodemLog); return FALSE; } //log nLogLength = sprintf(cReadLog,"SendSingleFile = %d\r\n",n); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); //log } files++; } SendZFIN(); if (file) fclose(file); if (zmodemLog) fclose(zmodemLog); return TRUE; } // This function is used *everywhere*, and benefits from being // declared as inline. inline int Zmodem::ReadChar( long timeout ) { // int c = ReadBuff( timeout ); // return ( c < 0 ) ? TIMEOUT : c; if ((0 == m_BuffCount) || (m_nBufReadIndex == m_BuffCount))//除非已经读取完毕,再进行缓存的读取 { if (ReadBuff(timeout) != TIMEOUT) { return buff[m_nBufReadIndex++ ] & 0377; } else return TIMEOUT; } return buff[m_nBufReadIndex++ ] & 0377; } // This is the worker routine that transmits a single file. int Zmodem::SendSingleFile( char *name ) { int c; unsigned long crc_value; long lastcrcrq = -1; int length; //add by waston int nnlen;//log char *FileName; int nLogCode = 0; CString strLogTmp = _T(""); //add by waston file = fopen( name, "rb" ); FileName = strrchr(name,'\\'); FileName++; if ( file == NULL) { error( "Failed to open %s", name ); return OK; } file_at_eof = 0; fseek( file, 0, SEEK_END ); file_length = ftell( file ); fseek( file, 0, SEEK_SET ); m_FileSize = file_length; //将文件大小写到接口 length = sprintf( buffer, "%s%c%u 0 0 0 0 0", FileName, 0, file_length ); for ( ; ; ) { PackLongIntoHeader( 0L ); SendBinaryHeader( 4, ZFILE, transmitted_header ); SendDataFrame( buffer, length, ZCRCW ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRUN: send ZFILE DATA FRAME: %s"), __LINE__, GetTickCount() - dwBegin,buffer); PrintLogLast(strLogTmp); //log again: c = ReadHeader( received_header ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tswitch = %d"), __LINE__, GetTickCount() - dwBegin, c); PrintLogLast(strLogTmp); //log switch ( c ) { case ZRINIT: while ( ( c = ReadChar( 5000L ) ) > 0 ) if ( c == ZPAD ) goto again; /* **** FALL THRU TO **** */ default: continue; case ZCAN: case TIMEOUT: case ZABORT: case ZFIN: return ZERROR; case ZCRC: if ( received_file_position != lastcrcrq ) { Crc32 crc( 0xFFFFFFFFL ); lastcrcrq = received_file_position; fseek( file, 0L, SEEK_SET ); while ( ( ( c = getc( file ) ) != EOF ) && --lastcrcrq ) crc.update(c ); crc_value = ~crc.value(); fseek( file, 0L, SEEK_SET ); lastcrcrq = received_file_position; } PackLongIntoHeader( crc_value ); SendBinaryHeader( 4, ZCRC, transmitted_header ); goto again; case ZSKIP: fclose( file ); return OK; case ZRPOS: nLogCode = fseek(file,received_file_position,SEEK_SET); if (nLogCode) { //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\t case ZRPOS = %d,fseek = %d"), __LINE__, GetTickCount() - dwBegin, received_file_position,nLogCode); PrintLogLast(strLogTmp); //log return ZERROR; } last_sync_position = (byte_count=transmitted_file_position= last_reported_position= received_file_position) - 1; return SendFileContents(); } } } int Zmodem::SendFileContents( void ) { int c; int e; int n; int junkcount; int newcnt; junkcount = 0; CString strLogTmp = _T(""); start_read: receiver_buffer_length = 2048; newcnt = receiver_buffer_length; PackLongIntoHeader( transmitted_file_position ); SendBinaryHeader( 4, ZDATA, transmitted_header ); do { n = fread( buffer, 1, 1024, file ); if ( n < 1024 ) file_at_eof = 1; if ( file_at_eof ) e = ZCRCE; else if ( junkcount > 3 ) e = ZCRCW; else if ( byte_count == last_sync_position ) e = ZCRCW; else if ( receiver_buffer_length && ( newcnt -= n ) <= 0 ) e = ZCRCW; else e = ZCRCG; SendDataFrame( buffer, n, e ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tSendData: %d, fread = %d"), __LINE__, GetTickCount() - dwBegin, e,n); PrintLogLast(strLogTmp); //log m_ByteTransfer = byte_count = transmitted_file_position += n; if ( e == ZCRCW ) goto waitack; while ( /*port->RXSpaceUsed()*/ m_nBufReadIndex < m_BuffCount) { switch ( ReadChar( 100 ) ) { case CAN: case ZPAD: c = SyncWithReceiver( 1 ); if ( c == ZACK ) break; SendDataFrame( buffer, 0, ZCRCE ); goto gotack; case XOFF: case XOFF | 0x80 : ReadChar( 10000L ); default: junkcount++; } } } while ( !file_at_eof ); int nnlen; for ( ; ; ) { PackLongIntoHeader( transmitted_file_position ); SendBinaryHeader( 4, ZEOF, transmitted_header ); switch ( SyncWithReceiver( 0 ) ) { case ZACK: continue; case ZRPOS: goto start_read; case ZRINIT: return OK; case ZSKIP: fclose( file ); return c; default: fclose( file ); return ZERROR; } } //Backchannel processing waitack: junkcount = 0; c = SyncWithReceiver( 0 ); gotack: switch ( c ) { default: case ZCAN: fclose( file ); return ZERROR; case ZSKIP: fclose( file ); return c; case ZACK: case ZRPOS: break; case ZRINIT: return OK; } while ( /*port->RXSpaceUsed()*/ m_nBufReadIndex < m_BuffCount) { switch ( ReadChar( 100 ) ) { case CAN: case ZPAD: c = SyncWithReceiver( 1 ); goto gotack; case XOFF : case XOFF | 0x80 : ReadChar( 10000L ); } } goto start_read; } // If all goes well, the public receive function just calls // WakeUpSender(), then ReceiveFiles(). If both of those // do what they are supposed to do, a batch of files will // have been properly transferred. BOOL Zmodem::Receive( CString Path ) { //log zmodemLog = fopen("log\\zmodemRZLog.txt","ab"); if (!zmodemLog) { zmodemLog = fopen("zmodemRZLog.txt","ab"); } dwBegin = GetTickCount(); memset(cReadLog,0,2048); nLogLength = sprintf(cReadLog,"\r\n"); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); nLogLength = sprintf(cReadLog,"\r\n"); fwrite(cReadLog,nLogLength,1,zmodemLog); fflush(zmodemLog); //log memset(buffer,0,BUFFSIZE); static char CancelString[] = { CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, BS, BS, BS, BS, BS, BS, BS, BS, BS, BS }; switch ( WakeUpSender() ) { case 0 : case ZCOMPL : return OK; case ZERROR : break; default : if ( ReceiveFiles(Path) == OK ) { if ( file ) { if (0 != fclose(file)) { CString strTmp = _T(""); strTmp.Format(_T("{Zmodem} [%d] close file failed!!!,ErrorNo = %d"), __LINE__, GetLastError()); PrintLogLast(strTmp); } file = NULL; } fclose(zmodemLog);//log; return TRUE; } } port->ZmodemSendDataDirectly( CancelString, sizeof CancelString);//tmp if ( file ) { if (0 != fclose(file)) { CString strTmp = _T(""); strTmp.Format(_T("{Zmodem} [%d] close file failed!!!,ErrorNo = %d"), __LINE__, GetLastError()); PrintLogLast(strTmp); } file = NULL; } fclose(zmodemLog);//log; return FALSE; } // This is the general purpose receiver function. It calls the // ReceiveSingleFile function repeatedly as long as the wakeup // function keeps receiving ZFILE frames. int Zmodem::ReceiveFiles( CString Path ) { int return_status ; for ( ; ; ) { switch ( return_status = ReceiveSingleFile(Path) ) { case ZEOF: case ZSKIP: switch ( WakeUpSender() ) { case ZCOMPL: return OK; default: return ZERROR; case ZFILE: break; } continue; default: return return_status; case ZERROR: return ZERROR; } } } // Some data used various places in the class static char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"}; static char *frametypes[] = { "No Response to Error Correction Request", "No Carrier Detect", "TIMEOUT", "ZERROR", "ZRQINIT", "ZRINIT", "ZSINIT", "ZACK", "ZFILE", "ZSKIP", "ZNAK", "ZABORT", "ZFIN", "ZRPOS", "ZDATA", "ZEOF", "ZFERR", "ZCRC", "ZCHALLENGE", "ZCOMPL", "ZCAN", "ZFREECNT", "ZCOMMAND", "ZSTDERR" }; // This function is used when receiving files. It sends out the // initial frame and waits for a response from the sender. If // things go properly it will get the file data subpacket and // return ZFILE. If the sender has no more files it will send a // ZFIN, which is handled here. int Zmodem::WakeUpSender( void ) { int c; int n; for ( n = 0 ; n < 16 ; n++ ) { PackLongIntoHeader( 3072L ); transmitted_header[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK; SendHexHeader( 4, wake_up_sender_header_type, transmitted_header ); if ( wake_up_sender_header_type == ZSKIP ) wake_up_sender_header_type = ZRINIT; for ( int try_again = 1 ; try_again ; ) { switch ( ReadHeader( received_header ) ) { case ZRQINIT : case ZEOF : case TIMEOUT : default : try_again = 0; break; case ZFILE : wake_up_sender_header_type = ZRINIT; c = ReadDataFrame( buffer, 1024 ); if ( c == GOTCRCW ) return ZFILE; SendHexHeader( 4, ZNAK, transmitted_header ); break; case ZSINIT : if (ReadDataFrame(attention_string, ZATTNLEN) == GOTCRCW ) { PackLongIntoHeader( 1L ); SendHexHeader(4,ZACK,transmitted_header); } else SendHexHeader(4,ZNAK,transmitted_header); break; case ZCOMPL : break; case ZFIN : AckZFIN(); return ZCOMPL; case ZCAN : return ZERROR; } } } return 0; } // This is the workhorse routine that reads a single file from the // sender. It reads in headers until it gets a ZDATA header, then // it switches over to reading data subpackets until it gets one // of the end of supbacket codes. int Zmodem::ReceiveSingleFile( CString Path ) { int c; int error_count; long rxbytes; if ( OpenInputFile( Path, buffer ) == ZERROR ) return wake_up_sender_header_type = ZSKIP; error_count = 0; rxbytes = 0L; long log_data2; // char chLog[200] = ""; CString strLogTmp = _T(""); for ( ; ; ) { PackLongIntoHeader( rxbytes ); SendHexHeader( 4, ZRPOS, transmitted_header ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRUN: send ZPROS:rxbytes = %d"), __LINE__, GetTickCount() - dwBegin, rxbytes); PrintLogLast(strLogTmp); //log nxthdr: c = ReadHeader( received_header ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRUN: read ReadHeader c = %d"), __LINE__, GetTickCount() - dwBegin, c); PrintLogLast(strLogTmp); //log switch (c) { default: error( "ReceiveSingleFile: ReadHeader returned %d", c ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRUN: default:"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log // return ZERROR;3 goto nxthdr; case ZNAK: { strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\ Receice ZANK!!!"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); continue; } case TIMEOUT: if ( ++error_count >= 20 ) { error("ReceiveSingleFile: ReadHeader returned %d", c ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\ Receice TIMEOUT"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return ZERROR; } continue; case ZFILE: ReadDataFrame( buffer, 1024 ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: ZFILE:"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log continue; case ZEOF: log_data2 = UnpackHeaderIntoLong(received_header); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: ZEOF:log_data2 = %d, rxbytes = %d"), __LINE__, GetTickCount() - dwBegin, log_data2, rxbytes); PrintLogLast(strLogTmp); //log if (log_data2 != rxbytes) goto nxthdr; // continue; // if ( fclose( file ) != 0 ) // { // wake_up_sender_header_type = ZFERR; // error( "ReceiveSingleFile: fclose() " // "returned error" ); // return ZERROR; // } return c; case ZERROR: //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: ZERROR:"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log if ( ++error_count >= 20 ) { error( "ReceiveSingleFile: ReadHeader " "returned %d", c ); return ZERROR; } // SendAttentionString(); // continue;1 goto nxthdr; case ZSKIP: //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: ZSKIP:"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log status("ReceiveSingleFile: Sender SKIPPED file"); return c; case ZDATA: long log_data = UnpackHeaderIntoLong( received_header ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: recv ZDATA:log_data = %d, rxbytes = %d"), __LINE__, GetTickCount() - dwBegin, log_data,rxbytes); PrintLogLast(strLogTmp); //log if ( log_data != rxbytes ) { if ( ++error_count >= 20 ) return ZERROR; // SendAttentionString(); continue; } // if ( UnpackHeaderIntoLong( received_header ) != rxbytes ) // { // if ( ++error_count >= 20 ) // return ZERROR; // // SendAttentionString(); // continue; // } moredata: switch ( c = ReadDataFrame( buffer, 1024 ) ) { case ZCAN: error( "ReceiveSingleFile: ReadData returned %d", c ); return ZERROR; case ZERROR: if ( ++error_count >= 20 ) { error( "ReceiveSingleFile: ReadData " "returned %d", c ); return ZERROR; } // SendAttentionString(); continue; case TIMEOUT: if ( ++error_count >= 20 ) { error( "ReceiveSingleFile: ReadData " "returned %d", c ); return ZERROR; } continue; case GOTCRCW: error_count = 0; fwrite( buffer, 1, Rxcount, file ); rxbytes += Rxcount; m_ByteTransfer = rxbytes; PackLongIntoHeader( rxbytes ); SendHexHeader( 4, ZACK, transmitted_header ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: send ZACK:rxbytes = %d"), __LINE__, GetTickCount() - dwBegin, rxbytes); PrintLogLast(strLogTmp); //log SendChar( XON ); SendCharToComm(); goto nxthdr; case GOTCRCQ: error_count = 0; fwrite( buffer, 1, Rxcount, file ); rxbytes += Rxcount; m_ByteTransfer = rxbytes; PackLongIntoHeader( rxbytes ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d \t\t\t\t\t\tRUN: send ZACK:rxbytes = %d"), __LINE__, GetTickCount() - dwBegin, rxbytes); PrintLogLast(strLogTmp); //log SendHexHeader( 4, ZACK, transmitted_header ); goto moredata; case GOTCRCG: error_count = 0; fwrite( buffer, 1, Rxcount, file ); rxbytes += Rxcount; m_ByteTransfer = rxbytes; goto moredata; case GOTCRCE: error_count = 0; fwrite( buffer, 1, Rxcount, file ); rxbytes += Rxcount; m_ByteTransfer = rxbytes; goto nxthdr; } // return ZERROR; } } #ifdef _MSC_VER return 0; // MSC 7.0 generates an error w/o this line, // although it can never be reached #endif } // This routine just has to decide whether to send the binary // header using CRC-16 or CRC-32. After that it just spits out // the data. void Zmodem::SendBinaryHeader( int length, int type, char *header ) { CString strLogTmp = _T(""); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tSend: %d"), __LINE__, GetTickCount() - dwBegin, type); PrintLogLast(strLogTmp); //log int i; Crc32 crc32( 0xFFFFFFFFL ); unsigned long crc32val; Crc16 crc16( 0 ); status( "SendBinaryHeader: %d %s %lx", length, frametypes[ type + 4 ], UnpackHeaderIntoLong( header ) ); SendChar( ZPAD ); SendChar( ZDLE ); if ( receiver_wants_crc32 ) { SendChar( ZBIN32 ); SendEncodedChar( type ); crc32.update( type ); for ( i = 0; i < length; i++ ) { crc32.update( 0xff & header[ i ] ); SendEncodedChar( header[ i ] ); } crc32val = ~crc32.value(); for ( i = 0 ; i < 4 ; i++ ) { SendEncodedChar( (int) crc32val ); crc32val >>= 8; } } else { SendChar( ZBIN ); SendEncodedChar( type ); crc16.update( type ); for ( i = 0 ; i < length ; i++ ) { SendEncodedChar( header[ i ] ); crc16.update( header[ i ] & 0xff ); } crc16.update( 0 ); crc16.update( 0 ); SendEncodedChar( crc16.value() >> 8 ); SendEncodedChar( crc16.value() ); } SendCharToComm(); } // Sending the hex header involves no decisions whatsoever. void Zmodem::SendHexHeader( int len, int type, char *header ) { CString strLogTmp = _T(""); if (type == ZRPOS) { //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tSend:%d\tZrposIndex = %d"), __LINE__, GetTickCount() - dwBegin, type,nIndex); PrintLogLast(strLogTmp); //log nIndex++; } else { //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tSend: %d"), __LINE__, GetTickCount() - dwBegin, type); PrintLogLast(strLogTmp); //log } int n; Crc16 crc( 0 ); status( "SendHexHeader: %d %s %lx", len, frametypes[type+4], UnpackHeaderIntoLong( header ) ); SendChar( ZPAD ); SendChar( ZPAD ); SendChar( ZDLE ); SendChar( ZHEX ); SendHexEncodedChar( type ); crc.update( type ); for ( n = 0 ; n < len ; n++ ) { SendHexEncodedChar( header[ n ] ); crc.update( 0xff & header[ n ] ); } crc.update( 0 ); crc.update( 0 ); SendHexEncodedChar( crc.value() >> 8 ); SendHexEncodedChar( crc.value() ); SendChar( CR ); SendChar( LF | 0x80 ); if ( type != ZFIN && type != ZACK ) SendChar( XON ); SendCharToComm(); } // Reading in headers and data subpackets is a relatively // difficult job. The next four routines combine to read in // headers. The first routine dispatches one of the next three, // depending on what the header type is. int Zmodem::ReadHeader( char *header ) { int c; int n; int cancount; Settings settings; ReadSettings( settings ); n = 1400; n += ( settings.BaudRate > 19200L ) ? 19200 : (int) settings.BaudRate; startover: cancount = 0; again: switch ( c = ReadChar( 10000L ) ) { case TIMEOUT: goto finished; case CAN: gotcan: if ( ++cancount >= 5 ) { c = ZCAN; goto finished; } switch ( c = ReadChar( 100 ) ) { case TIMEOUT: goto again; case ZCRCW: switch ( ReadChar( 100 ) ) { case TIMEOUT: c = ZERROR; goto finished; default: goto agn2; } default: break; case CAN: if ( ++cancount >= 5 ) { c = ZCAN; goto finished; } goto again; } default: agn2: if ( --n == 0 ) { c = GARBAGE_COUNT; goto finished; } goto startover; case ZPAD | 0x80: /* This is what we want. */ case ZPAD: break; } cancount = 0; splat: switch ( c = ReadUnencodedByte() ) { case ZPAD: goto splat; case TIMEOUT: goto finished; default: goto agn2; case ZDLE: /* This is what we want. */ break; } c = ReadUnencodedByte(); switch ( c ) { case ZBIN32: current_frame_uses_crc32 = 1; c = ReadBinaryHeaderCRC32( header ); break; case TIMEOUT: goto finished; case ZBIN: current_frame_uses_crc32 = 0; c = ReadBinaryHeaderCRC16( header ); break; case ZHEX: current_frame_uses_crc32 = 0; c = ReadHexHeaderCRC16( header ); break; case CAN: goto gotcan; default: goto agn2; } received_file_position = header[ ZP3 ] & 0xff; received_file_position <<= 8; received_file_position += header[ ZP2 ] & 0xff; received_file_position <<= 8; received_file_position += header[ ZP1 ] & 0xff; received_file_position <<= 8; received_file_position += header[ ZP0 ] & 0xff; finished: switch ( c ) { case GOTCAN: c = ZCAN; /* **** FALL THRU TO **** */ case ZNAK: case ZCAN: case ZERROR: case TIMEOUT: case GARBAGE_COUNT: error( "Got %s", frametypes[ c + 4 ] ); /* **** FALL THRU TO **** */ default: if ( c >= -4 && c <= 22 ) error( "ReadHeader: %s %lx", frametypes[ c + 4 ], received_file_position ); else error( "ReadHeader: %d %lx", c, received_file_position ); } return c; } // At this point most of the hard work has been done. The next //three routines just read in the type of header and the data // associated with it, then check to see if the CRC is correct. int Zmodem::ReadBinaryHeaderCRC16( char *header ) { int c; int i; Crc16 crc( 0 ); int header_type; CString strLogTmp=_T(""); if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; header_type = c; //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead: %d\t\t\t\tin ReadBinaryHeaderCRC16"), __LINE__, GetTickCount() - dwBegin, header_type); PrintLogLast(strLogTmp); //log crc.update( c ); for ( i = 0 ; i < 4 ; i++ ) { if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; crc.update( c ); header[ i ] = (char) c; } if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; crc.update( c ); if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; crc.update( c ); if ( crc.value() & 0xFFFF ) { error( "Bad CRC" ); return ZERROR; } return header_type; } int Zmodem::ReadBinaryHeaderCRC32( char *header ) { int c; int i; Crc32 crc( 0xFFFFFFFFL ); int header_type; CString strLogTmp=_T(""); if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; header_type = c; //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead: %d\t\t\t\tin ReadBinaryHeaderCRC32"), __LINE__, GetTickCount() - dwBegin, header_type); PrintLogLast(strLogTmp); //log crc.update( c ); for ( i = 0 ; i < 4 ; i++ ) { if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; crc.update( c ); header[ i ] = (char) c; } for ( i = 0; i < 4 ; i++ ) { if ( ( c = ReadEncodedByte() ) & ~0xff ) return c; crc.update( c ); } if ( crc.value() != 0xDEBB20E3L ) { error( "Bad CRC" ); return ZERROR; } return header_type; } int Zmodem::ReadHexHeaderCRC16( char *header ) { int c; Crc16 crc( 0 ); int i; int header_type; CString strLogTmp = _T(""); if ( ( c = ReadHexByte() ) < 0 ) return c; header_type = c; //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead: %d\t\t\t\tin ReadHexHeaderCRC16"), __LINE__, GetTickCount() - dwBegin, header_type); PrintLogLast(strLogTmp); //log crc.update( c ); for ( i = 0 ; i < 4 ; i++ ) { if ( ( c = ReadHexByte() ) < 0 ) return c; crc.update( c ); header[ i ] = (char) c; } if ( ( c = ReadHexByte() ) < 0 ) return c; crc.update( c ); if ( ( c = ReadHexByte() ) < 0 ) return c; crc.update( c ); if ( crc.value() & 0xFFFF ) { error( "Bad CRC" ); return ZERROR; } switch ( c = ReadChar( 100 ) ) { case CR : case CR | 0x80 : ReadChar( 100 ); } return header_type; } // The next three routines are used to read in binary data // subpackets. This code is somewhat simpler than the code // used to read in a header, mostly because there are fewer // things to go wrong. int Zmodem::ReadDataFrame( char *buffer, int length ) { int typee; CString strLogTmp = _T(""); if ( current_frame_uses_crc32 ) { typee = ReadDataFrameCRC32( buffer, length ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead: %d\t\t\t\tin ReadDataFrame CRC32"), __LINE__, GetTickCount() - dwBegin, typee); PrintLogLast(strLogTmp); //log return typee; } else { typee = ReadDataFrameCRC16( buffer, length ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead: %d\t\t\t\tin ReadDataFrame CRC16"), __LINE__, GetTickCount() - dwBegin, typee); PrintLogLast(strLogTmp); //log return typee; } } int Zmodem::ReadDataFrameCRC32( char *buffer, int length ) { int c; Crc32 crc( 0xFFFFFFFFL ); char *end; int d; CString strLogTmp = _T(""); Rxcount = 0; end = buffer + length; while ( buffer <= end ) { if ( ( c = ReadEncodedByte() ) & ~0xff ) { crcfoo: switch ( c ) { case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: d = c; c &= 0xff; crc.update( c ); if ( ( c = ReadEncodedByte() ) & ~0xff ) goto crcfoo; crc.update( c ); if ( ( c = ReadEncodedByte() ) & ~0xff ) goto crcfoo; crc.update( c ); if ( ( c = ReadEncodedByte() ) & ~0xff ) goto crcfoo; crc.update( c ); if ( ( c = ReadEncodedByte() ) & ~0xff ) goto crcfoo; crc.update( c ); if ( crc.value() != 0xDEBB20E3L ) { error( "Bad CRC" ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead:\t\tCRC32:Bad CRC"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return ZERROR; } Rxcount = (int) ( length - (end - buffer) ); error( "ReadDataFrameCRC32: %d %s", Rxcount, Zendnames[ d - GOTCRCE & 3 ] ); return d; case GOTCAN: error( "Sender Canceled" ); return ZCAN; case TIMEOUT: error( "TIMEOUT" ); return c; default: error( "Garbled data subpacket" ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead:\t\tCRC32:Garbled data subpacket"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return c; } } *buffer++ = (char) c; crc.update( c ); } error( "Data subpacket too long" ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead:\t\tCRC32:subpacket too long"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return ZERROR; } int Zmodem::ReadDataFrameCRC16( char *buffer, int length ) { int c; Crc16 crc( 0 ); char *end; int d; CString strLogTmp = _T(""); Rxcount = 0; end = buffer + length; while ( buffer <= end ) { if ( ( c = ReadEncodedByte() ) & ~0xff ) { crcfoo: switch ( c ) { case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: crc.update( (d = c) & 0xff ); if ( ( c = ReadEncodedByte()) & ~0xff ) goto crcfoo; crc.update( c ); if ( ( c = ReadEncodedByte() ) & ~0xff ) goto crcfoo; crc.update( c ); if ( crc.value() & 0xFFFF ) { error( "Bad CRC"); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead:\t\tCRC16:Bad CRC"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return ZERROR; } Rxcount = (int) ( length - ( end - buffer ) ); error( "ReadDataFrame: %d %s", Rxcount, Zendnames[ d - GOTCRCE & 3 ] ); return d; case GOTCAN: error( "Sender Canceled" ); return ZCAN; case TIMEOUT: error( "TIMEOUT" ); return c; default: error( "Garbled data subpacket" ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead:\t\tCRC16:Garbled data subpacket"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return c; } } *buffer++ = (char) c; crc.update( c ); } error( "Data subpacket too long" ); //log strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead:\t\tCRC16:subpacket too long"), __LINE__, GetTickCount() - dwBegin); PrintLogLast(strLogTmp); //log return ZERROR; } // The attention string processor has to process a couple of // special characters used to sleep and send breaks. void Zmodem::SendAttentionString() { int i = 0; int c; long timer; while ( TXSpaceUsed() > 0 ) //port->IdleFunction(); while ( ( c = attention_string[ i++ ] ) != 0 ) { switch ( c ) { case 0xde : timer = ReadTime() + 1000L; while ( ReadTime() < timer ) // port->IdleFunction(); break; case 0xdd : //port->Break(); break; default: SendChar( c ); } } } // This function is called by the receiver before exiting. void Zmodem::AckZFIN( void ) { int n; status( "AckZFIN" ); PackLongIntoHeader( 0L ); for ( n = 0 ; n < 7 ; n++ ) { /*port->FlushRXBuffer();*/ SendHexHeader( 4, ZFIN, transmitted_header ); switch ( ReadChar( 10000L ) ) { case 'O': ReadChar( 120 ); /* Discard 2nd 'O' */ status( "AckZFIN complete" ); return; case TIMEOUT: default: break; } } } // This utility routine has to scan the incoming data subpacket // for the file name and length. int Zmodem::OpenInputFile( CString Path, char *data ) { CString szFileName(data); Path+=szFileName; m_FileSize = GetFileSize(data + szFileName.GetLength() + 1);//这个地方把文件名之后的信息传进去 //log CString strLogTmp = _T(""); // strcpy( file_name, data ); strLogTmp.Empty(); strLogTmp.Format(_T("[%d] Duration: %d\t\t\t\t\t\tRead: %s"), __LINE__, GetTickCount() - dwBegin,data); PrintLogLast(strLogTmp); //log strcpy( file_name, data ); if (sscanf(data+strlen(data)+1, "%ld", &file_length ) < 1 ) file_length = -1L; file = fopen( Path.GetBuffer(Path.GetLength()), "wb" ); if (NULL == file) { DWORD dwErrNo = GetLastError(); CString strTmp = _T(""); if (LANG_ZHCN == g_iUILanguage) { strTmp.Format(_T("[%d] 打开文件失败!, 错误码 = %d"), __LINE__, dwErrNo); AfxMessageBox(strTmp.GetBuffer(0)); } else { strTmp.Format(_T("[%d] Open file failed!, error = %d"), __LINE__, dwErrNo); MessageBoxEx(NULL, strTmp, STRING_MESSAGEBOXEX_TITLE, MB_OK, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); } PrintLogLast(strTmp); return ZERROR; } return OK; } // File position values are longs packed into a four byt header. // The following two routines are resposible for packing and // unpacking the data. void Zmodem::PackLongIntoHeader( long header_data ) { transmitted_header[ ZP0 ] = (char) header_data; transmitted_header[ ZP1 ] = (char) ( header_data >> 8 ); transmitted_header[ ZP2 ] = (char) ( header_data >> 16 ); transmitted_header[ ZP3 ] = (char) ( header_data >> 24 ); } long Zmodem::UnpackHeaderIntoLong( char *header ) { long l; l = header[ ZP3 ] & 0xff; l = ( l << 8 ) | ( header[ ZP2 ] & 0xff ); l = ( l << 8 ) | ( header[ ZP1 ] & 0xff ); l = ( l << 8 ) | ( header[ ZP0 ] & 0xff ); return l; } // Hex headers need to send data in hex format. void Zmodem::SendHexEncodedChar( int c ) { static char *digits = "0123456789abcdef"; SendChar( digits[ ( c & 0xF0 ) >> 4 ] ); SendChar( digits[ c & 0xF ] ); } // This routine handles all the escape sequences necessary to send // control characters. void Zmodem::SendEncodedChar( int c ,bool b) { if ( c & 0x60 ) SendChar( last_char_sent = c ,b); else { switch ( c &= 0xff ) { case CR : case CR | 0x80 : if ( ( last_char_sent & 0x7f ) != '@' ) { SendChar( last_char_sent = c ,b); break; } // else fall through case ZDLE : case DLE : case XON : case XOFF : case DLE | 0x80 : case XON | 0x80 : case XOFF | 0x80 : SendChar( ZDLE ,b); c ^= 0x40; SendChar( last_char_sent = c ,b); break; default: SendChar( last_char_sent = c ,b); } } } // Read a byte, taking into account escape sequences, and checking // for the 5*CAN abort sequence. int Zmodem::ReadEncodedByte( void ) { int c; for ( ; ; ) { c = ReadChar( 10000L ); if ( c == ZDLE ) break; switch ( c ) { case XON : case XON | 0x80 : case XOFF : case XOFF | 0x80 : break; default: return c; } } for ( ; ; ) { if ( ( c = ReadChar( 10000L ) ) < 0 ) return c; if ( c == CAN && ( c = ReadChar( 10000L ) ) < 0 ) return c; if ( c == CAN && ( c = ReadChar( 10000L ) ) < 0 ) return c; if ( c == CAN && ( c = ReadChar( 10000L ) ) < 0 ) return c; switch ( c ) { case CAN: return GOTCAN; case ZCRCE: case ZCRCG: case ZCRCQ: case ZCRCW: return c | GOTFLAG; case ZRUB0: return 0x7f; case ZRUB1: return 0xff; case XOFF : case XOFF | 0x80 : case XON : case XON | 0x80 : break; default: if ( ( c & 0x60 ) == 0x40 ) return ( c ^ 0x40 ); else return ZERROR; } } } // This routine reads a raw data byte, throws out the parity bit and // ignores handshaking characters. int Zmodem::ReadUnencodedByte( void ) { int c; for ( ; ; ) { if ( ( c = ReadChar( 10000L ) ) < 0 ) return c; switch ( c &= 0x7f ) { case XON: case XOFF: continue; default: return c; } } } // When reading Hex headers, we need to convert hex values to a // usable format. int Zmodem::ReadHexByte( void ) { int c; int n; if ( ( c = ReadUnencodedByte() ) < 0 ) return c; n = c - '0'; if ( n > 9 ) n -= ( 'a' - ':' ); if ( n & ~0xF ) return ZERROR; if ( ( c = ReadUnencodedByte( )) < 0 ) return c; c -= '0'; if ( c > 9 ) c -= ( 'a' - ':' ); if ( c & ~0xF ) return ZERROR; c += ( n << 4 ); return c; } // The sender needs to get the RINIT frame before it can start // sending the file. This routine takes care of that. int Zmodem::GetRinitHeader( void ) { int i; for ( i = 0 ; i < 10 ; i++ ) { switch ( ReadHeader( received_header ) ) { case ZCHALLENGE: // Echo receiver's challenge number PackLongIntoHeader( received_file_position ); SendHexHeader( 4, ZACK, transmitted_header ); continue; case ZCOMMAND: // They didn't see our ZRQINIT PackLongIntoHeader( 0L ); SendHexHeader( 4, ZRQINIT, transmitted_header ); continue; case ZRINIT: receiver_wants_crc32 = received_header[ ZF0 ] & CANFC32; receiver_buffer_length = ( received_header[ ZP0 ] & 0xff ) + ( ( received_header[ ZP1 ] & 0xff ) << 8 ); return OK; case ZCAN: case TIMEOUT: return ZERROR; case ZRQINIT: if ( received_header[ ZF0 ] == ZCOMMAND ) continue; default: SendHexHeader( 4, ZNAK, transmitted_header ); continue; } } return ZERROR; } // This routine sends a data subpacket, which is used here to send // file names and file data. void Zmodem::SendDataFrame( char *buffer, int length, int frameend ) { Crc32 crc32( 0xFFFFFFFFL ); unsigned long crc32val; Crc16 crc16( 0 ); int i; status( "SendDataFrame: %d %s", length, Zendnames[ frameend - ZCRCE & 3 ] ); int nnlen = 0; if ( receiver_wants_crc32 ) { for ( i = 0 ; i < length ; i++ ) { SendEncodedChar( buffer[ i ] ,TRUE); crc32.update( buffer[ i ] & 0xff ); } SendChar( ZDLE ,TRUE); SendChar( frameend ,TRUE); crc32.update( frameend ); crc32val = ~crc32.value(); for ( i = 0 ; i < 4 ; i++ ) { SendEncodedChar( (int) crc32val ,TRUE); crc32val >>= 8; } } else { for ( i = 0 ; i < length ; i++ ) { SendEncodedChar( buffer[ i ] ,TRUE); crc16.update( buffer[ i ] & 0xff ); } SendChar( ZDLE ,TRUE); SendChar( frameend ,TRUE); crc16.update( frameend ); crc16.update( 0 ); crc16.update( 0 ); SendEncodedChar( crc16.value() >> 8 ,TRUE); SendEncodedChar( crc16.value() ,TRUE); } if ( frameend == ZCRCW ) SendChar( XON ,TRUE); SendCharToComm(); } // The sender sends a ZFIN frame just before exiting. void Zmodem::SendZFIN( void ) { for ( ; ; ) { PackLongIntoHeader( 0L ); SendHexHeader( 4, ZFIN, transmitted_header ); switch ( ReadHeader( received_header ) ) { case ZFIN: SendChar( 'O'); SendChar( 'O'); SendCharToComm(); case ZCAN: case TIMEOUT: return; } } } int Zmodem::SyncWithReceiver( int flag ) { int c; for ( ; ; ) { c = ReadHeader( received_header ); switch ( c ) { case ZCAN: case ZABORT: case ZFIN: case TIMEOUT: return ZERROR; case ZRPOS: if ( fseek( file, received_file_position, SEEK_SET ) ) return ZERROR; file_at_eof = 0; byte_count = last_reported_position = transmitted_file_position = received_file_position; last_sync_position = received_file_position; return c; case ZACK: last_reported_position = received_file_position; if ( flag || transmitted_file_position == received_file_position ) return ZACK; continue; case ZRINIT: case ZSKIP: fclose( file ); return c; case ZERROR: default: SendBinaryHeader( 4, ZNAK, transmitted_header ); continue; } } } long Zmodem::ReadTime( void ) { return 0; } //---------------------------------------------------------------------------------- int Zmodem::ReadBuff(long timeout) { /*暂时注释用新写法来写 int nCountError; nCountError = 0; if (m_BuffCount > 0) { m_BuffCount -= 1; if(m_BuffCount == 0) { return (*str & 0377); } else { return (*str++ & 0377); } } m_BuffCount = 0; memset(buff,0,BUFFSIZE); do { port->ZmodemReceiveDataDirectly(buff,&m_BuffCount); if (m_BuffCount != 0) { nCountError = 0; str = buff; break; } else { nCountError++; Sleep(50); } }while(nCountError < 40); if( nCountError == 40 ) { return TIMEOUT; } m_BuffCount -=1; if(m_BuffCount == 0) { return (*str & 0377); } else { return (*str++ & 0377); } */ int nCountError = 0; CString strTmp = _T(""); ClearReadBuf(); do { port->ZmodemReceiveDataDirectly(buff,&m_BuffCount); if (0 == m_BuffCount) { nCountError++; Sleep(50); } else if (m_BuffCount > BUFFSIZE) { strTmp.Format(_T("[%d] read buffersize is too big than 8K, read_buffer_size = %d"), __LINE__, m_BuffCount); PrintLogLast(strTmp); return 1; } else { return 1; } }while(nCountError < 240); strTmp.Format(_T("[%d] read buffersize is TIMEOUT"), __LINE__); PrintLogLast(strTmp); return TIMEOUT; } void Zmodem::ReadSettings(Settings ©) { copy.BaudRate = 115200; copy.Parity = 'N'; copy.WordLength = 8; copy.StopBits = 1; copy.Dtr = 1; copy.Rts = 0; copy.XonXoff = 0; copy.RtsCts = 0; copy.DtrDsr = 0; } int Zmodem::TXSpaceUsed( void ) { return (m_BuffCount - BUFFSIZE); } int Zmodem::GetFileSize(char *buffer) { char cFileSize[64]; memset(cFileSize, 0, 64); int nIndex = 0; while(buffer[nIndex] != ' ') { cFileSize[nIndex] = buffer[nIndex]; nIndex++; } return atoi(cFileSize); } void Zmodem::SendCharToComm() { port->ZmodemSendDataDirectly(m_WriteBuff, m_WriteBuffLength); memset(m_WriteBuff, 0, 8192); m_WriteBuffLength = 0; } void Zmodem::ClearReadBuf() { m_BuffCount = 0; m_nBufReadIndex = 0; memset(buff,0,BUFFSIZE); } void Zmodem::PrintLog(char *chPrtlog, int nLogLen) { ASSERT(NULL != chPrtlog); fwrite(chPrtlog,nLogLen,1,zmodemLog); fflush(zmodemLog); } void Zmodem::PrintLogLast(CString& strLog) { if (NULL == zmodemLog) { if (LANG_ZHCN == g_iUILanguage) AfxMessageBox(_T("无法写入device_log,日志文件点为空")); else MessageBoxEx(NULL, _T("Can't write device_log, log File point is NULL"), _T("LOG_ERROR"), MB_OK, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); return; } CString strOutPut = _T(""); strOutPut.Format(_T("%04d-%02d-%02d %02d:%02d:%02d.%03d %s \r\n"),g_sysCurTime.wYear, g_sysCurTime.wMonth, g_sysCurTime.wDay, g_sysCurTime.wHour, g_sysCurTime.wMinute, g_sysCurTime.wSecond, g_sysCurTime.wMilliseconds, strLog.GetBuffer(0)); fwrite(strOutPut.GetBuffer(0), 1, strOutPut.GetLength(), zmodemLog); fflush(zmodemLog); } int Zmodem::GetFileSize() { return m_FileSize; } int Zmodem::GetTransferPosit() { return m_ByteTransfer; } BOOL Zmodem::SendCancelString() { char chCancelString[] = { CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, BS, BS, BS, BS, BS, BS, BS, BS, BS, BS }; return port->ZmodemSendDataDirectly( chCancelString, sizeof(chCancelString));//tmp }