/* This program takes a standard hexdump textfile as input and saves its contents in the original binary format.
 * The hexdump textfile must have 4-digit line numbers at beginning of each line, 16 bytes per line separated by spaces
 * and the ASCII representation at the end as a 16 character string (no spaces in between).
 * The end of line style should be Unix, not Windblows.
 * The 4-digit line numbers are ignored (they don't affect the order of bytes saved as binary, bytes are saved in
 * the exact order they are found in the hexdump textfile, line after line).
 * An example of hexdump textfile acceptable as input is this:

0000  00 F3 E5 3E C9 32 00 00 CD 00 00 3B 3B E1 3E 41  ...>.2.....;;.>A
0010  D3 FE 01 30 00 09 11 00 5A 06 20 C5 4E 06 08 3E  ...0....Z. .N..>
0020  00 CB 01 30 02 3E 12 12 13 10 F4 C1 23 10 EC 18  ...0.>......#...
0030  4A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07  J...............
0040  10 48 C0 04 90 49 00 04 90 49 C0 07 10 48 20 04  .H...I...I...H .
0050  10 49 20 04 1E 30 C0 00 00 00 00 00 00 00 00 00  .I ..0..........
0060  00 00 00 00 00 00 00 00 FF FF 00 00 FF FF 00 00  ................
0070  FF FF 00 00 FF FF 00 00 FF FF 00 3E 01 D3 FE E1  ...........>....
0080  EB 2A 9A 00 4B 44 ED B8 EB CB E5 CB FD E9 00 00  .*..KD..........
0090  18 20 09 FD FC FC 00 20 FF 3F 00 04 03 03 08 01  . ..... .?......
00A0  04 02 0F 03 4A 02 C6 09 EF 3F 00 00 00 00 00 00  ....J....?......
00B0  FF FF F3 F9 ED 5E 7C ED 47 7D D3 E3 3E FF D3 E3  .....^|.G}..>...
00C0  3E 01 D3 E3 3E 7B D3 FB 2A 92 FC CD 0C FD CD 4F  >...>{..*......O
00D0  FD 21 A8 FC ED 4B 9C FC CD 2F FD D9 ED 4B A0 FC  .!...K.../...K..
00E0  CD 2C FD CD 4F FD CB 6F D9 20 05 34 CB 96 18 EB  .,..O..o. .4....
00F0  CD 71 FD CD 71 FD E9 ED A2 FB ED 4D E5 2A 92 FC  .q..q......M.*..
0100  22 90 FC 21 F0 FC 36 FF E1 FB ED 4D F3 22 90 FC  "..!..6....M."..
0110  3E 7F D3 EB 3A 96 FC D3 EB 3E FF D3 F3 3A 97 FC  >..:....>...:..
0120  D3 F3 C9 DB F5 CB 7F 28 FA CB 77 C9 21 AA FC DB  ......(..w.!...
0130  F5 CB 67 20 FA 18 02 4E 23 F3 CD 23 FD 20 22 59  ..g ...N#..#. "Y
0140  0E FD ED 59 10 F1 FB C9 ED 4B 9E FC CD 2F FD 21  ...Y.....K.../.!
0150  B2 FC 06 08 CD 23 FD 28 13 36 FF DB FD 77 23 10  .....#.(.6...w#.
0160  F3 F3 AF 6F 67 F6 C1 D3 FE ED 4F E9 21 B2 FC 7E  ...og.....O.!..~
0170  C9 ED 4B A2 FC CD 2C FD CD 48 FD CB 6F 28 F9 E6  ..K...,..H..o(..
0180  50 20 DE CD 48 FD FE 80 20 F9 DB F5 E6 0F 20 FA  P ..H... ..... .
0190  06 03 D9 ED 4B A4 FC CD 2C FD CD 4F FD E6 C0 D9  ....K...,..O....
01A0  28 04 10 EE 18 BB 21 B5 FC 11 AB FC 01 04 00 ED  (.....!.........
01B0  B0 2B 46 2B 7E 12 C5 AF 32 F0 FC 21 F7 FC CD 0C  .+F+~...2..!....
01C0  FD 23 CB DE ED 4B A6 FC CD 2C FD 2A 98 FC 76 DB  .#...K...,.*..v.
01D0  F5 E6 20 20 F9 22 9A FC 2A 92 FC CD 0C FD CD 4F  ..  ."..*......O
01E0  FD 3A F0 FC B7 C1 28 05 7E E6 D8 28 05 10 C7 C3  .:....(.~..(....
01F0  61 FD 21 AB FC 34 2A 9A FC 22 98 FC 23 C9 00 00  a.!..4*.."..#...

*/

