From 222ba811574b7cc9166995deef42a52b9cc26c24 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 14 Feb 2013 20:02:17 +0100 Subject: [PATCH] Add a parser for Intel HEX files. --- src/Makefile.am | 1 + src/ihex.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ihex.h | 56 +++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 src/ihex.c create mode 100644 src/ihex.h diff --git a/src/Makefile.am b/src/Makefile.am index 3e1417c..2cce288 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,6 +39,7 @@ libdivecomputer_la_SOURCES = \ mares_puck.c \ mares_darwin.c mares_darwin_parser.c \ mares_iconhd.c mares_iconhd_parser.c \ + ihex.h ihex.c \ hw_ostc.c hw_ostc_parser.c \ hw_frog.c \ hw_ostc3.c \ diff --git a/src/ihex.c b/src/ihex.c new file mode 100644 index 0000000..5389696 --- /dev/null +++ b/src/ihex.c @@ -0,0 +1,206 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "ihex.h" +#include "context-private.h" +#include "checksum.h" +#include "array.h" + +struct dc_ihex_file_t { + dc_context_t *context; + FILE *fp; +}; + +dc_status_t +dc_ihex_file_open (dc_ihex_file_t **result, dc_context_t *context, const char *filename) +{ + dc_ihex_file_t *file = NULL; + + if (result == NULL || filename == NULL) { + ERROR (context, "Invalid arguments."); + return DC_STATUS_INVALIDARGS; + } + + file = malloc (sizeof (dc_ihex_file_t)); + if (file == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + file->context = context; + + file->fp = fopen (filename, "rb"); + if (file->fp == NULL) { + ERROR (context, "Failed to open the file."); + free (file); + return DC_STATUS_IO; + } + + *result = file; + + return DC_STATUS_SUCCESS; +} + +dc_status_t +dc_ihex_file_read (dc_ihex_file_t *file, dc_ihex_entry_t *entry) +{ + unsigned char ascii[9 + 2 * 255 + 2] = {0}; + unsigned char data[4 + 255 + 1] = {0}; + unsigned int type, length, address; + unsigned char csum_a, csum_b; + size_t n; + + if (file == NULL || entry == NULL) { + ERROR (file ? file->context : NULL, "Invalid arguments."); + return DC_STATUS_INVALIDARGS; + } + + /* Read the start code. */ + while (1) { + n = fread (ascii, 1, 1, file->fp); + if (n != 1) { + if (feof (file->fp)) { + return DC_STATUS_DONE; + } else { + ERROR (file->context, "Failed to read the start code."); + return DC_STATUS_IO; + } + } + + if (ascii[0] == ':') + break; + + /* Ignore CR and LF characters. */ + if (ascii[0] != '\n' && ascii[0] != '\r') { + ERROR (file->context, "Unexpected character (0x%02).", ascii[0]); + return DC_STATUS_DATAFORMAT; + } + } + + /* Read the record length, address and type. */ + n = fread (ascii + 1, 1, 8, file->fp); + if (n != 8) { + ERROR (file->context, "Failed to read the header."); + return DC_STATUS_IO; + } + + /* Convert to binary representation. */ + if (array_convert_hex2bin (ascii + 1, 8, data, 4) != 0) { + ERROR (file->context, "Invalid hexadecimal character."); + return DC_STATUS_DATAFORMAT; + } + + /* Get the record length. */ + length = data[0]; + + /* Read the record payload. */ + n = fread (ascii + 9, 1, 2 * length + 2, file->fp); + if (n != 2 * length + 2) { + ERROR (file->context, "Failed to read the data."); + return DC_STATUS_IO; + } + + /* Convert to binary representation. */ + if (array_convert_hex2bin (ascii + 9, 2 * length + 2, data + 4, length + 1) != 0) { + ERROR (file->context, "Invalid hexadecimal character."); + return DC_STATUS_DATAFORMAT; + } + + /* Verify the checksum. */ + csum_a = data[4 + length]; + csum_b = ~checksum_add_uint8 (data, 4 + length, 0x00) + 1; + if (csum_a != csum_b) { + ERROR (file->context, "Unexpected checksum (0x%02x, 0x%02x).", csum_a, csum_b); + return DC_STATUS_DATAFORMAT; + } + + /* Get the record address. */ + address = array_uint16_be (data + 1); + + /* Get the record type. */ + type = data[3]; + if (type < 0 || type > 5) { + ERROR (file->context, "Invalid record type (0x%02x).", type); + return DC_STATUS_DATAFORMAT; + } + + /* Verify the length and address. */ + if (type != 0) { + unsigned int len = 0; + switch (type) { + case 1: /* End of file record. */ + len = 0; + break; + case 2: /* Extended segment address record. */ + case 4: /* Extended linear address record. */ + len = 2; + break; + case 3: /* Start segment address record. */ + case 5: /* Start linear address record. */ + len = 4; + break; + } + if (length != len || address != 0) { + ERROR (file->context, "Invalid record length or address."); + return DC_STATUS_DATAFORMAT; + } + } + + /* Set the record fields. */ + entry->type = type; + entry->address = address; + entry->length = length; + + /* Copy the record data. */ + memcpy (entry->data, data + 4, entry->length); + memset (entry->data + entry->length, 0, sizeof (entry->data) - entry->length); + + return DC_STATUS_SUCCESS; +} + +dc_status_t +dc_ihex_file_reset (dc_ihex_file_t *file) +{ + if (file == NULL) { + ERROR (NULL, "Invalid arguments."); + return DC_STATUS_INVALIDARGS; + } + + rewind (file->fp); + + return DC_STATUS_SUCCESS; +} + +dc_status_t +dc_ihex_file_close (dc_ihex_file_t *file) +{ + if (file) { + fclose (file->fp); + free (file); + } + + return DC_STATUS_SUCCESS; +} diff --git a/src/ihex.h b/src/ihex.h new file mode 100644 index 0000000..10cc9e6 --- /dev/null +++ b/src/ihex.h @@ -0,0 +1,56 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef DC_IHEX_H +#define DC_IHEX_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct dc_ihex_file_t dc_ihex_file_t; + +typedef struct dc_ihex_entry_t { + unsigned int type; + unsigned int address; + unsigned int length; + unsigned char data[255]; +} dc_ihex_entry_t; + +dc_status_t +dc_ihex_file_open (dc_ihex_file_t **file, dc_context_t *context, const char *filename); + +dc_status_t +dc_ihex_file_read (dc_ihex_file_t *file, dc_ihex_entry_t *entry); + +dc_status_t +dc_ihex_file_reset (dc_ihex_file_t *file); + +dc_status_t +dc_ihex_file_close (dc_ihex_file_t *file); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_IHEX_H */