1 /*-----------------------------------------------------------------------
4 * utility to pack an Intel HEX format file by removing redundant
5 * extended offset records and accumulating data records up to
6 * OUTPUT_CHUNK (currently 16) bytes.
8 * Released to the public domain 10/16/2000 Kevin Vigor.
19 /* A cooked line of input. */
22 Uint8 len; /* length of data portion of record. */
29 /* Largest expected line of raw input. */
30 #define MAX_INPUT_RAW 128
31 /* Largest expected cooked data portion of an input line. */
32 #define MAX_INPUT_COOKED (MAX_INPUT_RAW / 2)
34 /* Globals: current input & output line numbers. */
38 /* Convert hex digit to numeric value 0 - 15; assumes input is a
39 * valid digit (i.e. passes isxdigit()).
41 static Uint8 hexDigit(const char c)
45 return (Uint8)(c - '0');
49 return (Uint8)((islower(c) ? toupper(c) : c) - 'A' + 10);
53 /* Convert two hex digits from cp to a byte value. */
54 static int getHexByte(const char *cp, Uint8 *byte)
56 if (cp && cp[0] && isxdigit(cp[0]) && cp[1] && isxdigit(cp[1]))
58 *byte = (hexDigit(cp[0]) << 4) + hexDigit(cp[1]);
67 /* Convert four hex digits from cp to a 2 byte value. */
68 static int getHexWord(const char *cp, Uint16 *word)
72 if (getHexByte(cp, &byte1) || getHexByte(cp + 2, &byte2))
76 *word = (byte1 << 8) + byte2;
80 /* Return a single cooked line of input from the passed file handle. */
81 Line *readLine(FILE *inFile)
83 static char buffer[MAX_INPUT_RAW];
88 line = (Line *)malloc(sizeof(Line));
91 fprintf(stderr, "packihx: no memory!\n");
97 if (!fgets(buffer, MAX_INPUT_RAW, inFile))
103 if (!buffer[0] || buffer[0] == '\r' || buffer[0] == '\n')
105 /* Empty input line. */
108 } while (buffer[0] != ':');
111 bp++; /* Skip leading : */
113 if (getHexByte(bp, &line->len))
115 fprintf(stderr, "packihx: can't read line length @ line %d\n",
120 bp += 2; /* Two digits consumed. */
122 if (line->len > MAX_INPUT_COOKED)
124 fprintf(stderr, "packihx: line length %X too long @ line %d\n",
125 (int)line->len, lineno);
130 if (getHexWord(bp, &line->offset))
132 fprintf(stderr, "packihx: can't read line offset @ line %d\n",
137 bp += 4; /* Four digits consumed. */
139 if (getHexByte(bp, &line->type))
141 fprintf(stderr, "packihx: can't read record type @ line %d\n",
146 bp += 2; /* Two digits consumed. */
148 /* Hack - always allocate something, even if len is zero.
149 * Avoids special case for len == 0. */
150 line->data = (Uint8 *)malloc(line->len ? line->len : 1);
154 fprintf(stderr, "packihx: no memory!\n");
158 for (i = 0; i < (unsigned)line->len; i++)
160 if (getHexByte(bp, &(line->data[i])))
163 "packihx: can't read data byte %u of %u @ line %d\n",
164 i, (unsigned) line->len, lineno);
169 bp += 2; /* Two digits consumed. */
172 if (getHexByte(bp, &line->checksum))
174 fprintf(stderr, "packihx: can't read checksum @ line %d\n",
180 /* bp += 2; */ /* Two digits consumed. */
185 /* Compute the checksum of a line. */
186 Uint16 lineChecksum(unsigned len, unsigned offset, unsigned type,
192 checksum = len + type + (offset >> 8) +
195 for (i = 0; i < len; i++)
203 checksum = 0x100 - checksum;
208 /* Ensure that the checksum of a line matches the expected value. */
209 int validateChecksum(Line *line)
213 checksum = lineChecksum(line->len, line->offset, line->type, line->data);
215 if (checksum != line->checksum)
217 fprintf(stderr, "packihx: invalid checksum %X (want %X) @ line %d\n",
218 (unsigned)checksum, (unsigned)(line->checksum),
226 /* Write a single record line. */
227 int writeRecord(unsigned len, unsigned offset, unsigned type,
232 if (printf(":%02X%04X%02X", len, offset, type) == EOF)
237 for (i = 0; i < len; i++)
239 if (printf("%02X", data[i]) == EOF)
245 if (printf("%02X\n", lineChecksum(len, offset, type, data)) == EOF)
253 #define OUTPUT_CHUNK 16
254 static unsigned pendingLen = 0;
255 static unsigned pendingOffset = 0;
256 static char pending[MAX_INPUT_COOKED + OUTPUT_CHUNK];
258 /* Buffer up a data record. */
259 int bufferOutput(Line *line)
264 /* Stick the data onto any pending data. */
265 assert(pendingLen < OUTPUT_CHUNK);
266 memcpy(&pending[pendingLen], line->data, line->len);
267 pendingLen += line->len;
269 /* Write it out untill we have less than an OUTPUT_CHUNK left. */
270 while (!rc && pendingLen >= OUTPUT_CHUNK)
272 rc = writeRecord(OUTPUT_CHUNK, pendingOffset, 0, &pending[offset]);
273 offset += OUTPUT_CHUNK;
274 pendingOffset += OUTPUT_CHUNK;
275 pendingLen -= OUTPUT_CHUNK;
278 /* Copy any remaining bits back to the beginning of the buffer. */
281 memmove(pending, &pending[offset], pendingLen);
286 /* Write out any pending data. */
287 int flushPendingData(void)
291 assert(pendingLen < OUTPUT_CHUNK);
295 rc = writeRecord(pendingLen, pendingOffset, 0, pending);
296 pendingLen = pendingOffset = 0;
301 /* Write an arbitrary line of output (buffering if possible) */
302 int writeLine(Line *line)
304 static Uint16 lastExtendedOffset = 0;
309 /* Not a data record. */
314 /* Extended offset record. */
318 "packihx: invalid extended offset record @ line %d\n",
323 offset = (line->data[0] << 8) + line->data[1];
325 if (offset == lastExtendedOffset)
327 /* We can simply skip this line. */
332 lastExtendedOffset = offset;
336 if (flushPendingData())
341 /* Write the line as is. */
342 rc = writeRecord(line->len, line->offset, line->type, line->data);
346 if (pendingOffset + pendingLen != (unsigned)line->offset)
348 /* This line is not contigous with the last one. Dump pending. */
349 if (flushPendingData())
353 pendingOffset = line->offset;
355 rc = bufferOutput(line);
360 int main(int argc, char *argv[])
369 inFile = fopen(argv[1], "rt");
372 fprintf(stderr, "packihx: cannot open %s\n",
384 while (!rc && ((line = readLine(inFile)) != NULL))
386 rc = validateChecksum(line);
390 rc = writeLine(line);
397 if (!rc && !feof(inFile))
399 /* readLine must have failed for some reason. */
400 fprintf(stderr, "packihx: aborting after %d lines.\n", lineno);
406 /* Just in case there's something still pending. */
407 rc = flushPendingData();
412 fprintf(stderr, "packihx: read %d lines, wrote %d: OK.\n", lineno, outlineno);