Browse Source

Supermogrify the thing into a lib

master
heck 2 years ago
parent
commit
e57077a3d1
  1. 1
      .gitignore
  2. 19
      src/Makefile
  3. 133
      src/fvhex2bin.c
  4. 57
      src/kk_ihex.h
  5. 127
      src/kk_ihex_read.c
  6. 106
      src/kk_ihex_read.h
  7. 140
      src/libfvhex2bin.c
  8. 3
      src/libfvhex2bin.h

1
.gitignore

@ -2,3 +2,4 @@
*.d
src/fvhex2bin
src/libfvhex2bin.a
/data/flangers.bin

19
src/Makefile

@ -1,32 +1,19 @@
TARGET_EXE=fvhex2bin
TARGET_LIB=lib$(TARGET_EXE).a
# default config
#SYS_PREFIX?=/opt/local
# System
#SYS_INC_PATH?=$(SYS_PREFIX)/include
#SYS_LIB_PATH?=$(SYS_PREFIX)/lib
CFLAGS+=-std=c99 -MMD -MP -Wno-format-extra-args -Wno-deprecated-declarations -fvisibility=hidden
#CFLAGS+=-isystem$(SYS_INC_PATH)
#LDFLAGS+=-DMACOSX_DEPLOYMENT_TARGET=11.00 -framework Foundation -framework IOKit -framework Security
#STATIC_LINK_LIBS=$(SYS_LIB_PATH)/libusb-1.0.a
SRC_ALL=$(wildcard *.c)
SRC_EXE=$(TARGET_EXE).c
SRC_LIB=$(filter-out $(SRC_EXE), $(SRC_ALL))
#OBJ_EXE=$(subst .c,.o,$(SRC_EXE))
OBJ_LIB=$(subst .c,.o,$(SRC_LIB))
DEPENDS=$(subst .c,.d,$(SRC_ALL))
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPENDS)
endif
.PHONY: all clean uninstall install
.PHONY: all clean test
.DEFAULT_GOAL := all
all: $(TARGET_EXE)
@ -39,6 +26,10 @@ $(TARGET_EXE): $(SRC_EXE) $(TARGET_LIB)
$(TARGET_LIB): $(OBJ_LIB)
$(AR) -cr $@ $^
test: all
./fvhex2bin -i ../data/flangers.hex -o ../data/flangers.bin
cmp -l ../data/flangers.bin ../data/flangers.hex.bin.target
clean:
rm $(OBJ_LIB) $(TARGET_LIB) $(TARGET_EXE) $(DEPENDS)

133
src/fvhex2bin.c

