/*
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/*
 * faum_cd_to_iso - converts faum-cd-images to iso-images
 */

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

#include "../node-pc/simulator/cd_image.h"

const char * progname;

/* tell compiler we do not return */
static void
usage(int retval) __attribute__((__noreturn__));
static void
usage(int retval)
{
	fprintf(stderr, "%s [OPTION]\n", progname);
	fprintf(stderr, "Convert FAUmachine-CD-Image to ISO-9660-Images.\n\nOptions:\n"
		"\t-name    <imagename>    Filename of the FAUmachine-CD-Image\n"
		"\t-iso     <iso-filename> Filename of the output-iso-image\n"
		"\t-track   <trackno>      Number of the track (defaults to first track)\n"
#if 0
		"\t-session <trackno>      Number of the session (defaults to first session)\n"
#endif
		);
	exit(retval);
}

static void
msf_to_lba(uint8_t msf[3], int64_t *lba)
{
	/* msf[0] = min; msf[1] = sec; msf[2] = frame */
	if (msf[0] < 90) {
		*lba = (msf[0] * 60 + msf[1]) * 75 + msf[2] - 150;
	} else {
		*lba = (msf[0] * 60 + msf[1]) * 75 + msf[2] - 450150;
	}
}

int main(int argc, char ** argv)
{
	int fd;
	int srcfd;

	int trackno = 0;
#if 0
	int sessionno;
#endif
	int ret;
	cd_image media_desc;
	toc_session toc;
	mode1 cd_block;
	int sizeoftrack = 0;
	toc_entry *act_toc_line;
	int64_t lead_in_size;

	int option;
	const char * filename = NULL;
	const char * isoname = NULL;

	struct option longopts[] = {
		/* name, has_arg, flag, val */
		{ "name",	1, NULL, 'n' },
		{ "iso",	1, NULL, 'i' },
#if 0
		{ "session",	1, NULL, 's' },
#endif
		{ "track",	1, NULL, 't' },
		{ NULL, 0, NULL, 0 }
	};

	progname = *argv;
	
	if (argc == 1) {
		fprintf(stderr, "need parameters!\n");
		usage(EXIT_FAILURE);
	}

	while ((option = getopt_long_only(argc, argv, "", longopts, NULL)) != -1) {
		switch(option) {
		case 'n':
			filename = optarg;
			break;
		case 'i':
			isoname = optarg;
			break;
#if 0
		case 's':
			sessionno = strtol(optarg, (char **)NULL, 10);
#endif
		case 't':
			trackno = strtol(optarg, (char **)NULL, 10);
			if (99 < trackno) {
				fprintf(stderr, "Max # of tracks is 99\n");
				usage(EXIT_FAILURE);
			}
			break;
		default:
			usage(EXIT_FAILURE);
		}
	}

	if ( ! filename ) {
		fprintf(stderr, "No FAUM-CD-Image set\n");
		usage(EXIT_FAILURE);
	}

	if ( ! isoname ) {
		fprintf(stderr, "No ISO-Image set\n");
		usage(EXIT_FAILURE);
	}

	if ( trackno == 0 ) {
		fprintf(stderr, "No Trackno set\n");
		usage(EXIT_FAILURE);
	}

	if (argc != optind) {
		fprintf(stderr, "unknown parameter: %s\n", argv[optind]);
		usage(EXIT_FAILURE);
	}

	srcfd = open(filename, O_RDONLY);
	if (srcfd <= 0) {
		fprintf(stderr, "opening filename %s failed: %s\n",
						filename, strerror(errno));
		exit(EXIT_FAILURE);
	}

	fd = creat(isoname, 0666); 
	if (fd <= 0) {
		fprintf(stderr, "opening isoname %s failed: %s\n",
						isoname, strerror(errno));
		exit(EXIT_FAILURE);
	}


	ret = read(srcfd, &media_desc, sizeof(cd_image));
	
	if (strncmp(media_desc.magic,"\211FAUM-CDIMAGE",13) != 0) {
		fprintf(stderr, "no FAUM-CDimage\n");
		fprintf(stderr, "magic: %s\n", media_desc.magic);
		return 1;
	}


	/* set seek pointer to start of toc (after offset) */
	lseek(srcfd, media_desc.offset, SEEK_SET);

	ret = read(srcfd, &toc, sizeof(toc_session));

	act_toc_line = &(toc.x01) + ( trackno - 1 );
	sizeoftrack = act_toc_line->blkcount;

	/* set seek pointer to start of data (after offset and lead-in) */
	msf_to_lba((uint8_t *)&media_desc.start_of_leadin_m, &lead_in_size);
	lseek(srcfd, media_desc.offset + media_desc.toc_size + (-1 * lead_in_size) + 150 * 2352, SEEK_SET);
	
	while ( 0 < sizeoftrack ) {
		size_t sz;
		/* read 2352 byte from source and write 2048 to imagefile */
		sz = read(srcfd, &cd_block, sizeof(cd_block));
		assert(sz == sizeof(cd_block));
		sz = write(fd, &cd_block.data, sizeof(cd_block.data));
		assert(sz == sizeof(cd_block.data));
		sizeoftrack--;
	}

	ret = close(fd);
	assert(0 <= ret);
	ret = close(srcfd);
	assert(0 <= ret);
	
	return 0;
}
