001package jmri.jmrix.loconet.sdf; 002 003import java.io.File; 004import java.io.IOException; 005import java.io.InputStream; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.List; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Provide tools for reading, writing and accessing Digitrax SPJ files. 014 * <p> 015 * Maintains several representations: 016 * <ul> 017 * <li>A byte array of the SDF contents after assembly. This is not complete, as 018 * it can't contain information like contents, labels, etc. Nor can it 019 * distinguish certain options with identical values (e.g. several constants 020 * that boil down to a zero value) 021 * <li>An array of nested SdfMacro objects. These contain more detailed 022 * representations of the macro source, in the limit containing the entire 023 * thing. 024 * </ul> 025 * 026 * @author Bob Jacobsen Copyright (C) 2007, 2008, 2010 027 */ 028public class SdfBuffer { 029 030 public SdfBuffer(byte[] buffer) { 031 this.buffer = Arrays.copyOf(buffer, buffer.length); 032 loadMacroList(); 033 } 034 035 public SdfBuffer(String name) throws IOException { 036 File file = new File(name); 037 int length = (int) file.length(); 038 039 InputStream s = new java.io.BufferedInputStream(new java.io.FileInputStream(file)); 040 041 try { 042 // Assume we can get all this in memory 043 buffer = new byte[length]; 044 045 for (int i = 0; i < length; i++) { 046 buffer[i] = (byte) (s.read() & 0xFF); 047 } 048 loadMacroList(); 049 } catch (java.io.IOException e1) { 050 log.error("error reading file", e1); 051 throw e1; 052 } finally { 053 try { 054 s.close(); 055 } catch (java.io.IOException e2) { 056 log.error("Exception closing file", e2); 057 } 058 } 059 } 060 061 protected int index; 062 063 public void resetIndex() { 064 index = 0; 065 } 066 067 public int getAtIndex() { 068 return buffer[index] & 0xFF; 069 } 070 071 public int getAtIndexAndInc() { 072 return buffer[index++] & 0xFF; 073 } 074 075 public boolean moreData() { 076 return index < buffer.length; 077 } 078 079 public void setAtIndex(int data) { 080 buffer[index] = (byte) (data & 0xFF); 081 } 082 083 public void setAtIndexAndInc(int data) { 084 buffer[index++] = (byte) (data & 0xFF); 085 } 086 087 /** 088 * Reload the byte buffer from the contained instruction objects 089 */ 090 public void loadByteArray() { 091 // first get length of new array 092 int length = 0; 093 for (int i = 0; i < ops.size(); i++) { 094 length += ops.get(i).totalLength(); 095 } 096 buffer = new byte[length]; 097 log.debug("create buffer of length {}", length); 098 resetIndex(); 099 // recurse to store bytes 100 for (int i = 0; i < ops.size(); i++) { 101 ops.get(i).loadByteArray(this); 102 } 103 if (index != length) { 104 log.error("Lengths did not match: {} {}", index, length); 105 } 106 } 107 108 @Override 109 public String toString() { 110 StringBuilder out = new StringBuilder(""); 111 for (int i = 0; i < ops.size(); i++) { 112 SdfMacro m = ops.get(i); 113 114 out.append(m.allInstructionString(" ")); 115 } 116 return out.toString(); 117 } 118 119 public byte[] getByteArray() { 120 return Arrays.copyOf(buffer, buffer.length); 121 } 122 123 public List<SdfMacro> getMacroList() { 124 return ops; 125 } 126 127 void loadMacroList() { 128 resetIndex(); 129 ops = new ArrayList<>(); 130 while (moreData()) { 131 SdfMacro m = SdfMacro.decodeInstruction(this); 132 ops.add(m); 133 } 134 } 135 136 // List of contained instructions 137 ArrayList<SdfMacro> ops; 138 139 // byte[] representation 140 byte[] buffer; 141 142 private final static Logger log = LoggerFactory.getLogger(SdfBuffer.class); 143 144}