#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <zlib.h>

#define KB (1024)
#define EB (128 * KB)
#define KERNEL_SIZE (10 * EB)
/* The max size limit is imposed by the upload limit on the Netgear webserver :( */
#define MAX_SIZE (38 * EB)
#define TRAILER_SIZE (100)


/* TODO: Allow commandline specification of some of these values. */
char trailer[TRAILER_SIZE] = 
	"device : WGT634U\n"
	"version : 2.3.4.5\n"
	"filetype : image\n"
	"date : 2005.05.02\n"
	"#############################\n";

int main(int argc, char *argv[]);
void usage(void);
int check_size(const char *file, off_t size);
void package(void *buffer, const char *kern, const char *jffs, const char *out);
int load_file(void *buffer, const char *file, off_t max_size);

/* TODO: Proper command line parsing, so that options can be specified in any order. */
int main(int argc, char *argv[])
{
	void *buffer;

	if(argc != 4)
	{
		usage();
		return 1;
	}

	fprintf(stderr, " Kernel image: %s\n"
			"  JFFS2 image: %s\n"
			"Netgear image: %s\n",
			argv[1], argv[2], argv[3]);
	if(0 == check_size(argv[1], KERNEL_SIZE - TRAILER_SIZE) &&
	   0 == check_size(argv[2], MAX_SIZE - KERNEL_SIZE))
	{
		buffer = malloc(MAX_SIZE);
		if(buffer == 0)
		{
			fprintf(stderr, "Can not allocate %d bytes of memory for image.\n",
					MAX_SIZE);
			return 3;
		}
		package(buffer, argv[1], argv[2], argv[3]);
		free(buffer);
	}
	else
	{
		return 2;
	}

	return 0;
}

void usage(void)
{
	fprintf(stderr, 
		"Create a Netgear WGT634U compatible flash image\n"
		"by assembling the kernel and jffs2 images.\n\n"
		"Version: $Id: genimage.c,v 1.1 2005/09/30 04:08:55 biswas Exp $\n"
		"Compiled: "__DATE__"\n\n"
		"Usage: mk-nfi kernel jffs2 destination\n\n"
		);
}

int check_size(const char *file, off_t size)
{
	struct stat st;
	int r;

	r = stat(file, &st);
	if(r == 0)
	{
		if(st.st_size > size)
		{
			fprintf(stderr, "File %s is too large. Max size %d\n",
					file, size);
			return -1;
		}

		if(st.st_size == 0)
		{
			fprintf(stderr, "File %s is empty\n",
					file);
			return -1;
		}
		return 0;
	}
	else
	{
		fprintf(stderr, "Can not stat() \"%s\": %s\n",
				file, strerror(errno));
		return -1;
	}
}

void dowrite(int fd, char *buf, int len)
{
	int amount_written = 0;
	int x;
	while (amount_written < len) {
		x = write(fd, buf + amount_written, len - amount_written);
		if (x < 0) {
			perror("bad write!");
			exit(-1);
		}
		amount_written += x;
	}
}

int doread(int fd, char *buf, int len)
{
	int amount_read = 0;
	int x;
	while (amount_read < len) {
		x = read(fd, buf + amount_read, len - amount_read);
		if (x < 0) {
			perror("bad read!");
			exit(-1);
		} if (x == 0) {
			return amount_read;
		}
			  
		amount_read += x;
	}
	return amount_read;
}

/* TODO: Error handling */
void package(void *buffer, const char *kern, const char *jffs, const char *out)
{
	char crc[32];
	unsigned long check = 0L;
	int fd;
	int fssize;
	int totalsize;

	memset(buffer, 0xFF, MAX_SIZE);
	load_file(buffer, kern, KERNEL_SIZE - TRAILER_SIZE);
	fssize = load_file(buffer + KERNEL_SIZE, jffs, MAX_SIZE - KERNEL_SIZE);
	totalsize = KERNEL_SIZE + fssize;
	memcpy(buffer + KERNEL_SIZE - TRAILER_SIZE, trailer, TRAILER_SIZE);
	check = crc32(check, buffer, totalsize);
	sprintf(crc, "%08lx", check);
	printf("CRC32 = %s\n", crc);
	fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0777);
	dowrite(fd, buffer, totalsize);
	dowrite(fd, crc, 8);
	close(fd);
}

/* TODO: Error handling */
int load_file(void *buffer, const char *file, off_t max_size)
{
	int fd;
	int ret;
	fd = open(file, O_RDONLY);
	ret = doread(fd, buffer, max_size);
	close(fd);
	return ret;
}