/* The one and only argument for this program is the name of the hexdump textfile */

#define _GNU_SOURCE	/* required by asprintf() */
#include <stdio.h>
#include <sys/stat.h>	/* required by stat type */
#include <stdlib.h>	/* required by exit() function */
#include <errno.h>	/* required by system var errno (?) */
#include <string.h>	/* required by string functions starting named "str*" */
#include <ctype.h>	/* required by isdigit() */
#include <fcntl.h>	/* required by low-level file access */
#include <unistd.h>	/* required by low-level file deletion function "unlink" */
#include <libgen.h>	/* required by dirname and basename */
#include <sys/types.h>	/* required by opendir() */
#include <dirent.h>	/* required by opendir() */

int main (int argc, char *argv[]) {

	char hexdumptxt_file_name[100], out_file_name[100];
	int out_file_dscrptr, error_code, line_no;
	FILE *file_stream;
	size_t len = 0;
	ssize_t bytes_read;
	char *line = NULL, byte_h[3];
	unsigned char byte, buffer[16], i;

	if (argc < 2) {printf("Argument missing, please specify hexdump file name\n"); exit(1);}

	for (i=0; i<=100; i++) {hexdumptxt_file_name[i]='\0'; out_file_name[i]='\0';}

	strcpy(hexdumptxt_file_name, argv[1]);
	if (!strstr(hexdumptxt_file_name, ".hexdump")) {printf("Hexdump textfile name must end in .hexdump\n"); exit(1);}
	strcpy(out_file_name, hexdumptxt_file_name);
	i = strstr(out_file_name, ".hexdump") - &out_file_name[0];
	out_file_name[i]='\0'; strcat(out_file_name, ".bin");

	file_stream = fopen (argv[1], "r");
	if (file_stream == NULL) {printf("Error %d opening file %s: %m\n", errno, argv[1]); exit(1);}

	/* ################## hexdump file initial format check #################### */

	printf("Performing hexdump file format initial check ... ");
	line_no = 0; error_code = 0;
	while ((bytes_read = getline(&line, &len, file_stream)) != -1) {
		line_no ++;
		/* bytes_read will include the newline char at the end */
		if (bytes_read != 72) {printf("\nError: hexdump line %d has %d characters instead of 71.", line_no, bytes_read-1); error_code = 1;}
	}
	if (error_code) {printf("\nHexdump to binary conversion aborted: errors encountered.\nPlease check hexdump file, make sure EOL is UNIX style (LF and NOT CR+LF)\n"); exit(1);}
	else {printf("OK.\n");}
	/* ######################################################################### */
	
	fseek (file_stream, 0, SEEK_SET);	/* rewinding file pointer to start of file */
	umask(000);
	out_file_dscrptr = open(out_file_name, O_CREAT | O_WRONLY | O_APPEND, 00666);
	if (out_file_dscrptr == -1) {printf("Error %d creating output file: %m\n", errno); exit(1);}

	byte_h[2] = '\0';
	while ((bytes_read = getline(&line, &len, file_stream)) != -1) {
		if (bytes_read == 72) {
			for (i=6; i<=51; i+=3) {
				memcpy(&byte_h[0], &line[i], 2);
				sscanf(byte_h, "%2hhX", &byte);
				buffer[(i-6)/3] = byte;
			}
			error_code = write(out_file_dscrptr, (void *) buffer, 16);
			if (error_code == -1) {printf("WRITE ERROR %d: %m\n", errno);}
		} else {printf("Error: hexdump line has %d characters instead of 72\n", bytes_read);}
	}
	close(out_file_dscrptr); fclose(file_stream);
	printf("File %s successfully created.\n", out_file_name);

	return(0);

}