/************************************************************************
 *	TITLE:			JAZSAV.C				*
 *	PROJECT:		JAGUAR/JAZ				*
 *	FUNCTION:		JAZ SYNTH CONFIG SAVING MODULE		*
 *	PROGRAMMER:		Hans-Martin Krober			*
 *	DATE:			6/95					*
 *									*
 *              COPYRIGHT 1993,1994 Atari U.S. Corporation              *
 *          UNATHORIZED REPRODUCTION, ADAPTATION, DISTRIBUTION,         *
 *          PERFORMANCE OR DISPLAY OF THIS COMPUTER PROGRAM OR          *
 *        THE ASSOCIATED AUDIOVISUAL WORK IS STRICTLY PROHIBITED.       *
 *                            ALL RIGHTS RESERVED.                      *
 *									*
 ************************************************************************/



#include <stdarg.h>
#include <ctype.h>
#include "portab.h"
#include "slibc.h"
#include "alloc.h"
#include "glib.h"
#include "gemlib.h"
#include "jazobj.h"
#include "jazsyn.h"
#include "ptchptch.h"
#include "dos.h"

/*
 *	EXTERNALS
 */
EXTERN BYTE	SLASH;				/* slash '/' or '\' */
EXTERN LONG	*_def_patches;			/* patches which came with synth */
EXTERN OBJECT	pstabtree[];			/* patch loading page tree */
EXTERN OBJECT	jaztree[];			/* main page tree */
EXTERN WORD	scrn_w, scrn_h;			/* screen sizes */
EXTERN BYTE	filebuf[];			/* file i.o */
EXTERN ULONG	*_xvalue;			/* for getting symbol refs */
EXTERN ULONG	*_mdtab_start;			/* start of dsp mdtabs */
EXTERN ULONG	*_env_start;			/* start of dsp envelopes */
EXTERN ULONG	*_env_end;			/* end  of dsp envelopes */
EXTERN ULONG	*_env_temp;			/* envelope temp buffer */
EXTERN ULONG	*_vt_return_loc;
EXTERN struct _pitchpatch pitchpatch[];

EXTERN LONG	patchtab[][PATCH_SIZE];		/* buffer for patches */
EXTERN PTT_TAB 	ptt_table[];			/* PTT Data (Patch Map) */
EXTERN WORD	ptt_count;			/* # of entries in PTT */

EXTERN OBJECT 	algtree[];			/* algorithm edit patge */

EXTERN LONG	*curr_patch;			/* current edited patch */
EXTERN WORD 	curr_pnr;			/* current edited patch number */

EXTERN ENVTAB	*pi_find_envtab(WORD, WORD);
EXTERN WORD	pi_find_envtab_ix(WORD, WORD);


EXTERN BYTE	patch_names[NR_PATCHES][16];	/* names of patches */


/*
 * 	Sample Reference Handling
 */

EXTERN SMPTAB	sample_table[N_SAMPLES];	/* sample pointers */
EXTERN WORD	smpslot_cnt;			/* current sample slot */

/*
 * 	Envelope Reference Handling
 */
EXTERN ENVTAB	env_table[N_ENVS];		/* envelope data */
EXTERN WORD	envslot_cnt;			/* current env slot */
					/* envelope entry */
/*
 * 	MDTAB Reference Handling
 */

EXTERN MDTAB	mdtab_table[N_ENVS];		/* mdtab data */
EXTERN WORD	mdtabslot_cnt;			/* current mdtab slot */


/*
 * 	Patch Info Table
 */

EXTERN PATCHINFO patchinfo_table[NR_PATCHES];
EXTERN PATCHINFO patchinfo_temp;

EXTERN WORD	 patch_cnt;		/* number of loaded patches */


/*
 *	Build in stuff (ROM WAVES)
 */

EXTERN SYMTAB rom_symbols[];

/*
 *	Synth Externals
 */

