#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>

void usage(char *progname) {
	fprintf(stderr, "%s: A utility for editing pram files\n", progname);
	fprintf(stderr, "\n");
	fprintf(stderr, "\t-f|--file <filename>           The pram file to edit\n");
	fprintf(stderr, "\t--boot-slot <slot#>            The slot to boot from in hex\n");
	fprintf(stderr, "\t--boot-slotrsrc <rsrc#>        The slot's resource ID  boot from\n");
	fprintf(stderr, "\t--addrmode 24/32               Set the addressing mode 24 or 32bit\n");

	return;
}

int main(int argc, char *argv[]) {
	char c;
	int loptind = 0;
	struct option o[] = {
		{"boot-slot", 1, 0, 1},
		{"boot-slotrsrc", 1, 0, 2},
		{"addrmode", 1, 0, 3},
		{"file", 1, 0, 'f'},
		{"help", 0, 0, 'h'},
	};
	unsigned char slotnum = 0;
	unsigned char slotrsrc = 0;
	unsigned char addrmode = 0;
	char *pramfile = NULL;
	struct stat sb;
	int fd;

	if(argc < 2 ) {
		usage(argv[0]);
		exit(1);
	}

	while( (c = getopt_long(argc, argv, "f:h", o, &loptind)) != -1 ) {
		switch(c) {
		case  1 :
			if(!optarg) {
				fprintf(stderr, "option boot-slot requires an argument\n");
				usage(argv[0]);
				exit(1);
			}

			if(strlen(optarg) > 1) {
				fprintf(stderr, "Slot %s out of range\n", optarg);
				usage(argv[0]);
				exit(1);
			}

			sscanf(optarg, "%hhx", &slotnum);
			break;		
		case  2 :
			if(!optarg) {
				fprintf(stderr, "option boot-slotrsrc requires an argument\n");
				usage(argv[0]);
				exit(1);
			}

			if((strlen(optarg) > 1) && (optarg[1] == 'x' || optarg[1] == 'X')) {
				sscanf(optarg, "%hhx", &slotrsrc);
			} else {
				sscanf(optarg, "%hhd", &slotrsrc);
			}
			break;
		case  3 :
			if(!optarg) {
				fprintf(stderr, "option addrmode requires an argument\n");
				usage(argv[0]);
				exit(1);
			}

			if(strcmp(optarg, "24") == 0) {
				addrmode = 24;
			} else if(strcmp(optarg, "32") == 0) {
				addrmode = 32;
			} else {
				fprintf(stderr, "Unknown addressing mode %s\n", optarg);
				usage(argv[0]);
				exit(1);
			}

			break;
		case 'f':
			if(!optarg) {
				fprintf(stderr, "option file requires an argument\n");
				usage(argv[0]);
				exit(1);
			}
			
			pramfile = optarg;
			break;
		case 'h':
			usage(argv[0]);
			exit(1);
		}
	}

	if(!pramfile) {
		fprintf(stderr, "No pram file specified\n");
		exit(1);
	}

	fd = open(pramfile, O_RDWR);
	if(fd < 0) {
		fprintf(stderr, "Could not open pram file %s: %s\n", pramfile, strerror(errno));
		exit(1);
	}

	if(slotnum) {
		unsigned char zero = 0;
		pwrite(fd, &zero, 1, 0x78);
		pwrite(fd, &zero, 1, 0x79);
		pwrite(fd, &slotnum, 1, 0x7a);
		pwrite(fd, &slotrsrc, 1, 0x7b);
	}

	if(addrmode) {
		unsigned char curflags;
		pread(fd, &curflags, 1, 0x8a);
		if(addrmode == 24) {
			curflags &= ~0x05;
		} else if(addrmode == 32) {
			curflags |= 0x05;
		}
		pwrite(fd, &curflags, 1, 0x8a);
	}

	close(fd);
	exit(0);
}
