💾 Archived View for uscoffings.net › retro-computing › systems › TI994a › x99tape › x99tape_src › ma… captured on 2023-03-20 at 21:53:55.

View Raw

More Information

⬅️ Previous capture (2022-06-04)

-=-=-=-=-=-=-

/*
	Portable TI99 tape encoder/decoder

	main.c: Console main() function, including command-line parser

	syntax examples:

		decode memory dump tape:
		x99tape -d -m ROM2.wav ROM2.raw
		x99tape -d -m basic_le�on1.wav basic_le�on1.raw
		x99tape -d -m basic_le�on2.wav basic_le�on2.raw
		x99tape -d -m basic_le�on3.wav basic_le�on3.raw
		x99tape -d -m grom_dump1.wav grom_dump1.raw
		x99tape -d -m intro.wav INTRO
		x99tape -d -m poker.wav poker.raw
		x99tape -d -m puissance_4.wav PUISSANCE4
		x99tape -d -m splat(XB).wav splat(XB).raw
		x99tape -d -m voyage.wav voyage.raw
		x99tape -d -m test.wav TEST

		decode multirecord tape:
		x99tape -d -f ROM3.wav ROM3.raw
		x99tape -d -f -r114 grille1.wav GRILLE1
		x99tape -d -f -r128 grille1.wav GRILLE1
		x99tape -d -f -r128 grille1b.wav GRILLE1B

		encode a tape (TIFILE format):
		x99tape -e INTRO test.wav
		x99tape -e GRILLE1 grille1b.wav
		x99tape -e PUISSANCE4 puissance_4.wav

		encode memory dump tape (obsolete):
		x99tape -e -m ROM2.raw ROM2.wav
		x99tape -e -m grom_dump1.raw grom_dump1.wav

		encode multirecord tape (obsolete):
		x99tape -e -f -r114 grille1.raw grille1.wav

	Raphael Nabet 2002


#include <console.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>

#include "common.h"
#include "my_bool.h"
#include "my_int.h"

#include "tifiles.h"
#include "wave.h"
#include "tape_dec.h"
#include "tape_enc.h"


/*
	Set to 1 to display elapsed time after each file is processed

#define COMPUTE_ELAPSED_TIME 1

#if COMPUTE_ELAPSED_TIME
#include <time.h>
#endif

/*
	Static prototypes

static error_t do_decode(const char *src_fname, const char *dst_fname, ftype_t file_type, unsigned int record_len);
static error_t do_encode(const char *src_fname, const char *dst_fname, ftype_t default_type, unsigned int default_reclen);

#if 0
/*
	error strings

static char *error_strings[error_string_len-1] =
{
	"Internal error",							/* incorrect function parameter */
	"This cannot be a multi-record tape",		/* the tape cannot be a multi-record tape */
	"Invalid record length",					/* provided record len cannot be correct */

	"This is not a wave file",					/* wave file is incorrect */
	"Only PCM-encoded wave files are supported",	/* wave file may be correct, but belongs to a type that the program cannot handle */

	"Can't open file",							/* can't open file */
	"Read error",								/* error or unexpected EOF when reading file */
	"Write error",								/* error when writing file */

	"EOF",										/* expected EOF when reading file */
	"Time out",									/* timed out while looking for data */
};
#endif

/*
	Defaults when no command line option is specified

enum
{
	encode_default = false,				/* decode tapes */
	tape_type_default = ftype_program	/* tapes are assumed to be program tapes */
};


/*
	main func:

	parse command line parameters

int main(int argc, char **argv)
{
	error_t error;
	bool encode;
	ftype_t tape_type;
	unsigned int record_len;
	char *src_fname;
	char *dst_fname;

	int i;
	int parse_step;
	int file_processed_count;
	bool suppress_option_parsing;


#if 1
	/* Macintosh hack */
	argc = ccommand(&argv);
