/* * To change this template, choose Tools | Templates * and open the template lastInByte the editor. */ //package ymodem.transfer; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.lang.Thread; /** * * @author maziar */ /**! ymodem protocoll YMODEM 1K SENDER RECEIVER "sb -k foo.*<CR>" "sending in batch mode etc." C (command:rb) SOH 00 FF foo.c NUL "fileLength" NUL[xxx] CRC CRC ACK C STX 02 FD Data[1024] CRC CRC ACK SOH 03 FC Data[128] CRC CRC ACK SOH 04 FB Data[100] CPMEOF[28] CRC CRC ACK EOT NAK EOT ACK C SOH 00 FF NUL[128] CRC CRC ACK * @author OOBD.org * @version 1.0 */ public class YModem1K { private boolean isFirstSector = true; static boolean isEnd = false; static int counter = 0; private OutputStream outputStream; private InputStream inputStream; private InputStream fileInputStream; private static String feedback; private byte lastInByte; byte blockNumber = 1; public static boolean read = false; private CRC16 crc = new CRC16(); public static String getFeedback() { return feedback; } private void sendBytes(byte[] data, int len) { try { /* System.err.printf("----- output of %d Bytes -----------\n ", len); for (int i=0; i<len;i++){ System.err.printf("%02X ", data[i]); if (i % 16==0){ System.err.println(); } } System.err.println("------"); */ outputStream.write(data, 0, len); } catch (IOException ex) { System.err.println("Send error"); } } public boolean readByte(Integer timeout) { long starttime = System.currentTimeMillis(); lastInByte = 0; try { while (inputStream.available() < 1) { long time = System.currentTimeMillis(); if ((time - starttime) > timeout) { return false; } try { Thread.sleep(10); //sleep for 100ms } catch (InterruptedException e) {} } int readInt = inputStream.read(); if (readInt >= 0) { lastInByte = (byte) readInt; //System.err.println("Char recv.: 0x" + Integer.toHexString(lastInByte)); return true; } else { lastInByte = (byte) 0; return false; } } catch (IOException ex) { return false; } } public void flush() { try { while (inputStream.available() > 0) { inputStream.read(); } } catch (IOException ex) {} } private boolean waitForReply(Integer timeout, byte expectedReply) { long starttime = System.currentTimeMillis(); readByte(10); while (expectedReply != lastInByte) { long time = System.currentTimeMillis(); if ((time - starttime) > timeout) { System.err.println("timeout while waiting for answer"); return false; } readByte(10); } //System.err.println("WaitForReplay ended"+new Boolean(expectedReply==lastInByte).toString()); return expectedReply == lastInByte; } public boolean ymodemtransfer(InputStream inputStream, OutputStream outputStream, InputStream fileInputStream, String fileName, long fileLength) { this.inputStream = inputStream; this.outputStream = outputStream; this.fileInputStream = fileInputStream; System.err.println("S: start from receiver F: First block +: Block send -: Retry L:Last Block"); boolean retvalue = true; int actState = Constants.STATE_FIRSTSECTOR; while (actState != Constants.STATE_END) { if (actState == Constants.STATE_FIRSTSECTOR) actState = SendFileNameSector(fileName, fileLength); if (actState == Constants.STATE_NEXTSECTOR) actState = sendSector(); if (actState == Constants.STATE_EOT1) { flush(); //System.err.println("SEND EOT 1"); sendBytes(new byte[] { Constants.EOT }, 1); if (readByte(1000) && lastInByte == Constants.ACK) { actState = Constants.STATE_LASTSECTOR; } else { if (lastInByte == Constants.NAK) { actState = Constants.STATE_EOT2; } else { actState = Constants.STATE_ABORT; } } } if (actState == Constants.STATE_EOT2) { flush(); //System.err.println("SEND EOT 2"); sendBytes(new byte[] { Constants.EOT }, 1); if (readByte(1000) && lastInByte == Constants.ACK) { actState = Constants.STATE_LASTSECTOR; } else { actState = Constants.STATE_ABORT; } } if (actState == Constants.STATE_LASTSECTOR) actState = SendFileNameSector(null, 0); // signals EOT if (actState == Constants.STATE_ABORT) { System.err.println("Aborted"); retvalue = false; actState = Constants.STATE_END; } } return retvalue; } private int sendSector() { byte[] sector = new byte[Constants.sizeSectorNonZero]; counter = Constants.MAXERRORS; int bytesRead = 0; int actualBytesRead = 1; byte[] packet = new byte[1029]; byte[] innerData; byte[] header = new byte[3]; while (actualBytesRead != -1 && counter > 0) { counter = Constants.MAXERRORS; bytesRead = 0; try { while (bytesRead < Constants.sizeSectorNonZero && actualBytesRead > 0) { actualBytesRead = fileInputStream.read(sector, bytesRead, Constants.sizeSectorNonZero - bytesRead); if (actualBytesRead > 0) { bytesRead += actualBytesRead; } } } catch (IOException ex) { System.err.println("Error while reading filestream"); //fileInputStream.close(); return Constants.STATE_ABORT; } if (bytesRead > 0) { //System.err.println("Preparing block "+Integer.toString(blockNumber)); lastInByte = Constants.EMPTY; if (bytesRead < Constants.sizeSectorNonZero) { while (bytesRead < Constants.sizeSectorNonZero) { sector[bytesRead] = Constants.CPMEOF; bytesRead++; } } header[0] = Constants.STX; header[1] = (byte) blockNumber; header[2] = (byte)(255 - blockNumber); innerData = new byte[Constants.sizeSectorNonZero]; System.arraycopy(sector, 0, innerData, 0, sector.length); crc.resetInitValue(); crc.CRCCalculate(innerData); System.arraycopy(innerData, 0, packet, 3, innerData.length); System.arraycopy(header, 0, packet, 0, header.length); packet[1027] = crc.getCRCHighByte(); packet[1028] = crc.getCRCLowByte(); while (lastInByte != Constants.ACK && counter > 0) { //System.err.println("Sending block "+Integer.toString(blockNumber)); flush(); sendBytes(packet, 1029); readByte(1000); if (lastInByte != Constants.ACK) { if (lastInByte == Constants.CAN) { try { fileInputStream.close(); } catch (IOException ex) {} return Constants.STATE_ABORT; } else { System.err.print("-"); counter--; } } else { System.err.print("+"); } } blockNumber = blockNumber > 126 ? (byte) - 128 : (byte)(blockNumber + 1); } } try { fileInputStream.close(); } catch (IOException ex) {} if (lastInByte == Constants.ACK) { return Constants.STATE_EOT1; } else { return Constants.STATE_ABORT; } } private int SendFileNameSector(String fileName, long fileSize) { lastInByte = Constants.EMPTY; counter = Constants.MAXERRORS; flush(); while (lastInByte != Constants.C && counter > 0) { waitForReply(10000, Constants.C); //System.err.println("Wait for C"); counter--; } if (lastInByte != Constants.C) { return Constants.STATE_ABORT; } if (fileName != null) { //if this is not the last empty block, print start signal System.err.print("S"); } byte[] packet = new byte[133]; byte[] innerData; byte[] header = new byte[3]; header[0] = Constants.SOH; header[1] = 0; header[2] = (byte) 255; crc.resetInitValue(); innerData = new byte[Constants.sizeSectorZero]; //128 // copy the file name, if not empty // if empty, then send it empty as indication of transfer end if (fileName != null && fileName.length() > 0) { System.arraycopy(fileName.toLowerCase().getBytes(), 0, innerData, 0, fileName.toLowerCase().getBytes().length); // copy the file size System.arraycopy(Long.toString(fileSize).getBytes(), 0, innerData, fileName.toLowerCase().getBytes().length + 1, Long.toString(fileSize).getBytes().length); } System.arraycopy(innerData, 0, packet, 3, innerData.length); System.arraycopy(header, 0, packet, 0, header.length); // Calculate the CRC crc.CRCCalculate(innerData); packet[131] = crc.getCRCHighByte(); packet[132] = crc.getCRCLowByte(); lastInByte = Constants.EMPTY; counter = Constants.MAXERRORS; while (lastInByte != Constants.ACK && counter > 0) { flush(); sendBytes(packet, 133); readByte(1000); //waiting for ACK counter--; } if (fileName != null) { //if this is not the last empty block, then waiting for C readByte(1000); } if (lastInByte == Constants.C) { if (fileName != null && fileName.length() > 0) { System.err.print("F"); return Constants.STATE_NEXTSECTOR; } else { System.err.println("L"); return Constants.STATE_END; } } else { if (fileName == null && lastInByte == Constants.ACK) { System.err.println("L"); return Constants.STATE_END; } else { if (lastInByte == Constants.CAN) { System.err.println("Canceled by remote"); return Constants.STATE_EOT1; } else { System.err.println("Error during First Sector"); return Constants.STATE_ABORT; } } } } public static void main(String[] args) { FileInputStream fileInputStream = null; File file = null; if (args.length != 1) { System.err.println("Usage: ymodem filename"); System.exit(0); } try { fileInputStream = new FileInputStream(args[0]); file = new File(args[0]); } catch (IOException ex) { System.err.println("cant read input file"); System.exit(1); } YModem1K myYModem = new YModem1K(); myYModem.ymodemtransfer(System.in, System.out, fileInputStream, file.getName(), file.length()); } } final class Constants { public static final int poly = 0x1021; public static final int initValueCRCModem = 0x0; public static final byte EMPTY = 0x00; public static final byte SOH = 0x01; public static final byte STX = 0x02; public static final byte EOT = 0x04; public static final byte ACK = 0x06; public static final byte NAK = 0x15; public static final byte CAN = 0x18; public static final byte G = 0x47; public static final byte C = 0x43; public static final byte CPMEOF = 26; //0x1a public static final int sizeSectorZero = 128; //<soh><00><ff><128 data><crchigh><crclow> public static final int sizeSectorNonZero = 1024; public static final int MAXERRORS = 5; public static final int STATE_FIRSTSECTOR = 0; public static final int STATE_NEXTSECTOR = 1; public static final int STATE_EOT1 = 2; public static final int STATE_EOT2 = 3; public static final int STATE_LASTSECTOR = 4; public static final int STATE_ABORT = 99; public static final int STATE_END = 100; } /** * * @author maziar */ /** * RReads in a sequence of bytes and prints out its 16 bit * Cylcic Redundancy Check * 1 + x + x^5 + x^12 + x^16 is irreducible polynomial. * For CRC-CCITT polynomial = 0x1021, Initial Value = 0xffff; * For CRC-16 polynomial = 0xa001, Initial Value = 0xffff; * For CRC-XModem polynomial = 0x1021, Initial Value = 0x0; is used here * @author OOBD.org * @version 1.0 */ class CRC16 { private int intCrcLow; private int intCrcHigh; // get the private int initValue = Constants.initValueCRCModem; //0x0 /**! * Get the low CRC-Bit * @return low CRC-Bit */ public byte getCRCLowByte() { return (byte) intCrcLow; } /**! get the high crc bit * * @return HIgh CRC-Bit */ public byte getCRCHighByte() { return (byte) intCrcHigh; } /**! give the calculate crc * Gives the Calculate the CRC * @return CRC as integer */ public int getCRC() { return initValue; } /**! * Reset the initValue before claculate the crc */ public void resetInitValue() { initValue = Constants.initValueCRCModem; //0x0 } /**! * Calculate the CRC from a Byte array * @param value The byte value which has to ben calculate */ public void CRCCalculate(byte[] value) { for (byte b: value) { for (int i = 0; i < 8; i++) { boolean bit = ((b >> (7 - i) & 1) == 1); boolean c15 = ((initValue >> 15 & 1) == 1); initValue <<= 1; if (c15 ^ bit) initValue ^= Constants.poly; } } initValue &= 0xffff; intCrcHigh = (initValue >> 8) & 0x00ff; //intCrcHigh= (initValue>>8); intCrcLow = initValue & 0xff; /* System.err.println("CRC16 = " +Integer.toHexString(initValue)); System.err.println("Highbit " + intCrcHigh); System.err.println("Lowbit " + intCrcLow); System.err.println("Highbit " + Integer.toHexString(getCRCHighByte())); System.err.println("Lowbit " + Integer.toHexString(getCRCLowByte())); */ } }