EXTERN ULONG	_attn;
EXTERN ULONG	_delay;
EXTERN ULONG	_dft_filter;
EXTERN ULONG	_filter_def_1;
EXTERN ULONG	_filter_def_2;
EXTERN ULONG	_mixer;
EXTERN ULONG	_reverb;
EXTERN ULONG	_osc_sampler;
EXTERN ULONG	_osc_sampler_loop;
EXTERN ULONG	_osc_nisampler;
EXTERN ULONG	_osc_fixedwave;
EXTERN ULONG	_osc_fixedwave_loop;
EXTERN ULONG	_osc_fm;
EXTERN ULONG	_osc_fm_loop;
EXTERN ULONG	_osc_polywave;
EXTERN ULONG	_osc_noise;
EXTERN ULONG	_osc_fx_pw;
EXTERN ULONG	_osc_fx_pan;
EXTERN ULONG	_osc_fx_pitch;
EXTERN ULONG	_env_fx_pw;
EXTERN ULONG	_env_fx_pan;
EXTERN ULONG	_env_fx_pitch;
EXTERN ULONG	_env_slot1;
EXTERN ULONG	_env_slot2;
EXTERN ULONG	_env_slot3;
EXTERN ULONG	_loopenv_slot1;
EXTERN ULONG	_loopenv_slot2;
EXTERN ULONG	_loopenv_slot3;
EXTERN ULONG	_loopenv_skip;
EXTERN ULONG	_lfo_1;
EXTERN ULONG	_lfo_2;
EXTERN ULONG	_dist;
EXTERN ULONG	*_mdtab_reverb;


/*
 *	Prototypes
 */
WORD	write_patch(WORD, BYTE *, WORD);
WORD	write_sect_patchmap(MFILE *);
WORD	write_sect_mdtab(MFILE *, WORD);
WORD	write_sect_envelope(MFILE *, WORD);
WORD	write_sect_sample(MFILE *, WORD);
WORD	write_sect_patch(MFILE *, WORD);
WORD	puts_mdtab(MFILE *, WORD);
WORD	puts_pvalue(MFILE *, WORD, WORD);
VOID	puts_name(BYTE *, BYTE*);
VOID	puts_fname(BYTE *, BYTE*);

/*
 *	Write a patch/performance file
 */

	WORD