@ -21,69 +21,33 @@
* Distribute freely, mark modified copies as such.
*/
#include "kk_ihex_read.h"
#include <stdbool.h>
#include "libfvhex2bin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define AUTODETECT_ADDRESS (~0UL)
static FILE *outfile;
static unsigned long line_number = 1L;
static unsigned long file_position = 0L;
static unsigned long address_offset = 0UL;
static bool debug_enabled = 0;
int main(int argc, char *argv[])
{
struct ihex_state ihex;
FILE *infile = stdin;
ihex_count_t count;
char buf[256];
outfile = stdout;
char *infile = NULL;
char *outfile = NULL;
while (--argc) {
char *arg = *(++argv);
if (arg[0] == '-' && arg[1] && arg[2] == '\0') {
switch (arg[1]) {
case 'a':
if (--argc == 0) {
goto invalid_argument;
}
++argv;
errno = 0;
address_offset = strtoul(*argv, &arg, 0);
if (errno || arg == *argv) {
errno = errno ? errno : EINVAL;
goto argument_error;
}
break;
case 'A':
address_offset = AUTODETECT_ADDRESS;
break;
case 'i':
if (--argc == 0) {
goto invalid_argument;
}
++argv;
if (!(infile = fopen(*argv, "r"))) {
goto argument_error;
}
infile = *argv;
break;
case 'o':
if (--argc == 0) {
goto invalid_argument;
}
++argv;
if (!(outfile = fopen(*argv, "wb"))) {
goto argument_error;
}
break;
case 'v':
debug_enabled = 1;
outfile = *argv;
break;
case 'h':
case '?':
@ -97,98 +61,13 @@ int main(int argc, char *argv[])
invalid_argument:
(void)fprintf(stderr, "Invalid argument: %s\n", arg);
usage:
(void)fprintf(stderr, "kk_ihex " KK_IHEX_VERSION " - Copyright (c) 2013-2015 Kimmo Kulovesi\n");
(void)fprintf(
stderr,
"Usage: ihex2bin ([-a <address_offset>]|[-A])"
" [-o <out.bin>] [-i <in.hex>] [-v]\n");
return arg ? EXIT_FAILURE : EXIT_SUCCESS;
argument_error:
perror(*argv);
return EXIT_FAILURE;
}
ihex_read_at_address(
&ihex,
(address_offset != AUTODETECT_ADDRESS) ? (ihex_address_t)address_offset : 0);
while (fgets(buf, sizeof(buf), infile)) {
count = (ihex_count_t)strlen(buf);
ihex_read_bytes(&ihex, buf, count);
line_number += (count && buf[count - 1] == '\n');
}
ihex_end_read(&ihex);
if (infile != stdin) {
(void)fclose(infile);
}
hex2bin(infile, outfile);
return EXIT_SUCCESS;
}
ihex_bool_t ihex_data_read(struct ihex_state *ihex, ihex_record_type_t type, ihex_bool_t error)
{
if (error) {
(void)fprintf(stderr, "Checksum error on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
if ((error = (ihex->length < ihex->line_length))) {
(void)fprintf(stderr, "Line length error on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
if (!outfile) {
(void)fprintf(stderr, "Excess data after end of file record\n");
exit(EXIT_FAILURE);
}
if (type == IHEX_DATA_RECORD) {
unsigned long address = (unsigned long)IHEX_LINEAR_ADDRESS(ihex);
if (address < address_offset) {
if (address_offset == AUTODETECT_ADDRESS) {
// autodetect initial address
address_offset = address;
if (debug_enabled) {
(void)fprintf(stderr, "Address offset: 0x%lx\n", address_offset);
}
} else {
(void)fprintf(stderr, "Address underflow on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
}
address -= address_offset;
if (address != file_position) {
if (debug_enabled) {
(void)fprintf(
stderr,
"Seeking from 0x%lx to 0x%lx on line %lu\n",
file_position,
address,
line_number);
}
if (outfile == stdout || fseek(outfile, (long)address, SEEK_SET)) {
if (file_position < address) {
// "seek" forward in stdout by writing NUL bytes
do {
(void)fputc('\0', outfile);
} while (++file_position < address);
} else {
perror("fseek");
exit(EXIT_FAILURE);
}
}
file_position = address;
}
if (!fwrite(ihex->data, ihex->length, 1, outfile)) {
perror("fwrite");
exit(EXIT_FAILURE);
}
file_position += ihex->length;
} else if (type == IHEX_END_OF_FILE_RECORD) {
if (debug_enabled) {
(void)fprintf(stderr, "%lu bytes written\n", file_position);
}
if (outfile != stdout) {
(void)fclose(outfile);
}
outfile = NULL;
}
return true;
}

57
src/kk_ihex.h

@ -111,7 +111,7 @@
#include <stdint.h>
#ifdef IHEX_USE_STDBOOL
#include <stdbool.h>
#include <stdbool.h>
typedef bool ihex_bool_t;
#else
typedef uint_fast8_t ihex_bool_t;
@ -126,23 +126,23 @@ typedef int ihex_count_t;
// can be used to limit memory footprint on embedded systems, e.g.,
// most programs with IHEX output use 32.
#ifndef IHEX_LINE_MAX_LENGTH
#define IHEX_LINE_MAX_LENGTH 255
#define IHEX_LINE_MAX_LENGTH 255
#endif
enum ihex_flags {
IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow
IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow
};
typedef uint8_t ihex_flags_t;
typedef struct ihex_state {
ihex_address_t address;
ihex_address_t address;
#ifndef IHEX_DISABLE_SEGMENTS
ihex_segment_t segment;
ihex_segment_t segment;
#endif
ihex_flags_t flags;
uint8_t line_length;
uint8_t length;
uint8_t data[IHEX_LINE_MAX_LENGTH + 1];
ihex_flags_t flags;
uint8_t line_length;
uint8_t length;
uint8_t data[IHEX_LINE_MAX_LENGTH + 1];
} kk_ihex_t;
enum ihex_record_type {
@ -157,33 +157,34 @@ typedef uint8_t ihex_record_type_t;
#ifndef IHEX_DISABLE_SEGMENTS
// Resolve segmented address (if any). It is the author's recommendation that
// segmented addressing not be used (and indeed the write function of this
// library uses linear 32-bit addressing unless manually overridden).
//
#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4))
//
// Note that segmented addressing with the above macro is not strictly adherent
// to the IHEX specification, which mandates that the lowest 16 bits of the
// address and the index of the data byte must be added modulo 64K (i.e.,
// at 16 bits precision with wraparound) and the segment address only added
// afterwards.
//
// To implement fully correct segmented addressing, compute the address
// of _each byte_ with its index in `data` as follows:
//
#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4))
// Resolve segmented address (if any). It is the author's recommendation that
// segmented addressing not be used (and indeed the write function of this
// library uses linear 32-bit addressing unless manually overridden).
//
#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4))
//
// Note that segmented addressing with the above macro is not strictly adherent
// to the IHEX specification, which mandates that the lowest 16 bits of the
// address and the index of the data byte must be added modulo 64K (i.e.,
// at 16 bits precision with wraparound) and the segment address only added
// afterwards.
//
// To implement fully correct segmented addressing, compute the address
// of _each byte_ with its index in `data` as follows:
//
#define IHEX_BYTE_ADDRESS(ihex, byte_index) \
((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4))
#else // IHEX_DISABLE_SEGMENTS:
#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address)
#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index))
#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address)
#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index))
#endif
// The newline string (appended to every output line, e.g., "\r\n")
#ifndef IHEX_NEWLINE_STRING
#define IHEX_NEWLINE_STRING "\n"
#define IHEX_NEWLINE_STRING "\n"
#endif
// See kk_ihex_read.h and kk_ihex_write.h for function declarations!

127
src/kk_ihex_read.c

@ -10,9 +10,17 @@
#include "kk_ihex_read.h"
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#define IHEX_START ':'
#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U)
#define ADDRESS_HIGH_MASK ((ihex_address_t)0xFFFF0000U)
enum ihex_read_state {
READ_WAIT_FOR_START = 0,
@ -32,8 +40,8 @@ enum ihex_read_state {
#define IHEX_READ_STATE_MASK 0x78
#define IHEX_READ_STATE_OFFSET 3
void
ihex_begin_read (struct ihex_state * const ihex) {
void ihex_begin_read(struct ihex_state *const ihex)
{
ihex->address = 0;
#ifndef IHEX_DISABLE_SEGMENTS
ihex->segment = 0;
@ -43,22 +51,22 @@ ihex_begin_read (struct ihex_state * const ihex) {
ihex->length = 0;
}
void
ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) {
void ihex_read_at_address(struct ihex_state *const ihex, ihex_address_t address)
{
ihex_begin_read(ihex);
ihex->address = address;
}
#ifndef IHEX_DISABLE_SEGMENTS
void
ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) {
void ihex_read_at_segment(struct ihex_state *const ihex, ihex_segment_t segment)
{
ihex_begin_read(ihex);
ihex->segment = segment;
}
#endif
void
ihex_end_read (struct ihex_state * const ihex) {
void ihex_end_read(struct ihex_state *const ihex)
{
uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK;
uint_fast8_t sum;
if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) {
@ -66,7 +74,7 @@ ihex_end_read (struct ihex_state * const ihex) {
}
{
// compute and validate checksum
const uint8_t * const eptr = ihex->data + sum;
const uint8_t *const eptr = ihex->data + sum;
const uint8_t *r = ihex->data;
sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU);
while (r != eptr) {
@ -74,14 +82,14 @@ ihex_end_read (struct ihex_state * const ihex) {
}
sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum
}
if (ihex_data_read(ihex, type, (uint8_t) sum)) {
if (ihex_data_read(ihex, type, (uint8_t)sum)) {
if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) {
ihex->address &= 0xFFFFU;
ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) |
(((ihex_address_t) ihex->data[1]) << 16);
ihex->address |= (((ihex_address_t)ihex->data[0]) << 24) |
(((ihex_address_t)ihex->data[1]) << 16);
#ifndef IHEX_DISABLE_SEGMENTS
} else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) {
ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]);
ihex->segment = (ihex_segment_t)((ihex->data[0] << 8) | ihex->data[1]);
#endif
}
}
@ -89,9 +97,9 @@ ihex_end_read (struct ihex_state * const ihex) {
ihex->flags = 0;
}
void
ihex_read_byte (struct ihex_state * const ihex, const char byte) {
uint_fast8_t b = (uint_fast8_t) byte;
void ihex_read_byte(struct ihex_state *const ihex, const char byte)
{
uint_fast8_t b = (uint_fast8_t)byte;
uint_fast8_t len = ihex->length;
uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK);
ihex->flags ^= state; // turn off the old state
@ -122,60 +130,57 @@ ihex_read_byte (struct ihex_state * const ihex, const char byte) {
// We already know the lowest bit of `state`, dropping it may produce
// smaller code, hence the `>> 1` in switch and its cases.
switch (state >> 1) {
default:
// remain in initial state while waiting for :
return;
case (READ_COUNT_LOW >> 1):
// data length
ihex->line_length = b;
#if IHEX_LINE_MAX_LENGTH < 255
if (b > IHEX_LINE_MAX_LENGTH) {
ihex_end_read(ihex);
default:
// remain in initial state while waiting for :
return;
}
case (READ_COUNT_LOW >> 1):
// data length
ihex->line_length = b;
#if IHEX_LINE_MAX_LENGTH < 255
if (b > IHEX_LINE_MAX_LENGTH) {
ihex_end_read(ihex);
return;
}
#endif
break;
case (READ_ADDRESS_MSB_LOW >> 1):
// high byte of 16-bit address
ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address
ihex->address |= ((ihex_address_t) b) << 8U;
break;
case (READ_ADDRESS_LSB_LOW >> 1):
// low byte of 16-bit address
ihex->address |= (ihex_address_t) b;
break;
case (READ_RECORD_TYPE_LOW >> 1):
// record type
if (b & ~IHEX_READ_RECORD_TYPE_MASK) {
// skip unknown record types silently
return;
}
ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b;
break;
case (READ_DATA_LOW >> 1):
if (len < ihex->line_length) {
// data byte
ihex->length = len + 1;
state = READ_DATA_HIGH;
goto save_read_state;
}
// end of line (last "data" byte is checksum)
state = READ_WAIT_FOR_START;
end_read:
ihex_end_read(ihex);
break;
case (READ_ADDRESS_MSB_LOW >> 1):
// high byte of 16-bit address
ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address
ihex->address |= ((ihex_address_t)b) << 8U;
break;
case (READ_ADDRESS_LSB_LOW >> 1):
// low byte of 16-bit address
ihex->address |= (ihex_address_t)b;
break;
case (READ_RECORD_TYPE_LOW >> 1):
// record type
if (b & ~IHEX_READ_RECORD_TYPE_MASK) {
// skip unknown record types silently
return;
}
ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b;
break;
case (READ_DATA_LOW >> 1):
if (len < ihex->line_length) {
// data byte
ihex->length = len + 1;
state = READ_DATA_HIGH;
goto save_read_state;
}
// end of line (last "data" byte is checksum)
state = READ_WAIT_FOR_START;
end_read:
ihex_end_read(ihex);
}
}
save_read_state:
ihex->flags |= state << IHEX_READ_STATE_OFFSET;
}
void
ihex_read_bytes (struct ihex_state * restrict ihex,
const char * restrict data,
ihex_count_t count) {
void ihex_read_bytes(struct ihex_state *restrict ihex, const char *restrict data, ihex_count_t count)
{
while (count > 0) {
ihex_read_byte(ihex, *data++);
--count;
}
}

106
src/kk_ihex_read.h

@ -40,75 +40,73 @@
#define KK_IHEX_READ_H
#ifdef __cplusplus
#ifndef restrict
#define restrict
#endif
#ifndef restrict
#define restrict
#endif
extern "C" {
#endif
#include "kk_ihex.h"
// Begin reading at address 0
void ihex_begin_read(struct ihex_state *ihex);
// Begin reading at address 0
void ihex_begin_read(struct ihex_state *ihex);
// Begin reading at `address` (the lowest 16 bits of which will be ignored);
// this is required only if the high bytes of the 32-bit starting address
// are not specified in the input data and they are non-zero
void ihex_read_at_address(struct ihex_state *ihex,
ihex_address_t address);
// Begin reading at `address` (the lowest 16 bits of which will be ignored);
// this is required only if the high bytes of the 32-bit starting address
// are not specified in the input data and they are non-zero
void ihex_read_at_address(struct ihex_state *ihex, ihex_address_t address);
// Read a single character
void ihex_read_byte(struct ihex_state *ihex, char chr);
// Read a single character
void ihex_read_byte(struct ihex_state *ihex, char chr);
// Read `count` bytes from `data`
void ihex_read_bytes(struct ihex_state * restrict ihex,
const char * restrict data,
ihex_count_t count);
// Read `count` bytes from `data`
void ihex_read_bytes(struct ihex_state *restrict ihex, const char *restrict data, ihex_count_t count);
// End reading (may call `ihex_data_read` if there is data waiting)
void ihex_end_read(struct ihex_state *ihex);
// End reading (may call `ihex_data_read` if there is data waiting)
void ihex_end_read(struct ihex_state *ihex);
// Called when a complete line has been read, the record type of which is
// passed as `type`. The `ihex` structure will have its fields `data`,
// `line_length`, `address`, and `segment` set appropriately. In case
// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an
// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not
// yet parsed - it will be parsed into the `address` or `segment` field
// only if `ihex_data_read` returns `true`. This allows manual handling
// of extended addresses by parsing the `ihex->data` bytes.
//
// Possible error cases include checksum mismatch (which is indicated
// as an argument), and excessive line length (in case this has been
// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated
// by `line_length` greater than `length`. Unknown record types and
// other erroneous data is usually silently ignored by this minimalistic
// parser. (It is recommended to compute a hash over the complete data
// once received and verify that against the source.)
//
// Example implementation:
//
// ihex_bool_t ihex_data_read(struct ihex_state *ihex,
// ihex_record_type_t type,
// ihex_bool_t error) {
// error = error || (ihex->length < ihex->line_length);
// if (type == IHEX_DATA_RECORD && !error) {
// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET);
// (void) fwrite(ihex->data, 1, ihex->length, outfile);
// } else if (type == IHEX_END_OF_FILE_RECORD) {
// (void) fclose(outfile);
// }
// return !error;
// }
//
extern ihex_bool_t ihex_data_read(struct ihex_state *ihex,
ihex_record_type_t type,
ihex_bool_t checksum_mismatch);
// Called when a complete line has been read, the record type of which is
// passed as `type`. The `ihex` structure will have its fields `data`,
// `line_length`, `address`, and `segment` set appropriately. In case
// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an
// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not
// yet parsed - it will be parsed into the `address` or `segment` field
// only if `ihex_data_read` returns `true`. This allows manual handling
// of extended addresses by parsing the `ihex->data` bytes.
//
// Possible error cases include checksum mismatch (which is indicated
// as an argument), and excessive line length (in case this has been
// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated
// by `line_length` greater than `length`. Unknown record types and
// other erroneous data is usually silently ignored by this minimalistic
// parser. (It is recommended to compute a hash over the complete data
// once received and verify that against the source.)
//
// Example implementation:
//
// ihex_bool_t ihex_data_read(struct ihex_state *ihex,
// ihex_record_type_t type,
// ihex_bool_t error) {
// error = error || (ihex->length < ihex->line_length);
// if (type == IHEX_DATA_RECORD && !error) {
// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET);
// (void) fwrite(ihex->data, 1, ihex->length, outfile);
// } else if (type == IHEX_END_OF_FILE_RECORD) {
// (void) fclose(outfile);
// }
// return !error;
// }
//
extern ihex_bool_t ihex_data_read(
struct ihex_state *ihex,
ihex_record_type_t type,
ihex_bool_t checksum_mismatch);
// Begin reading at `segment`; this is required only if the initial segment
// is not specified in the input data and it is non-zero.
//
#ifndef IHEX_DISABLE_SEGMENTS
void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment);
void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment);
#endif
#ifdef __cplusplus

140
src/libfvhex2bin.c

@ -0,0 +1,140 @@
/*
* ihex2bin.c: Read Intel HEX format, write binary data.
*
* By default reads from stdin and writes to stdout. The command-line
* options `-i` and `-o` can be used to specify the input and output
* file, respectively. Specifying an output file allows sparse writes.
*
* NOTE: Many Intel HEX files produced by compilers/etc have data
* beginning at an address greater than zero, potentially causing very
* unnecessarily large files to be created. The command-line option
* `-a` can be used to specify the start address of the output file,
* i.e., the value will be subtracted from the IHEX addresses (the
* result must not be negative).
*
* Alternatively, the command-line option `-A` sets the address offset
* to the first address that would be written (i.e., first byte of
* data written will be at address 0).
*
* Copyright (c) 2013-2019 Kimmo Kulovesi, https://arkku.com
* Provided with absolutely no warranty, use at your own risk only.
* Distribute freely, mark modified copies as such.
*/
#include "kk_ihex_read.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define AUTODETECT_ADDRESS (~0UL)
static unsigned long line_number = 1L;
static unsigned long file_position = 0L;
static unsigned long address_offset = 0UL;
static bool debug_enabled = 0;
static FILE *infile = NULL;
static FILE *outfile = NULL;
int hex2bin(const char *infilename, const char *outfilename)
{
int ret = 0;
if (!(infile = fopen(infilename, "r"))) {
return 255;
}
if (!(outfile = fopen(outfilename, "wb"))) {
return 255;
}
struct ihex_state ihex;
ihex_count_t count;
char buf[256];
ihex_read_at_address(
&ihex,
(address_offset != AUTODETECT_ADDRESS) ? (ihex_address_t)address_offset : 0);
while (fgets(buf, sizeof(buf), infile)) {
count = (ihex_count_t)strlen(buf);
ihex_read_bytes(&ihex, buf, count);
line_number += (count && buf[count - 1] == '\n');
}
ihex_end_read(&ihex);
if (infile != stdin) {
(void)fclose(infile);
}
return ret;
}
ihex_bool_t ihex_data_read(struct ihex_state *ihex, ihex_record_type_t type, ihex_bool_t error)
{
if (error) {
(void)fprintf(stderr, "Checksum error on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
if ((error = (ihex->length < ihex->line_length))) {
(void)fprintf(stderr, "Line length error on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
if (!outfile) {
(void)fprintf(stderr, "Excess data after end of file record\n");
exit(EXIT_FAILURE);
}
if (type == IHEX_DATA_RECORD) {
unsigned long address = (unsigned long)IHEX_LINEAR_ADDRESS(ihex);
if (address < address_offset) {
if (address_offset == AUTODETECT_ADDRESS) {
// autodetect initial address
address_offset = address;
if (debug_enabled) {
(void)fprintf(stderr, "Address offset: 0x%lx\n", address_offset);
}
} else {
(void)fprintf(stderr, "Address underflow on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
}
address -= address_offset;
if (address != file_position) {
if (debug_enabled) {
(void)fprintf(
stderr,
"Seeking from 0x%lx to 0x%lx on line %lu\n",
file_position,
address,
line_number);
}
if (outfile == stdout || fseek(outfile, (long)address, SEEK_SET)) {
if (file_position < address) {
// "seek" forward in stdout by writing NUL bytes
do {
(void)fputc('\0', outfile);
} while (++file_position < address);
} else {
perror("fseek");
exit(EXIT_FAILURE);
}
}
file_position = address;
}
if (!fwrite(ihex->data, ihex->length, 1, outfile)) {
perror("fwrite");
exit(EXIT_FAILURE);
}
file_position += ihex->length;
} else if (type == IHEX_END_OF_FILE_RECORD) {
if (debug_enabled) {
(void)fprintf(stderr, "%lu bytes written\n", file_position);
}
if (outfile != stdout) {
(void)fclose(outfile);
}
outfile = NULL;
}
return true;
}

3
src/libfvhex2bin.h

@ -0,0 +1,3 @@
#pragma once
int hex2bin(const char *infilename, const char *outfile);
Loading…
Cancel
Save