/* hex2bin
 * Intel Hex file to binary file converter for the broken Z80DT hex files
 * 
 * Autor: Andre Adrian
 * Version: 26jan2011
 *
 * Compiler: Microsoft Visual C++ 2010 Express
 * but any ANSI C compiler should do.
 */

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int hexvalue(char c) {
	if (c >= '0' && c <= '9') {
		return c-'0';
	} else if (c >= 'A' && c <= 'F') {
		return c-'A'+10;
	} else if (c >= 'a' && c <= 'f') {
		return c-'a'+10;
	} else {
		fprintf(stderr, "error invalid hex digit %c", c);
		return -1;
	}
}

int hex2bin(FILE *fpBin, FILE *fpHex) {
	int recordType;
	int numLine = 0;
	unsigned int myAddress = 0xffff;
	do {
		char line[256], *crPosition;
		unsigned char byte[128];
		unsigned int i, ii, length, address, byteCount, checkSum, myCheckSum;

		fgets(line, sizeof(line), fpHex);
		++numLine;
		if (line[0] != ':') {
			fprintf(stderr, "error invalid start code %c in line %d\n",
				line[0], numLine);
			return -1;
		}
		crPosition = strstr(line, "\n");
		if (crPosition != NULL) *crPosition = 0;
		length = strlen(line);
		for (i = 1, ii = 0; i < length; i += 2, ++ii) {
			int high, low;

			high = hexvalue(line[i]);
			low = hexvalue(line[i+1]);
			if (high < 0 || low < 0) {
				fprintf(stderr, " in line %d\n", 
					numLine);
				return -1;
			}
			byte[ii] = 16 * high + low;
		}
		byteCount = byte[0];
		address = 256 * byte[1] + byte[2];
		if (myAddress != 0xffff && myAddress != address) {
			fprintf(stderr, "warning address not continous in line %d\n", 
				numLine);
			// return -1;
		}
		myAddress = address + byteCount; // address of next line
		recordType = byte[3];
		if (recordType > 1) {
			fprintf(stderr, "error unsupported record type %d in line %d\n", 
				recordType, numLine);
			return -1;
		}
		checkSum = byte[4 + byteCount];
		myCheckSum = 0;
		for (i = 0; i < byteCount + 5; ++i) {
			myCheckSum += byte[i];
		}
		if ((myCheckSum & 0xFF) != 0) {
			fprintf(stderr, "error invalid checksum in line %d\n", numLine);
			return -1;
		}

		fwrite(&byte[4], 1, byteCount, fpBin);
	} while (recordType != 1);
	return 0;
}

int main(int argc, char* argv[]) {
	char filename[256], *dotPosition;
	FILE *fpHex, *fpBin;
	int rv;

	if (argc < 2) {
		/* interactive mode. Ask for hexfile name */
		printf("hex2bin. Enter hexfile name: ");
		fgets(filename, sizeof(filename), stdin);
		char *crPosition = strstr(filename, "\n");
		if (crPosition != NULL) *crPosition = 0;
	} else {
		/* command line argument mode */
		filename[0] = 0;
		strncat(filename, argv[1], sizeof(filename));
	}
	fpHex = fopen(filename, "rt");
	if (NULL == fpHex) {
		fprintf(stderr, "error fopen(%s): %s\n", filename, strerror(errno));
		goto abort;
	}
	printf("Open hexfile %s\n", filename);

	dotPosition = strstr(filename, ".");
	if (dotPosition != NULL) *dotPosition = 0;
	strncat(filename, ".BIN", sizeof(filename));
	fpBin = fopen(filename, "wb");
	if (NULL == fpBin) {
		fprintf(stderr, "error fopen(%s): %s\n", filename, strerror(errno));
		fclose(fpHex);
		goto abort;
	}
	printf("Create binfile %s\n", filename);
	rv = hex2bin(fpBin, fpHex);

	fclose(fpHex);
	fclose(fpBin);
	if (rv < 0) goto abort;

	if (argc < 2) fgetc(stdin);
	return 0;

abort:
	if (argc < 2) fgetc(stdin);
	return 1;
}