write_patch(file_flag, fname, patch_no)
	WORD file_flag;	/* tells us if we want a patch or performance file */
	BYTE *fname;	/* file name */
	WORD patch_no;	/* first patch slot to load into */
{
	BYTE		buf[256];
	REG BYTE	*s, *t;
	MFILE 		*fp;
	WORD		found = FALSE;
	BYTE		str[64];
	WORD		n;
	WORD		ret = 0;


	/*
	 * do file operations on it
	 */
	if ((fp = mfopen(filebuf, "wa")) == NULL) {
		form_alert(FRM_ALERT, FMSTR_FILEOPENERROR);
		return -1;
	}

	switch (file_flag) {
	case PATCH_FILE:
		mfputs(";#ATPATCH - DO NOT REMOVE THIS LINE", fp);
		mfputs(";#", fp);
		mfputs(";# JAZ - JAGUAR AUDIO SYSTEM - PATCH FILE", fp);
		mfputs(";#", fp);
		break;
	case PERF_FILE:
		mfputs(";#ATPERF - DO NOT REMOVE THIS LINE", fp);
		mfputs(";#", fp);
		mfputs(";# JAZ - JAGUAR AUDIO SYSTEM - PERFORMANCE FILE", fp);
		mfputs(";#", fp);
		break;
	}
	mfputs("\t.include \"jaguar.inc\"", fp);
	mfputs("\t.extern OSC_SAMPLER", fp);
	mfputs("\t.extern OSC_SAMPLER_LOOP", fp);
	mfputs("\t.extern OSC_NISAMPLER", fp);
	mfputs("\t.extern OSC_FM", fp);
	mfputs("\t.extern OSC_FIXEDWAVE", fp);
	mfputs("\t.extern OSC_FIXEDWAVE_LOOP", fp);
	mfputs("\t.extern OSC_POLYWAVE", fp);
	mfputs("\t.extern LOOPENV_SLOT1", fp);
	mfputs("\t.extern LOOPENV_SLOT2", fp);
	mfputs("\t.extern LOOPENV_SKIP", fp);
	mfputs("\t.extern LFO_1", fp);
	mfputs("\t.extern OSC_FX_PW", fp);
	mfputs("\t.extern OSC_FX_PAN", fp);
	mfputs("\t.extern DFT_FILTER", fp);
	mfputs("\t.extern MIXER", fp);
	mfputs("\t.extern MDTAB_REVERB", fp);
	mfputs("\t.extern ATTN", fp);
	mfputs("\t.extern VT_RETURN_LOC", fp);
	mfputs("\t.extern DELAY", fp);
	mfputs(" ", fp);
	mfputs("\t.extern FILTER_DEF_1", fp);
	mfputs("\t.extern FILTER_DEF_2", fp);
	mfputs(" ", fp);
	mfputs("\tPATCHMAP\tequ 0", fp);
	mfputs("\tMDTAB\t\tequ 1", fp);
	mfputs("\tENVELOPE\tequ 2", fp);
	mfputs("\tSAMPLE\t\tequ 3", fp);
	mfputs("\tWAVEFORM\tequ 4", fp);
	mfputs("\tFILTER\t\tequ 5", fp);
	mfputs("\tPATCH\t\tequ 6", fp);
	mfputs(" ", fp);
	mfputs(" ", fp);

	if (file_flag == PERF_FILE)
		write_sect_patchmap(fp);
	write_sect_mdtab(fp, file_flag);
	write_sect_envelope(fp, file_flag);
	write_sect_sample(fp, file_flag);
/*	write_sect_waveform(fp); */
/*	write_sect_filter(fp); */
	write_sect_patch(fp, file_flag);
	
	dos_write(fname, (LONG) fp->_cnt);

	mfclose(fp);
	return ret;
}


	WORD
write_sect_patchmap(fp)
	MFILE	*fp;

{
	REG WORD i;
	BYTE buf[256];


	mfputs("\tsection set PATCHMAP", fp);
	mfputs(" ", fp);
	mfputs(";# THE FOLLOWING DATA IS USED BY JAZ ONLY !!!!" ,fp);
	mfputs(";#", fp);
	mfputs(";# CH	PATCH	NOTE RANGE	OFFSET	PRI	VOL	TUNE", fp);
	mfputs(";#", fp);

	for (i = 0; i < ptt_count; i++) {
		sprintf(buf, ";: %d\t%d\t%s\t%s\t%s\t%s\t%s", 
			ptt_table[i].channel - 1,
			ptt_table[i].patch - 1,
			ptt_table[i].s_note,
			ptt_table[i].s_offset,
			ptt_table[i].s_priority,
			ptt_table[i].s_volume,
			ptt_table[i].s_tuning);
			mfputs(buf, fp);
	}
	mfputs(" ", fp);

	return SUCCESS;
}


