/* MED - music editor © 1989, 1990 by Teijo Kinnunen */
/* med-objsav.c: Object file saving routine */

#include "med.h"
#include "medproto.h"

#define	ERR return(DISKERR)
extern struct Kappale far song;
extern struct Soitin *sample[];
extern struct Lohko *lohko[];
extern UBYTE blocks;
extern BPTR fh;

static BOOL __regargs FWrite(char *,long);
static BOOL __regargs WriteLong(ULONG);
static ULONG __regargs NofLWs(ULONG);
static BOOL __regargs WriteName(char *);
static BOOL __regargs pad(ULONG);

static BOOL __regargs FWrite(char *ptr,long len)
{
	return((BOOL)(Write(fh,ptr,len) != len));
}

static BOOL __regargs WriteLong(ULONG lw)
{
	return(FWrite((char *)(&lw),4));
}

static ULONG __regargs NofLWs(ULONG num)
{
	while(num % 4) num++;
	num /= 4;
	return(num);
}

static BOOL __regargs WriteName(char *name)
{
	ULONG	namelen = NofLWs(strlen(name));
	UBYTE	padn = ((UBYTE)namelen * 4) - strlen(name),padcnt,zero = 0;
	if(WriteLong(namelen)) return(TRUE);
	if(FWrite(name,strlen(name))) return(TRUE);
	for(padcnt = 0; padcnt < padn; padcnt++) {
		if(FWrite(&zero,1)) return(TRUE);
	}
	return(FALSE);
}

static BOOL __regargs pad(ULONG size)
{
	ULONG paddedsize = NofLWs(size) * 4;
	UBYTE zero = 0;
	while(size++ < paddedsize) if(FWrite(&zero,1)) return(TRUE);
	return(FALSE);
}