#endif

	/*if (strcmp(argv[0], "decode"))
		encode = false;
	else if (strcmp(argv[0], "encode"))
		encode = true;
	else*/
	encode = encode_default;
	tape_type = tape_type_default;
	record_len = 0;

	parse_step = 0;
	file_processed_count = 0;
	suppress_option_parsing = false;
	for (i=1; i<argc; i++)
	{
		if ((argv[i][0] == '-') && (! suppress_option_parsing))
		{
			if (parse_step == 2)
			{
				fprintf(stderr, "Options should *precede* source file name.\n"
								"Type: %s -h for help.\n", argv[0]);
				return 1;
			}

			parse_step = 1;

			if (argv[i][1] == '\0')
			{
				suppress_option_parsing = true;
			}
			else
			{
				switch (argv[i][1])
				{
				case 'h':
				case 'H':
					/* display help */
					fprintf(stderr, "TI99 tape encoder/decoder\n"
									"help:\n"
									"\t%s -h\n"
									"encode/decode:\n"
									"\t%s [-d|-e] [-b|-m] [-r|-rNNN] [-] source1 dest1 [[[-e|-d] [-b|-m] [-r|-rNNN] [-] source2 dest2] ...]\n"
									"-h: display this help message\n"
									"-d: decode sampled tape into data file\n"
									"-e: encode data file into sampled tape\n"
									"-m: decode as memory dump tape\n"
									"-f: decode as multirecord file tape\n"
									"-r: use default record len for multirecord file tapes (64, 128 or 192 according to number of blocks per record)\n"
									"-rNNN: specify record len for multirecord file tapes (should be in [1,192])\n"
									"-: force next 2 parameters to be regarded as file names (required if either file name (or both) starts with a '-')\n", argv[0], argv[0]);
					if (i == (argc-1))
						return 0;
					else
					{
						fprintf(stderr, "Help asked, ignoring remaining command line parameters\n");
						return 1;
					}

				case 'e':
					/* enter tape encode mode */
					encode = true;
					break;

				case 'd':
					/* enter tape decode mode */
					encode = false;
					break;

				case 'f':
					/* enter sequential file tape mode */
					tape_type = ftype_data;
					break;

				case 'm':
					/* enter memory dump tape mode */
					tape_type = ftype_program;
					break;

				case 'r':
					/* enter record len */
					if (! argv[i][2])
						/* no len: use default record len */
						record_len = 0;
					else
					{	/* parse record len */
						if (sscanf(argv[i]+2, "%u", &record_len) != 1)
						{
							fprintf(stderr, "Can't read record len NNN in '-rNNN' option.\n"
											"Type: %s -h for help.\n", argv[0]);
							return 1;
						}
						if ((record_len < 1) || (record_len > 192))
						{
							fprintf(stderr, "Incorrect record len %d (should be in [1,192])\n"
											"Type: %s -h for help.\n", record_len, argv[0]);
							return 1;
						}
					}
					break;

				default:
					/* unknown option */
					fprintf(stderr, "Unrecognized option.\n"
									"Type: %s -h for help.\n", argv[0]);
					return 1;
				}
			}
		}
		else
		{
			if ((parse_step == 0) || (parse_step == 1))
			{
				src_fname = argv[i];
				parse_step = 2;
			}
			else
			{
				dst_fname = argv[i];

				if (encode)
					error = do_encode(src_fname, dst_fname, tape_type, record_len);
				else
					error = do_decode(src_fname, dst_fname, tape_type, record_len);
				if (error)
				{
					fprintf(stderr, "error\n");
					return 1;
				}

				file_processed_count++;
				suppress_option_parsing = false;
				parse_step = 0;
			}
		}
	}


	if (parse_step == 1)
	{	/* some options, but no file name */
		fprintf(stderr, "Source and destination file names missing.\n"
						"Type: %s -h for help.\n", argv[0]);
		return 1;
	}
	else if (parse_step == 2)
	{	/* a source file name, but no destination */
		fprintf(stderr, "Destination file name missing.\n"
						"Type: %s -h for help.\n", argv[0]);
		return 1;
	}
	else if (file_processed_count == 0)
	{	/* No file was processed - not an error strictly speaking , but the user could obviously
		use	some help */
		fprintf(stderr, "TI99 tape encoder/decoder\n"
						"Type: %s -h for help.\n", argv[0]);
	}


	return 0;
}


/*
	handle decode command

	src_fname: name of source file
	dst_fname: name of destination file
	file_type: memory dump or data file
	record_len: record length in bytes ([1,192], 0 for automatic)

static error_t do_decode(const char *src_fname, const char *dst_fname, ftype_t file_type, unsigned int record_len)
{
	FILE *src, *dst;
	error_t error = no_error;
	wave_file_in_t wave_file;
	tifile_out_t tifile;

#if COMPUTE_ELAPSED_TIME
	clock_t start;
#endif

	/*if (! file_type)
		record_len = 64;*/

	if ((record_len < 0) || (record_len > 192))
	{
		error = invalid_parameters;
		goto error;
	}

	src = fopen(src_fname, "rb");
	if (! src)
	{
		error = cant_open;
		goto error;
	}

	dst = fopen(dst_fname, "wb");
	if (! dst)
	{
		error = cant_open;
		goto error2;
	}

	error = open_wave_in(src, & wave_file);
	if (error)
		goto error3;

	error = open_tifile_out(dst, &tifile, file_type, record_len);
	if (error)
		goto error3;

	#if COMPUTE_ELAPSED_TIME
		start = clock();
	#endif

	error = read_tape(& wave_file, &tifile);
	if (error)
		goto error4;

	#if COMPUTE_ELAPSED_TIME
		fprintf(stderr, "Elapsed time (s): %f\n", (double) (clock()-start) / CLOCKS_PER_SEC);
	#endif

error4:
	close_tifile_out(&tifile);
error3:
	fclose(dst);
error2:
	fclose(src);

error:
	return error;
}


/*
	handle encode command

	src_fname: name of source file
	dst_fname: name of destination file
	default_type: for raw binary source format: memory dump or data file
	default_reclen: for raw binary source format: record length in bytes
		([1,192], 0 admitted if encoding a program file (memory dump))

static error_t do_encode(const char *src_fname, const char *dst_fname, ftype_t default_type, unsigned int default_reclen)
{
	FILE *src, *dst;
	error_t error = no_error;
	tifile_in_t tifile;
	wave_file_out_t wave_file;


	/* open source */
	src = fopen(src_fname, "rb");
	if (! src)
	{
		error = cant_open;
		goto error;
	}

	error = open_tifile_in(src, & tifile, default_type, default_reclen);
	if (error)
		goto error3;


	dst = fopen(dst_fname, "wb");
	if (! dst)
	{
		error = cant_open;
		goto error2;
	}

	error = open_wave_out(dst, 22050L, & wave_file);
	if (error)
		goto error3;

	error = write_tape(& tifile, & wave_file);
	if (error)
		goto error3;

	error = close_wave_out(& wave_file);
	if (error)
		goto error3;

error3:
	fclose(dst);
error2:
	fclose(src);

error:
	return error;
}