struct _mdtab_strtab {
	ULONG	*addr;
	BYTE	*label;
} mdtab_strtab[] = {
	{ &_osc_sampler, "OSC_SAMPLER" }, 
	{ &_osc_sampler_loop, "OSC_SAMPLER_LOOP" },
	{ &_osc_nisampler, "OSC_NISAMPLER" }, 
	{ &_osc_fixedwave, "OSC_FIXEDWAVE" }, 
	{ &_osc_fixedwave_loop, "OSC_FIXEDWAVE_LOOP" }, 
	{ &_osc_fm, "OSC_FM" }, 
	{ &_osc_polywave, "OSC_POLYWAVE" },
	{ &_osc_fx_pan, "OSC_FX_PAN" },
	{ &_osc_fx_pw, "OSC_FX_PW" },
	{ &_osc_fx_pitch, "OSC_FX_PITCH" },
	{ &_env_fx_pan, "ENV_FX_PAN" },
	{ &_env_fx_pw, "ENV_FX_PW" },
	{ &_env_fx_pitch, "ENV_FX_PITCH" },
	{ &_loopenv_slot1, "LOOPENV_SLOT1" },
	{ &_loopenv_slot2, "LOOPENV_SLOT2" },
	{ &_loopenv_slot3, "LOOPENV_SLOT3" },
	{ &_loopenv_skip, "LOOPENV_SKIP" },
	{ &_lfo_1, "LFO_1" },
	{ &_dist, "DIST" },
	{ &_dft_filter, "DFT_FILTER" },
	{ &_attn, "ATTN" },
	{ NULL, NULL }
};




	WORD	
write_sect_mdtab(fp, file_flag)
	MFILE *fp;
	WORD file_flag;
{
	REG WORD i, j, n, x;
	WORD f;
	REG struct _mdtab_strtab *p;
	REG MDTAB *md;
	BYTE buf[128];

	mfputs("\tsection set MDTAB", fp);
	mfputs(" ", fp);

	if (file_flag == PERF_FILE) {
		/*
		 * just for NOW: later, we put in real values
		 */
		mfputs("MDTAB_REVERB:", fp);
		if (*((ULONG*)(_mdtab_reverb+1L)) == _reverb) {
			mfputs("\t.dc.l\tDELAY", fp);
			mfputs("\t.dc.l\tREVERB", fp);
		} else if (*_mdtab_reverb == _reverb) {
			mfputs("\t.dc.l\tDELAY", fp);
		}
		mfputs("\t.dc.l\tVT_RETURN_LOC", fp);
		mfputs(" ", fp);
		n = mdtabslot_cnt;
		x = 0;
	} else {
		x = (WORD) (patchinfo_table[curr_pnr].mdtab - mdtab_table);
		n = x + 1;
	}
	
	for (i = x; i < n; i++) {
		md = &mdtab_table[i];
		if (md->free)
			continue;
		sprintf(buf, "MDTAB_%d:", i+1);
		strcpy(md->name, buf);
		md->name[strlen(buf) - 1] = '\0'; /* kill : */
		mfputs(buf, fp);
		for (j = 0; j < N_MDMODULES; j++) {
			for (f = 0, p = mdtab_strtab; p->addr; p++) {
				if (*p->addr == (ULONG) md->modules[j]) {
					sprintf(buf, "\t.dc.l\t%s", p->label);
					mfputs(buf, fp);
					f = 1;
				}
			}
			if (!f) {
				/* error: not found */ ;
			}
			if (*p->addr == _attn)
				break;
		}
		mfputs(" ", fp);
	}
	if (file_flag == PERF_FILE) {
		/*
		 * HACK ALERT:  For NOW we only allow the 10 modules tabs
		 * 		which fit into the DSP!!!!!!!!!!
		 */
		for (; i < 10; i++ ) {
			sprintf(buf, "MDTAB_%d:", i+1);
			mfputs(buf, fp);
			mfputs("\t\t.dc.l\tVT_RETURN_LOC", fp);
			mfputs(" ", fp);
		}
		mfputs("MDTAB_END:", fp);
		mfputs("VT_RET_VALUE:", fp);
		mfputs("vt_ret_value:", fp);
		mfputs("\t.dc.l\tVT_RETURN_LOC", fp);
		mfputs(" ", fp);
	}
	mfputs(" ", fp);
}



	WORD	
