/*
 * Copyright 2009 Luke Dashjr <luke_n8x0_cfgread@dashjr.org>
 */

#include <assert.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <mtd/mtd-abi.h>
#include <stdio.h>
#include <string.h>

#define NVT_A 0
#define NVT_B 1
#define NVT_HEXADDR 4

typedef struct {
	char magic[4];
	char magicB;
	char part;
	char type;
	char magicC;
	char name[16];
	int32_t datalen;
	char unknown[8];
	
	char*data;
} nv_t;
#define NV_HEADER_SIZE (sizeof(nv_t) - sizeof(char*))

char*s_name = NULL;
char s_part_chk = 0;
char s_part;
char m_found = 0;
char pp;

signed int
dumpone(int fd) {
	nv_t c;
//	printf("Offset: %x\n", lseek(fd, 0, SEEK_CUR));
	if (read(fd, &c, NV_HEADER_SIZE) < 1)
		return -1;
	
	if (s_name && strcmp(s_name, c.name))
		goto skip;
	if (s_part_chk && s_part != c.part)
		goto skip;
	
	if (!(memcmp(c.magic, "\0\0\0\0", 4) && memcmp(c.magic, "\xff\xff\xff\xff", 4)))
	{
skip:
		lseek(fd, 0x800 - NV_HEADER_SIZE, SEEK_CUR);
		return 0;
	}
	
	assert(!memcmp(c.magic, "ConF", 4));
	assert(c.magicB == 2);
	assert(c.magicC == 0);
	
	c.data = malloc(c.datalen);
	read(fd, c.data, c.datalen);
	
//	printf("Read %d bytes of %s (part %d)\n", c.datalen, c.name, c.part);
	switch (pp) {
	case 1:
		for (int i = 0; i < c.datalen; i += 4)
		{
			assert(!(c.data[i + 1] || c.data[i + 2] || c.data[i + 3]));
			if (i == 0)
			{
				assert(!c.data[i]);
				continue;
			}
			else
			if (i > 4)
				putchar(':');
			printf("%02x", c.data[i]);
		}
		puts("");
		break;
	default:
		fwrite(c.data, c.datalen, 1, stdout);
	}
	
	free(c.data);
	
	if (s_part_chk)
		exit(0);
	m_found = 1;
	
	lseek(fd, 0x800 - NV_HEADER_SIZE - c.datalen, SEEK_CUR);
	return 0;
}

void
dumpall(int fd) {
	while (!dumpone(fd))
		;
}

void
usage(const char*appname) {
	fprintf(stderr, "Usage: %s [name] [part] [pp] [mtd] [mode]\n", appname);
	fprintf(stderr, "PP: 0 = raw; 1 = MAC\n");
	fprintf(stderr, "Modes: 1 = normal; 2 = OTP user; 4 = OTP factory\n");
	exit(1);
}

int
main(int argc, char**argv) {
	const char*mtd = "/dev/mtd1ro";
	char clmode = 7;
	
	{
	int aci = 0;
	if (argc > ++aci)
	{
		if (argv[aci][0] == '-')
			usage(argv[0]);
		s_name = argv[aci];
	}
	if (argc > ++aci)
	{
		s_part = atoi(argv[aci]);
		s_part_chk = 1;
	}
	if (argc > ++aci)
		pp = atoi(argv[aci]);
	if (argc > ++aci)
		mtd = argv[aci];
	if (argc > ++aci)
		clmode = atoi(argv[aci]);
	}
	
	int fd = open(mtd, O_RDONLY);
	assert(fd != -1);
	
	if (clmode & 1)
	{
		lseek(fd, 0, SEEK_SET);
		dumpall(fd);
	}
	
	if (clmode & 2)
	{
		ioctl(fd, MTDFILEMODE, MTD_OTP_USER);
		lseek(fd, 0, SEEK_SET);
		dumpall(fd);
	}
	
	if (clmode & 4)
	{
		ioctl(fd, MTDFILEMODE, MTD_OTP_FACTORY);
		lseek(fd, 0, SEEK_SET);
		dumpall(fd);
	}
	
	return !m_found;
}

