Bump java lib versions in preparation for 1.9.2
[fw/altos] / altoslib / AltosEeprom.java
1 /*
2  * Copyright © 2017 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 package org.altusmetrum.altoslib_14;
16
17 import java.util.*;
18 import java.io.*;
19
20 public class AltosEeprom {
21
22         private AltosJson       config;
23         ArrayList<Byte>         data;
24         private AltosConfigData config_data;
25         int                     errors = 0;
26
27         /*
28          * Public accessor APIs
29          */
30         public int data8(int offset) {
31                 return ((int) data.get(offset)) & 0xff;
32         }
33
34         public int data16(int offset) {
35                 return data8(offset) | (data8(offset+1) << 8);
36         }
37
38         public int data24(int offset) {
39                 return (data8(offset) |
40                         (data8(offset+1) << 8) |
41                         (data8(offset+2) << 16));
42         }
43
44         public int data32(int offset) {
45                 return (data8(offset) |
46                         (data8(offset+1) << 8) |
47                         (data8(offset+2) << 16) |
48                         (data8(offset+3) << 24));
49         }
50
51         public int size() {
52                 return data.size();
53         }
54
55         public AltosConfigData config_data() {
56                 if (config_data == null) {
57                         config_data = (AltosConfigData) config.make(AltosConfigData.class);
58                         if (config_data == null)
59                                 config_data = new AltosConfigData();
60
61                         if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN) {
62                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
63                                 if (config_data.product != null) {
64                                         if (config_data.product.startsWith("TeleMetrum"))
65                                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
66                                         else if (config_data.product.startsWith("TeleMini"))
67                                                 config_data.log_format = AltosLib.AO_LOG_FORMAT_TINY;
68                                 }
69                         }
70                 }
71                 return config_data;
72         }
73
74         private void write_config(Writer w) throws IOException {
75                 config.write(w, 0, true);
76                 w.append('\n');
77         }
78
79         /*
80          * Private I/O APIs
81          */
82         private void write_data(Writer w) throws IOException {
83                 PrintWriter pw = new PrintWriter(w);
84
85                 for (int i = 0; i < data.size(); i++) {
86                         if (i > 0) {
87                                 if ((i & 0x1f) == 0)
88                                         pw.printf("\n");
89                                 else
90                                         pw.printf(" ");
91                         }
92                         pw.printf("%02x", data.get(i));
93                 }
94                 w.append('\n');
95         }
96
97         private boolean read_config(InputStream stream) throws IOException {
98                 config = AltosJson.fromInputStream(stream);
99                 if (config == null)
100                         return false;
101                 return true;
102         }
103
104         private String read_line(InputStream stream) throws IOException {
105                 StringBuffer    buffer = null;
106                 int             c;
107
108                 for (;;) {
109                         c = stream.read();
110                         if (c == -1 && buffer == null)
111                                 return null;
112                         if (buffer == null)
113                                 buffer = new StringBuffer();
114                         if (c == -1 || c == '\n')
115                                 return buffer.toString();
116                         buffer.append((char) c);
117                 }
118         }
119
120         private boolean read_data(InputStream stream) throws IOException {
121                 String                  s;
122
123                 data = new ArrayList<Byte>();
124                 while ((s = read_line(stream)) != null) {
125
126                         String[] tokens = s.split("\\s+");
127
128                         for (int i = 0; i < tokens.length; i++) {
129                                 if (tokens[i].length() > 0) {
130                                         try {
131                                                 data.add((byte) AltosLib.fromhex(tokens[i]));
132                                         } catch (NumberFormatException e) {
133                                                 throw new IOException(e.toString());
134                                         }
135                                 }
136                         }
137                 }
138                 return true;
139         }
140
141         private boolean read_old_config(InputStream stream) throws IOException {
142                 AltosConfigData cfg = new AltosConfigData();
143                 for (;;) {
144                         boolean done = false;
145
146                         /* The data starts with an upper case F character followed by a space */
147                         stream.mark(2);
148                         int     first = stream.read();
149                         if (first == 'F') {
150                                 int second =  stream.read();
151                                 if (second == ' ')
152                                         done = true;
153                         }
154                         stream.reset();
155                         if (done)
156                                 break;
157
158                         String line = read_line(stream);
159                         if (line == null)
160                                 return false;
161                         cfg.parse_line(line);
162                 }
163                 config = new AltosJson(cfg);
164                 return true;
165         }
166
167         private boolean read_old_data(InputStream stream) throws IOException {
168                 String line;
169
170                 data = new ArrayList<Byte>();
171                 while ((line = read_line(stream)) != null) {
172                         String[] tokens = line.split("\\s+");
173
174                         /* Make sure there's at least a type and time */
175                         if (tokens.length < 2)
176                                 break;
177
178                         /* packet type */
179                         if (tokens[0].length() != 1)
180                                 break;
181                         int start = data.size();
182
183                         if (config_data().log_format != AltosLib.AO_LOG_FORMAT_TINY) {
184                                 byte cmd = (byte) tokens[0].codePointAt(0);
185                                 data.add(cmd);
186
187                                 int time = AltosLib.fromhex(tokens[1]);
188
189                                 data.add((byte) 0);
190                                 data.add((byte) (time & 0xff));
191                                 data.add((byte) (time >> 8));
192                         }
193                         if (tokens.length == 4) {
194                                 /* Handle ancient log files */
195                                 if (config_data().log_format == AltosLib.AO_LOG_FORMAT_TINY) {
196                                         /*
197                                          * Ancient TeleMini log files stored "extra" data to pretend
198                                          * that it was a TeleMetrum device. Throw that away and
199                                          * just save the actual log data.
200                                          */
201                                         int a = AltosLib.fromhex(tokens[2]);
202                                         int b = AltosLib.fromhex(tokens[3]);
203                                         if (a != 0)
204                                                 b = 0x8000 | a;
205                                         data.add((byte) (b & 0xff));
206                                         data.add((byte) ((b >> 8)));
207                                 } else {
208                                         for (int i = 2; i < tokens.length; i++) {
209                                                 int v = AltosLib.fromhex(tokens[i]);
210                                                 data.add((byte) (v & 0xff));
211                                                 data.add((byte) ((v >> 8)));
212                                         }
213                                         /* Re-compute the checksum byte */
214                                         data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
215                                 }
216                         } else {
217                                 for (int i = 2; i < tokens.length; i++)
218                                         data.add((byte) AltosLib.fromhex(tokens[i]));
219                                 /* Re-compute the checksum byte */
220                                 data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
221                         }
222                 }
223                 return true;
224         }
225
226         private void read(InputStream stream) throws IOException {
227                 BufferedInputStream     bis = new BufferedInputStream(stream);
228
229                 bis.mark(1);
230                 int c = bis.read();
231                 bis.reset();
232
233                 if (c == '{') {
234                         if (!read_config(bis))
235                                 throw new IOException("failed to read config");
236                         if (!read_data(bis))
237                                 throw new IOException("failed to read data");
238                 } else {
239                         if (!read_old_config(bis))
240                                 throw new IOException("failed to read old config");
241                         if (!read_old_data(bis))
242                                 throw new IOException("failed to read old data");
243                 }
244         }
245
246         /*
247          * Public APIs for I/O
248          */
249         public void write(Writer w) throws IOException {
250                 write_config(w);
251                 write_data(w);
252         }
253
254         public String toString() {
255                 try {
256                         Writer  w = new StringWriter();
257
258                         write(w);
259                         return w.toString();
260                 } catch (Exception e) {
261                         return null;
262                 }
263         }
264
265         public void print() throws IOException {
266                 System.out.printf("%s", toString());
267         }
268
269         /*
270          * Constructors
271          */
272         public AltosEeprom(InputStream stream) throws IOException {
273                 read(stream);
274         }
275
276         public AltosEeprom(String s) throws IOException {
277                 read(new AltosStringInputStream(s));
278         }
279
280         public AltosEeprom(AltosJson config, ArrayList<Byte> data) {
281                 this.config = config;
282                 this.data = data;
283         }
284
285         public AltosEeprom(AltosConfigData config_data, ArrayList<Byte> data) {
286                 this.config = new AltosJson(config_data);
287                 this.data = data;
288         }
289
290         public AltosEeprom() {
291         }
292 }