write_sect_envelope(fp, file_flag)
	MFILE *fp;
	WORD file_flag;
{
	REG WORD i, j, n, x;
	WORD f;
	ULONG u;
	BYTE buf[128];

	mfputs("\tsection set ENVELOPE", fp);
	mfputs(" ", fp);

	if (file_flag == PERF_FILE) {
		for (i = 0; i < envslot_cnt; i++) {
			if (env_table[i].free)
				continue;
			u = env_table[i].used[0] + env_table[i].used[1] +
			 	env_table[i].used[2] + env_table[i].used[3];

			if ((i == 0) && (u == 0x80000000L))
				continue;	/* default env - special case */
			if (u == 0)
				continue;
	
			sprintf(buf, "ENV_%d:", i);
			strcpy(env_table[i].name, buf);
			env_table[i].name[strlen(buf) - 1] = '\0'; /* kill : */
			mfputs(buf, fp);
			sprintf(buf, ";:\t.dc.l\t$%08lx\t; start value", 
				env_table[i].start_value);
			mfputs(buf, fp);
			n = env_table[i].n_entries;
			for (j = 0; j < n; j++) {
				sprintf(buf, "\t.dc.l\t$%08lx", env_table[i].data[j]);
				mfputs(buf, fp);
			}
			sprintf(buf, ";:\t.dc.l\t$%08lx\t; release slope", 
				env_table[i].release_slope);
			mfputs(buf, fp);
			mfputs(" ", fp);
		}
	} else {
		REG ENVTAB *env;

		n = patchinfo_table[curr_pnr].env_cnt;
		for (i = 0; i < n; i++) {
			env = patchinfo_table[curr_pnr].env[i];
			sprintf(buf, "ENV_%d:", i);
			mfputs(buf, fp);
			sprintf(buf, ";:\t.dc.l\t$%08lx\t; start value", 
				env->start_value);
			mfputs(buf, fp);
			n = env_table[i].n_entries;
			for (j = 0; i < n; j++) {
				sprintf(buf, "\t.dc.l\t$%08lx", env->data[j]);
				mfputs(buf, fp);
			}
			sprintf(buf, ";:\t.dc.l\t$%08lx\t; release slope", 
				env->release_slope);
			mfputs(buf, fp);
			mfputs(" ", fp);
		
		}

	}
	return SUCCESS;
}


	WORD	
write_sect_sample(fp, file_flag)
	MFILE *fp;
	WORD file_flag;
{
	REG WORD i, j, n, x;
	WORD f;
	BYTE buf[128];
	BYTE name[32];
	BYTE fname[256];

	mfputs("\tsection set SAMPLE", fp);
	mfputs(" ", fp);

	if (file_flag == PERF_FILE) {
		for (i = 8 /* skip ROM waves */; i < smpslot_cnt; i++) {
			if (sample_table[i].free)
				continue;
			if ((sample_table[i].used[0] == 0) && (sample_table[i].used[1] == 0)
			 && (sample_table[i].used[2] == 0) && (sample_table[i].used[3] == 0))
				continue;
			mfputs("\t.EVEN", fp);
			puts_name(name, sample_table[i].name);
			sprintf(buf, "%s:", name);
			mfputs(buf, fp);
			puts_fname(fname, sample_table[i].filename);
			sprintf(buf, "\t.incbin \"%s\"", fname);
			mfputs(buf, fp);
			mfputs(" ", fp);
		}
	} else {
		SMPTAB *smp;

		n = patchinfo_table[curr_pnr].smp_cnt;
		for (i = 0; i < n; i++) {
			smp = patchinfo_table[curr_pnr].sample[i];

			mfputs("\t.EVEN", fp);
			puts_name(name, sample_table[i].name);
			sprintf(buf, "%s:", name);
			mfputs(buf, fp);
			puts_fname(fname, sample_table[i].filename);
			sprintf(buf, "\t.incbin \"%s\"", fname);
			mfputs(buf, fp);
			mfputs(" ", fp);		
		}
	}
}

	WORD	