char *WriteObj(char *name,UWORD objtype)
{
	char fname2[52];
	struct MMD0 mod;
	ULONG instrsiz = 0,blksiz = 0,bpsiz,ioffstbl[63],blkoffstbl[100] = {0};
	ULONG reloffs = 0,size3rd;
	UBYTE scnt,nofinstrs = 0,oldexists = 0,maxinstr = 0;
	struct MMD0song *sng;
	if(*name == '\0') return(AskName());
	strcpy(fname2,name);
	scnt = strlen(fname2);
	if(scnt > 2 && fname2[scnt - 1] == 'o' && fname2[scnt - 2] == '.');
	else strcat(fname2,".o");
	if(IsHere(fname2)) {
		Ilmoita(fileex);
		oldexists = 1;
		if(!Continue()) return(notsaved);
	}
	if(!(fh = Open2(fname2,MODE_NEWFILE))) ERR;
	Ilmoita("Saving object file...");
/* -------- First: write hunk_unit, which contains the name of this unit */
	if(WriteLong(0x000003E7)) ERR; /* hunk_unit */
	if(WriteName(fname2)) ERR;
/* ----------- Write the first hunk, which contains the instruments */
	if(objtype == 0 && WriteLong(0x400003EA)) ERR; /* hunk_data (chip) */
	else if(objtype == 1 && WriteLong(0x000003EA)) ERR;
	for(scnt = 0; scnt < 63; scnt++) {
		if(sample[scnt]) {
			ioffstbl[scnt] = instrsiz;
			instrsiz += sizeof(struct Soitin);
			instrsiz += (sample[scnt]->length & 0xfffffffe);
			nofinstrs++;
			maxinstr = scnt + 1;
		} else ioffstbl[scnt] = 0L;
	}
	for(scnt = 0; scnt < blocks; scnt++) {
		blkoffstbl[scnt] = blksiz;
		blksiz += MMDBLKHDRSZ + 3 * lohko[scnt]->numtracks *
			(lohko[scnt]->lines + 1);
	}
	bpsiz = 4 * blocks; /* size of block pointers */
	if(WriteLong(NofLWs(instrsiz) | (objtype ? 0x40000000 : 0))) ERR;
	for(scnt = 0; scnt < 63; scnt++) {
		if(sample[scnt]) {
			if(FWrite((char *)sample[scnt],sizeof(struct Soitin)
				+ (sample[scnt]->length & 0xfffffffe))) ERR;
		}
	}
	if(pad(instrsiz)) ERR;
	if(WriteLong(0x000003F2)) ERR; /* hunk_end */
/* ---------- Then write the second hunk, music */
	if(WriteLong(0x000003EA)) ERR; /* hunk_data */
	if(WriteLong(NofLWs(blksiz))) ERR;
	for(scnt = 0; scnt < blocks; scnt++) {
		if(FWrite((char *)(lohko[scnt]),MMDBLKHDRSZ)) ERR;
		if(FWrite(lohko[scnt]->music,3 * lohko[scnt]->numtracks *
			(lohko[scnt]->lines + 1))) ERR;
	}
	if(pad(blksiz)) ERR;
	if(WriteLong(0x000003F2)) ERR; /* hunk_end */
/* --------- And finally the third hunk, which contains everything else */
	size3rd = 4 * maxinstr + bpsiz + sizeof(struct MMD0song) +
		sizeof(struct MMD0);
	if(WriteLong(0x000003EA)) ERR; /* hunk_data */
	if(WriteLong(NofLWs(size3rd))) ERR;
/* - write block pointers */
	if(FWrite((char *)blkoffstbl,bpsiz)) ERR;
/* - write sample ptrs */
	if(maxinstr && FWrite((char *)ioffstbl,maxinstr * 4)) ERR;
/* - create & write MMD0song */
	if(!(sng = MakeMMD0song())) return(nomem);
	sng->numsamples = maxinstr;
	if(FWrite((char *)sng,sizeof(struct MMD0song))) {
		FreeMem(sng,sizeof(struct MMD0song));
		ERR;
	}
	FreeMem(sng,sizeof(struct MMD0song));
/* - then build the module structure */
	memset((void *)&mod,0,sizeof(mod));
	mod.id = ((long)'M'<<24|(long)'M'<<16|(long)'D'<<8|(long)'0');
	mod.actplayline = -1;
	/* block ptr table offset = 0, no need to initialize */
	mod.smplarr = (struct Soitin **)bpsiz;
	mod.song = (struct MMD0song *)(bpsiz + maxinstr * 4);
/* - and write it */
	if(FWrite((char *)&mod,sizeof(struct MMD0))) ERR;
	if(pad(size3rd)) ERR;
/* --------- We need to relocate something */
	if(WriteLong(0x000003EC)) ERR; /* hunk_reloc32 */
/* reloc block ptrs */
	if(WriteLong((ULONG)blocks)) ERR;
	if(WriteLong(0x00000001)) ERR; /* Music in hunk #1 */
	for(scnt = 0; scnt < blocks; scnt++, reloffs += 4) {
		if(WriteLong(reloffs)) ERR;
	}
	if(nofinstrs) {
		if(WriteLong((ULONG)nofinstrs)) ERR;
		if(WriteLong(0x00000000)) ERR; /* Instruments in hunk #0 */
		for(scnt = 0; scnt < maxinstr; scnt++, reloffs += 4)
			if(sample[scnt]) {
				if(WriteLong(reloffs)) ERR;
			}
	}
	reloffs += sizeof(struct MMD0song); /* skip the song */
	if(WriteLong(3)) ERR; /* relocate block ptrs, sample ptrs & song */
	if(WriteLong(0x00000002)) ERR; /* they're in hunk #2 */
	if(WriteLong(reloffs + 8)) ERR; /* offset 8 = song ptr */
	if(WriteLong(reloffs + 16)) ERR; /* 16 = block table ptr */
	if(WriteLong(reloffs + 24)) ERR; /* 24 = sample table ptr */
	if(WriteLong(0x00000000)) ERR; /* end */
/* -------- And dump the external symbols defined in this file */
	if(WriteLong(0x000003EF)) ERR; /* hunk_ext */
	size3rd = NofLWs(strlen(name)+1);
	if(WriteLong(0x01000000|size3rd)) ERR;
	if(FWrite("_",1)) ERR; /* leading _ for C.. */
	if(FWrite(name,strlen(name))) ERR;
	if(pad(strlen(name)+1)) ERR;
	if(WriteLong(reloffs)) ERR; /* remember? reloffs = offset of MMD0 */
	if(WriteLong(0x00000000)) ERR;
	if(WriteLong(0x000003F2)) ERR; /* hunk_end */
	Close(fh); fh = 0L; /* huh, huh !! */
	if(!oldexists) InsertSavedFile(fname2);
	Ilmoita("Saved.");
	return(NOERR);
}
