001package jmri.jmrix.pricom.downloader;
002
003import java.io.BufferedInputStream;
004import java.io.File;
005import java.io.FileInputStream;
006import java.io.IOException;
007import java.io.InputStream;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * Support for reading PRICOM ".pdi" files
013 * <p>
014 * The PRICOM format documentation is Copyright 2003, 2005, PRICOM Corp. They
015 * have kindly given permission for this use.
016 *
017 * @author Bob Jacobsen Copyright (C) 2005
018 */
019public class PdiFile {
020
021    public PdiFile(File file) {
022        this.file = file;
023    }
024
025    File file;
026    private InputStream buffIn;
027
028    String comment = "";
029    int commentLength;
030
031    int lastAddress;
032    int address;
033
034    int fileLength;
035
036    public void open() throws IOException {
037        InputStream stream = new BufferedInputStream(new FileInputStream(file));
038        open(stream);
039    }
040
041    public void open(InputStream stream) throws IOException {
042        buffIn = stream;
043
044        // get comment length, comment
045        int high = (buffIn.read() & 0xFF);
046        int low = (buffIn.read() & 0xFF);
047        commentLength = high * 256 + low;
048
049        StringBuffer buffer = new StringBuffer();
050
051        // Note the count is decremented by two in the following.
052        // Apparently, the comment length field includes it's own
053        // two bytes in the count
054        for (int i = 0; i < (commentLength - 2); i++) {
055            int next = buffIn.read();
056            if (next == 0x0d) {
057                buffer.append("\n");
058            } else if (next != 0x0a) {
059                buffer.append((char) next);
060            }
061        }
062
063        comment = buffer.toString();
064
065        // get data base address
066        high = (buffIn.read() & 0xFF);
067        low = (buffIn.read() & 0xFF);
068        address = high * 256 + low;
069        if (log.isDebugEnabled()) {
070            log.debug("address {} {}", high, low);
071        }
072
073        // get last address to write
074        high = (buffIn.read() & 0xFF);
075        low = (buffIn.read() & 0xFF);
076        lastAddress = high * 256 + low;
077        if (log.isDebugEnabled()) {
078            log.debug("length {} {}", high, low);
079        }
080
081        fileLength = (int) file.length() - 6 - commentLength;
082
083        if (log.isDebugEnabled()) {
084            log.debug("lengths: file {}, comment {}, data {}", (int) file.length(), commentLength, lastAddress);
085        }
086    }
087
088    /**
089     * Return the comment embedded at the front of the file.
090     * @return file comment.
091     */
092    public String getComment() {
093        return comment;
094    }
095
096    int length() {
097        return fileLength;
098    }
099
100    /**
101     * Get the next n bytes for transmission to the device
102     *
103     * @param n number of data bytes to include
104     * @return byte buffer, starting with address info and containing data, but
105     *         not CRC
106     */
107    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
108        justification = "API defined by Pricom docs")
109    public byte[] getNext(int n) {
110        byte[] buffer = new byte[n + 3 + 2]; // 3 at front, 2 at back for CRC
111        int rd;
112
113        // load header
114        if (n == 128) {
115            buffer[0] = 60;
116        } else {
117            buffer[0] = 59;
118        }
119
120        buffer[1] = (byte) ((address >> 8) & 0xFF);
121        buffer[2] = (byte) (address & 0xFF);
122        address = address + n;
123
124        for (int i = 0; i < n + 2; i++) {
125            buffer[3 + i] = 0;  // clear data section
126        }
127        try {
128            // fill data
129            for (int i = 0; i < n; i++) {
130                rd = buffIn.read();                         // read from file, -1=EOF
131                if (rd == -1) {
132                    return null;                  // return NULL pointer
133                }
134                buffer[3 + i] = (byte) (rd & 0xFF);             // tuck the byte
135            }
136        } catch (IOException e) {
137            log.error("IO exception reading file", e);
138        }
139        return buffer;
140    }
141
142    private final static Logger log = LoggerFactory.getLogger(PdiFile.class);
143}