write_sect_patch(fp, file_flag)
	MFILE *fp;
	WORD file_flag;
{
	REG WORD i, j, n, x;
	WORD f;
	BYTE buf[128];
	BYTE name[32];

	mfputs("\tsection set PATCH", fp);
	mfputs(" ", fp);

	if (file_flag == PERF_FILE) {
		sprintf(buf, "patches:");
		mfputs(buf, fp);
		for (i = 0; i < patch_cnt; i++) {
			sprintf(buf, ";#%d", i);
			mfputs(buf, fp);
			puts_name(name, patch_names[i]);
			sprintf(buf, "%s:", name);
			mfputs(buf, fp);
			puts_mdtab(fp, i);
			for (j = 1; j < PATCH_SIZE; j++) {
				puts_pvalue(fp, i, j);
			}

		}
	} else {
		puts_name(name, patch_names[curr_pnr]);
		sprintf(buf, "%s:", name);
		mfputs(buf, fp);
		puts_mdtab(fp, i);
		for (j = 1; j < PATCH_SIZE; j++) {
			puts_pvalue(fp, curr_pnr, j);
		}
	}
	return SUCCESS;
}

	WORD
puts_mdtab(fp, pnr)
	MFILE *fp;
	WORD pnr;
{
	MDTAB *md = patchinfo_table[pnr].mdtab;
	BYTE buf[128];
	
	sprintf(buf, "\t.dc.l\t%s", md->name);
	mfputs(buf, fp);
	return SUCCESS;

}

	WORD
puts_pvalue(fp, pnr, offset)
	MFILE   *fp;
	WORD	pnr;
	WORD 	offset;
{
	REG WORD i, n;
	BYTE name[32];
	BYTE buf[128];


	for (i = 0; i < 8; i++) {
		if (patchtab[pnr][offset] == rom_symbols[i].addr) {
			sprintf(buf, "\t.dc.l\t%s", rom_symbols[i].name);
			mfputs(buf, fp);
			return SUCCESS;
		}
	}
	n = patchinfo_table[pnr].env_cnt;
	for (i = 0; i < n ; i++) {
		if (patchinfo_table[pnr].env_offset[i] == offset) {
			ENVTAB *env = patchinfo_table[pnr].env[i];
	
			puts_name(name, env->name);
			sprintf(buf, "\t.dc.l\t%s", name);
			mfputs(buf, fp);
			return SUCCESS;
		}
	}
	n = patchinfo_table[pnr].smp_cnt;
	for (i = 0; i < n ; i++) {
		if (patchinfo_table[pnr].smp_offset[i] == offset) {
			SMPTAB *smp = patchinfo_table[pnr].sample[i];

			puts_name(name, smp->name);
			sprintf(buf, "\t.dc.l\t%s", name);
			mfputs(buf, fp);
			return SUCCESS;
		}
	}

	if (patchtab[pnr][offset] == _filter_def_1) {
			sprintf(buf, "\t.dc.l\tFILTER_DEF_1");
			mfputs(buf, fp);
			return SUCCESS;
	}
	if (patchtab[pnr][offset] == _filter_def_2) {
			sprintf(buf, "\t.dc.l\tFILTER_DEF_2");
			mfputs(buf, fp);
			return SUCCESS;
	}

	sprintf(buf, "\t.dc.l\t$%08lx", patchtab[pnr][offset]);
	mfputs(buf, fp);
	return SUCCESS;
}

	VOID
puts_name(buf, name)
	REG BYTE *buf;
	REG BYTE *name;
{
	for (; *name; name++, buf++)
		if (*name == ' ')
			*buf = '_';
		else
			*buf = *name;
	*buf = '\0';
}


	VOID
puts_fname(buf, name)
	REG BYTE *buf;
	REG BYTE *name;
{
	for (; *name; name++, buf++)
		if (*name == '\\') {
			*buf++ = '\\';
			*buf = '\\';
		} else
			*buf = *name;
	*buf = '\0';
}


