diff --git a/src/main/java/cz/jaybee/intelhex/MemoryRegions.java b/src/main/java/cz/jaybee/intelhex/MemoryRegions.java index 91d9802..506b37b 100644 --- a/src/main/java/cz/jaybee/intelhex/MemoryRegions.java +++ b/src/main/java/cz/jaybee/intelhex/MemoryRegions.java @@ -42,8 +42,9 @@ public class MemoryRegions { public void add(long start, long length) { Region prevRegion; + if (regions.size() > 0) { - prevRegion = regions.get(regions.size() - 1); + prevRegion = regions.get(regions.size() - 1); //get last region long nextAddress = prevRegion.getAddressStart() + prevRegion.getLength(); if (nextAddress == start) { prevRegion.incLength(length); diff --git a/src/main/java/cz/jaybee/intelhex/Record.java b/src/main/java/cz/jaybee/intelhex/Record.java index 6fd0dd4..885cfd3 100644 --- a/src/main/java/cz/jaybee/intelhex/Record.java +++ b/src/main/java/cz/jaybee/intelhex/Record.java @@ -35,6 +35,7 @@ public class Record { public int address; public RecordType type; public byte[] data; + public byte checksum; /** * Convert the record to pretty string diff --git a/src/main/java/cz/jaybee/intelhex/cli/HexEditor.java b/src/main/java/cz/jaybee/intelhex/cli/HexEditor.java new file mode 100644 index 0000000..c3cf6a6 --- /dev/null +++ b/src/main/java/cz/jaybee/intelhex/cli/HexEditor.java @@ -0,0 +1,94 @@ +package cz.jaybee.intelhex.cli; + +import cz.jaybee.intelhex.IntelHexException; +import cz.jaybee.intelhex.Parser; +import cz.jaybee.intelhex.listeners.HexWriter; +import cz.jaybee.intelhex.listeners.RangeDetector; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class HexEditor { + + public static void main(String[] args) throws IOException, IntelHexException { + String fileIn = "input.hex"; + String fileOut = "output.hex"; + int address = 0x00; + boolean replace = false; + byte[] data = new byte[0]; + + if (args.length == 0) { + System.out.println("usage:"); + System.out.println(" hexEditor -replace "); + System.out.println(); + return; + } + + if (args.length >= 1) { + fileIn = args[0]; + } + + if (args.length >= 2) { + fileOut = args[1]; + } + + if (args.length >= 3 && "-replace".equals(args[2])) { + replace = true; + + if (args.length >= 4) { + address = Integer.parseInt(args[3].substring(2),16); + } + + if (args.length >= 5) { + data = fromHexString(args[4].substring(2)); + } + } + + + try (FileInputStream is = new FileInputStream(fileIn)) { + OutputStream os = Files.newOutputStream(Paths.get(fileOut)); + Parser parser = new Parser(is); + + // 1st iteration - calculate maximum output range + RangeDetector rangeDetector = new RangeDetector(); + parser.setDataListener(rangeDetector); + parser.parse(); + is.getChannel().position(0); + + // 2nd iteration - actual write of the output + HexWriter hexWriter = new HexWriter(rangeDetector.getFullRangeRegion(), os); + + parser.setDataListener(hexWriter); + parser.parse(); + + if(replace) { + hexWriter.replaceData(address, data); + } + + + //save into file + hexWriter.save(); + + } catch (IOException ex) { + Logger.getLogger(Hex2bin.class.getName()).log(Level.SEVERE, null, ex); + } + + } + + private static byte[] fromHexString(final String encoded) throws IntelHexException { + if ((encoded.length() % 2) != 0) + throw new IntelHexException("Input string must contain an even number of characters"); + + final byte[] result = new byte[encoded.length()/2]; + final char[] enc = encoded.toCharArray(); + + for (int i = 0; i < enc.length; i += 2) { + result[i/2] = (byte) Integer.parseInt(String.valueOf(enc[i]) + enc[i + 1], 16); + } + + return result; + } +} diff --git a/src/main/java/cz/jaybee/intelhex/cli/PrintBin.java b/src/main/java/cz/jaybee/intelhex/cli/PrintBin.java new file mode 100644 index 0000000..fd5cd7c --- /dev/null +++ b/src/main/java/cz/jaybee/intelhex/cli/PrintBin.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2015, Jan Breuer All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package cz.jaybee.intelhex.cli; + +import cz.jaybee.intelhex.IntelHexException; +import cz.jaybee.intelhex.Parser; +import cz.jaybee.intelhex.listeners.RangeDetector; +import cz.jaybee.intelhex.listeners.Writer; + +import java.io.*; +import java.nio.file.Paths; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class PrintBin { + + static long a = 0; + + + public static void main(String[] args) throws IOException, IntelHexException { + + String fileIn = "input.hex"; + + if (args.length == 0) { + System.out.println("Just print binary data from IntelHex file to console"); + System.out.println(); + System.out.println("usage:"); + System.out.println(" PrintBin "); + System.out.println(); + return; + } + + if (args.length >= 1) { + fileIn = args[0]; + } + + // create input stream of some IntelHex data + FileInputStream is = new FileInputStream(Paths.get(fileIn).toFile()); + + // create IntelHexParserObject + Parser parser = new Parser(is); + + // 1st iteration - calculate maximum output range + RangeDetector rangeDetector = new RangeDetector(); + parser.setDataListener(rangeDetector); + parser.parse(); + is.getChannel().position(0); + + // register parser listener + parser.setDataListener(new Writer(rangeDetector.getFullRangeRegion()) { + + + @Override + public void write() throws IOException { + printHumanMessage(buffer, " "); + } + + @Override + public void eof() { + try { + write(); + } catch (IOException e) { + Logger.getLogger(Hex2bin.class.getName()).log(Level.SEVERE, null, e); + } + } + }); + + System.out.println(); + parser.parse(); + + } + + public static void printHumanMessage(byte[] inputArray, String determiner) { + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < inputArray.length; i++) { + String hex = Integer.toHexString(inputArray[i] & 0xFF).toUpperCase().trim(); + sb.append(new String(new char[2 - hex.length()]).replace('\0', '0')) + .append(hex) + .append(determiner); + + if((i+1)%16==0) { + System.out.println(sb); + sb.delete(0, sb.length()); + } + } + + if (sb.length() > 0) { + System.out.println(sb); + } + } +} diff --git a/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java b/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java index 870781d..7ca41e1 100644 --- a/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java +++ b/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java @@ -1,16 +1,16 @@ /** * Copyright (c) 2015, Jan Breuer All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + *

* * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - * + *

* * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -28,6 +28,7 @@ import cz.jaybee.intelhex.DataListener; import cz.jaybee.intelhex.MemoryRegions; import cz.jaybee.intelhex.Region; + import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; @@ -39,55 +40,29 @@ * * @author Jan Breuer */ -public class BinWriter implements DataListener { +public class BinWriter extends Writer { - private final Region outputRegion; - private final OutputStream destination; - private final byte[] buffer; - private final MemoryRegions regions; - private long maxAddress; - private final boolean minimize; + final boolean minimize; public BinWriter(Region outputRegion, OutputStream destination, boolean minimize) { - this.outputRegion = outputRegion; - this.destination = destination; + super(outputRegion, destination); this.minimize = minimize; - this.buffer = new byte[(int) (outputRegion.getLength())]; - Arrays.fill(buffer, (byte) 0xFF); - regions = new MemoryRegions(); - maxAddress = outputRegion.getAddressStart(); } @Override - public void data(long address, byte[] data) { - regions.add(address, data.length); - - if ((address >= outputRegion.getAddressStart()) && (address <= outputRegion.getAddressEnd())) { - int length = data.length; - if ((address + length) > outputRegion.getAddressEnd()) { - length = (int) (outputRegion.getAddressEnd() - address + 1); - } - System.arraycopy(data, 0, buffer, (int) (address - outputRegion.getAddressStart()), length); - - if (maxAddress < (address + data.length -1)) { - maxAddress = address + data.length - 1; - } + public void write() throws IOException { + if (!minimize) { + maxAddress = outputRegion.getAddressEnd(); } + destination.write(buffer, 0, (int) (maxAddress - outputRegion.getAddressStart() + 1)); } @Override - public void eof() { + public void eof() { try { - if (!minimize) { - maxAddress = outputRegion.getAddressEnd(); - } - destination.write(buffer, 0, (int)(maxAddress - outputRegion.getAddressStart() + 1)); + write(); } catch (IOException ex) { Logger.getLogger(BinWriter.class.getName()).log(Level.SEVERE, null, ex); } } - - public MemoryRegions getMemoryRegions() { - return regions; - } } diff --git a/src/main/java/cz/jaybee/intelhex/listeners/HexWriter.java b/src/main/java/cz/jaybee/intelhex/listeners/HexWriter.java new file mode 100644 index 0000000..565c678 --- /dev/null +++ b/src/main/java/cz/jaybee/intelhex/listeners/HexWriter.java @@ -0,0 +1,194 @@ +package cz.jaybee.intelhex.listeners; + +import cz.jaybee.intelhex.IntelHexException; +import cz.jaybee.intelhex.Record; +import cz.jaybee.intelhex.RecordType; +import cz.jaybee.intelhex.Region; + +import java.io.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class HexWriter extends Writer { + + public HexWriter(Region outputRegion, OutputStream destination) { + super(outputRegion, destination); + } + + public void appendPrefix(byte[] data) throws IntelHexException { + byte[] newBuffer = new byte[buffer.length + data.length]; + + //copy whole buffer to bigger + System.arraycopy(buffer, 0, newBuffer, data.length, buffer.length); + buffer = newBuffer; + + replaceData(0, data); + } + + public void replaceData(int address, byte[] data) throws IntelHexException { + + if(address < 0 ) { + throw new IntelHexException("Replace error. Invalid address. Address=(" + address + ") < 0"); + } + + if(address + data.length > buffer.length) { + throw new IntelHexException("Replace error. Invalid address. Address+data.length=(" + address+data.length + ") > buffer length=(" + buffer.length+")"); + } + + System.arraycopy(data, 0, buffer, address, data.length); + } + + @Override + public void eof() { + //do nothing when eof is detected + } + + @Override + public void write() throws IOException { + + try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(destination))) { + + //cuts data to 16 bytes blocks + //if 0xFF are redundant, skip it + int i = 0; + while (i < buffer.length) { + byte[] recordData = new byte[16]; //line block of data + + int len = 0; + boolean detected0xFF = false; + boolean first0xFF = true; + + int skipIndex = 0; + for (int j = 0; j < 16; j++, skipIndex++) { + + if (buffer[i + j] != (byte) 0xff) { + + if (detected0xFF) { + //if 0xFF is detected + //and then NON 0xFF is detected + //go out of for and print line + break; + } + + recordData[j] = buffer[i + j]; + len++; + + } else { + + + if (first0xFF) { + first0xFF = false; + if (buffer[i + j + 1] != (byte) 0xff) { + //if first 0xFF is detected and after it + //is normal non 0xFF, then 0xFF doesn't skip + recordData[j] = buffer[i + j]; + len++; + continue; + } + } + + detected0xFF = true; + } + } + + Record record = new Record(); + record.length = len; + record.address = i; + record.data = recordData; + record.type = RecordType.DATA; + sumCalculator(record); + + if (len > 0) { + i += len; + bw.write(getLine(record)); + continue; + } + + //if len == 0, meaning only 0xFF lines + //so skip it + i += skipIndex; + } + + + //EOF record + Record record = new Record(); + record.length = 0; + record.address = 0x0000; + record.data = new byte[0]; + record.type = RecordType.EOF; + sumCalculator(record); + + bw.write(getLine(record)); + } + } + + public void sumCalculator(Record record) { + int sum = 0; + + //data + for (int i = 0; i < record.length; i++) { + sum += record.data[i] & 0xff; + } + + //address + sum += record.address & 0xff; + sum += (record.address >> 8) & 0xff; + + //type + sum += record.type.toInt() & 0xff; + + //length + sum += record.length; + + //sum + record.checksum = (byte) (~(sum & 0xff) + 1); + } + + private byte[] intTo1BytesArr(int data) { + return new byte[]{ + (byte) ((data) & 0xff), + }; + } + + private byte[] intTo2BytesArr(int data) { + return new byte[]{ + (byte) ((data >> 8) & 0xff), + (byte) ((data) & 0xff), + }; + } + + + public String getLine(Record record) { + + StringBuilder sb = new StringBuilder(":"); + get(sb, intTo1BytesArr(record.length)); + get(sb, intTo2BytesArr(record.address)); + get(sb, intTo1BytesArr(record.type.toInt())); + get(sb, record.data, record.length); + get(sb, intTo1BytesArr(record.checksum)); + + sb.append("\r\n"); //old windows line separator + return sb.toString().toUpperCase(); + } + + private void get(StringBuilder sb, byte[] arr) { + get(sb, arr, arr.length); + } + + private void get(StringBuilder sb, byte[] arr, int len) { + for (int i = 0; i < len; i++) { + byte b = arr[i]; + String hex = Integer.toHexString(b & 0xFF).trim(); + sb.append(new String(new char[2 - hex.length()]).replace('\0', '0')) + .append(hex); + } + } + + public void save() { + try { + write(); + } catch (IOException ex) { + Logger.getLogger(BinWriter.class.getName()).log(Level.SEVERE, null, ex); + } + } +} diff --git a/src/main/java/cz/jaybee/intelhex/listeners/Writer.java b/src/main/java/cz/jaybee/intelhex/listeners/Writer.java new file mode 100644 index 0000000..4e4ff84 --- /dev/null +++ b/src/main/java/cz/jaybee/intelhex/listeners/Writer.java @@ -0,0 +1,52 @@ +package cz.jaybee.intelhex.listeners; + +import cz.jaybee.intelhex.DataListener; +import cz.jaybee.intelhex.Region; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +public abstract class Writer implements DataListener { + + protected final Region outputRegion; + protected final OutputStream destination; + protected byte[] buffer; + //private final MemoryRegions regions; + protected long maxAddress; + + public Writer(Region outputRegion) { + this(outputRegion, null); + } + public Writer(Region outputRegion, OutputStream destination) { + this.outputRegion = outputRegion; + this.destination = destination; + this.buffer = new byte[(int) (outputRegion.getLength())]; + Arrays.fill(buffer, (byte) 0xFF); + //regions = new MemoryRegions(); + maxAddress = outputRegion.getAddressStart(); + } + + @Override + public void data(long address, byte[] data) { + //regions.add(address, data.length); + + if ((address >= outputRegion.getAddressStart()) && (address <= outputRegion.getAddressEnd())) { + int length = data.length; + + if ((address + length) > outputRegion.getAddressEnd()) { + length = (int) (outputRegion.getAddressEnd() - address + 1); + } + System.arraycopy(data, 0, buffer, (int) (address - outputRegion.getAddressStart()), length); + + if (maxAddress < (address + data.length -1)) { + maxAddress = address + data.length - 1; + } + } + } + + public abstract void write() throws IOException; + +}