home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Large Pack of OldSkool DOS MOD Trackers
/
goattracker_2.73.zip
/
src
/
greloc.c
< prev
next >
Wrap
C/C++ Source or Header
|
2014-07-23
|
57KB
|
2,189 lines
//
// GOATTRACKER v2 packer/relocator
//
#define GRELOC_C
#include "goattrk2.h"
#include "membuf.h"
#include "parse.h"
char *playeroptname[] =
{
"Buffered SID-writes",
"Sound effect support",
"Volume change support",
"Store author-info",
"Use zeropage ghostregs",
"Disable optimization",
"Full SID buffering"
};
char *tableleftname[] = {
"mt_wavetbl",
"mt_pulsetimetbl",
"mt_filttimetbl",
"mt_speedlefttbl"};
char *tablerightname[] = {
"mt_notetbl",
"mt_pulsespdtbl",
"mt_filtspdtbl",
"mt_speedrighttbl"};
unsigned char chnused[MAX_CHN];
unsigned char pattused[MAX_PATT];
unsigned char pattmap[MAX_PATT];
unsigned char instrused[MAX_INSTR];
unsigned char instrmap[MAX_INSTR];
unsigned char tableused[MAX_TABLES][MAX_TABLELEN+1];
unsigned char tablemap[MAX_TABLES][MAX_TABLELEN+1];
int pattoffset[MAX_PATT];
int pattsize[MAX_PATT];
int songoffset[MAX_SONGS][MAX_CHN];
int songsize[MAX_SONGS][MAX_CHN];
int tableerror;
int channels;
int fixedparams;
int simplepulse;
int firstnote;
int lastnote;
int patternlastnote;
int nofilter;
int nofiltermod;
int nopulse;
int nopulsemod;
int nowavedelay;
int norepeat;
int notrans;
int noportamento;
int notoneporta;
int novib;
int noinsvib;
int nosetad;
int nosetsr;
int nosetwave;
int nosetwaveptr;
int nosetpulseptr;
int nosetfiltptr;
int nosetfiltcutoff;
int nosetfiltctrl;
int nosetmastervol;
int nofunktempo;
int noglobaltempo;
int nochanneltempo;
int nogate;
int noeffects;
int nowavecmd;
int nofirstwavecmd;
int nocalculatedspeed;
int nonormalspeed;
int nozerospeed;
struct membuf src = STATIC_MEMBUF_INIT;
struct membuf dest = STATIC_MEMBUF_INIT;
#ifdef GT2RELOC
#ifdef __WIN32__
extern FILE *STDOUT, *STDERR;
#else
#define STDOUT stdout
#define STDERR stderr
#endif
extern char packedsongname[MAX_PATHNAME];
#define clearscreen()
#define fliptoscreen()
#define waitkeynoupdate()
#define printtextc(x, y, b) fputs(b, STDERR)
#define printmainscreen()
#endif
void relocator(void)
{
#ifndef GT2RELOC
char packedsongname[MAX_FILENAME];
char packedfilter[MAX_FILENAME];
#endif
unsigned char *packeddata = NULL;
char *playername = "player.s";
int tableerrortype = TYPE_NONE;
int tableerrorcause = CAUSE_NONE;
int tableerrorsource1 = 0;
int tableerrorsource2 = 0;
int patterns = 0;
int songs = 0;
int instruments = 0;
int numlegato = 0;
int numnohr = 0;
int numnormal = 0;
int freenormal;
int freenohr;
int freelegato;
int transuprange = 0;
int transdownrange = 0;
int pattdatasize = 0;
int patttblsize = 0;
int songdatasize = 0;
int songtblsize = 0;
int instrsize = 0;
int wavetblsize = 0;
int pulsetblsize = 0;
int filttblsize = 0;
int speedtblsize = 0;
int playersize = 0;
int packedsize = 0;
FILE *songhandle = NULL;
int selectdone;
int opt = 0;
unsigned char speedcode[] = {0xa2,0x00,0x8e,0x04,0xdc,0xa2,0x00,0x8e,0x05,0xdc};
int c,d,e;
unsigned char patttemp[512];
unsigned char *songwork = NULL;
unsigned char *pattwork = NULL;
unsigned char *instrwork = NULL;
channels = 3;
fixedparams = 1;
simplepulse = 1;
firstnote = MAX_NOTES-1;
lastnote = 0;
patternlastnote = 0;
noeffects = 1;
nogate = 1;
nofilter = 1;
nofiltermod = 1;
nopulse = 1;
nopulsemod = 1;
nowavedelay = 1;
nowavecmd = 1;
norepeat = 1;
notrans = 1;
noportamento = 1;
notoneporta = 1;
novib = 1;
noinsvib = 1;
nosetad = 1;
nosetsr = 1;
nosetwave = 1;
nosetwaveptr = 1;
nosetpulseptr = 1;
nosetfiltptr = 1;
nosetfiltcutoff = 1;
nosetfiltctrl = 1;
nosetmastervol = 1;
nofunktempo = 1;
noglobaltempo = 1;
nochanneltempo = 1;
nofirstwavecmd = 1;
nocalculatedspeed = 1;
nonormalspeed = 1;
nozerospeed = 1;
stopsong();
memset(pattused, 0, sizeof pattused);
memset(instrused, 0, sizeof instrused);
memset(chnused, 0, sizeof chnused);
memset(tableused, 0, sizeof tableused);
memset(tablemap, 0, sizeof tablemap);
tableerror = 0;
membuf_free(&src);
membuf_free(&dest);
// Process song-orderlists
countpatternlengths();
// Calculate amount of songs with nonzero length
for (c = 0; c < MAX_SONGS; c++)
{
if ((songlen[c][0]) &&
(songlen[c][1]) &&
(songlen[c][2]))
{
// See which patterns are used in this song
for (d = 0; d < MAX_CHN; d++)
{
songdatasize += songlen[c][d]+2;
for (e = 0; e < songlen[c][d]; e++)
{
if (songorder[c][d][e] < REPEAT)
{
int f;
int num = songorder[c][d][e];
pattused[num] = 1;
for (f = 0; f < pattlen[num]; f++)
{
if ((pattern[num][f*4] != REST) || (pattern[num][f*4+1]) || (pattern[num][f*4+2]))
chnused[d] = 1;
}
}
else
{
if (songorder[c][d][e] >= TRANSDOWN)
{
notrans = 0;
if (songorder[c][d][e] < TRANSUP)
{
int newtransdownrange = -(songorder[c][d][e] - TRANSUP);
if (newtransdownrange > transdownrange) transdownrange = newtransdownrange;
}
else
{
int newtransuprange = songorder[c][d][e] - TRANSUP;
if (newtransuprange > transuprange) transuprange = newtransuprange;
}
}
else norepeat = 0;
}
}
if (songorder[c][d][songlen[c][d]+1] >= songlen[c][d])
{
sprintf(textbuffer, "ILLEGAL SONG RESTART POSITION! (SUBTUNE %02X, CHANNEL %d)", c, d+1);
clearscreen();
printtextc(MAX_ROWS/2, 15, textbuffer);
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
}
songs++;
}
}
// Optimize amount of used channels
if (!chnused[2])
channels = 2;
if ((!chnused[1]) && (!chnused[2]))
channels = 1;
if (!songs)
{
clearscreen();
printtextc(MAX_ROWS/2, CTITLE, "NO SONGS, NO DATA TO SAVE!");
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
// Build the pattern-mapping
// Instrument 1 is always used
instrused[1] = 1;
for (c = 0; c < MAX_PATT; c++)
{
if (pattused[c])
{
pattmap[c] = patterns;
patterns++;
// See which instruments/tablecommands are used
for (d = 0; d < pattlen[c]; d++)
{
tableerror = 0;
if ((pattern[c][d*4] == KEYOFF) || (pattern[c][d*4] == KEYON))
nogate = 0;
if (pattern[c][d*4+1])
instrused[pattern[c][d*4+1]] = 1;
if (pattern[c][d*4+2])
noeffects = 0;
if ((pattern[c][d*4+2] >= CMD_SETWAVEPTR) && (pattern[c][d*4+2] <= CMD_SETFILTERPTR))
exectable(pattern[c][d*4+2] - CMD_SETWAVEPTR, pattern[c][d*4+3]);
if ((pattern[c][d*4+2] >= CMD_PORTAUP) && (pattern[c][d*4+2] <= CMD_VIBRATO))
{
exectable(STBL, pattern[c][d*4+3]);
calcspeedtest(pattern[c][d*4+3]);
}
if (pattern[c][d*4+2] == CMD_FUNKTEMPO)
exectable(STBL, pattern[c][d*4+3]);
if (pattern[c][d*4+2] == CMD_FUNKTEMPO)
{
nofunktempo = 0;
noglobaltempo = 0;
}
if ((pattern[c][d*4+2] == CMD_SETTEMPO) && ((pattern[c][d*4+3] & 0x7f) < 3)) nofunktempo = 0;
// See, which are the highest/lowest notes used
if ((pattern[c][d*4] >= FIRSTNOTE) && (pattern[c][d*4] <= LASTNOTE))
{
int newfirstnote = pattern[c][d*4] - FIRSTNOTE - transdownrange;
int newlastnote = pattern[c][d*4] - FIRSTNOTE + transuprange;
if (newfirstnote < 0) newfirstnote = 0;
if (newlastnote > MAX_NOTES-1) newlastnote = MAX_NOTES-1;
if (newfirstnote < firstnote) firstnote = newfirstnote;
if (newlastnote > lastnote)
{
patternlastnote = newlastnote;
lastnote = newlastnote;
}
if (newfirstnote > lastnote)
{
patternlastnote = newfirstnote;
lastnote = newfirstnote;
}
}
if ((tableerror) && (!tableerrortype))
{
tableerrortype = tableerror;
tableerrorcause = CAUSE_PATTERN;
tableerrorsource1 = c;
tableerrorsource2 = d;
}
}
}
}
// Count amount of normal, nohr, and legato instruments
// Also see if special first wave parameters are used
for (c = 0; c < MAX_INSTR; c++)
{
if (instrused[c])
{
if (instr[c].gatetimer & 0x40) numlegato++;
else
{
if (instr[c].gatetimer & 0x80) numnohr++;
else numnormal++;
}
if ((!instr[c].firstwave) || (instr[c].firstwave >= 0xfe))
nofirstwavecmd = 0;
}
}
freenormal = 1;
freenohr = freenormal + numnormal;
freelegato = freenohr + numnohr;
// Build the instrument-mapping
for (c = 0; c < MAX_INSTR; c++)
{
if (instrused[c])
{
if (instr[c].gatetimer & 0x40) instrmap[c] = freelegato++;
else
{
if (instr[c].gatetimer & 0x80) instrmap[c] = freenohr++;
else instrmap[c] = freenormal++;
}
instruments++;
for (d = 0; d < MAX_TABLES; d++)
{
tableerror = 0;
exectable(d, instr[c].ptr[d]);
if (d == STBL) calcspeedtest(instr[c].ptr[d]);
if ((tableerror) && (!tableerrortype))
{
tableerrortype = tableerror;
tableerrorcause = CAUSE_INSTRUMENT;
tableerrorsource1 = c;
tableerrorsource2 = d;
}
}
}
}
// Execute tableprograms invoked from wavetable commands
for (c = 0; c < MAX_TABLELEN; c++)
{
if (tableused[WTBL][c+1])
{
if ((ltable[WTBL][c] >= WAVECMD) && (ltable[WTBL][c] <= WAVELASTCMD))
{
d = -1;
tableerror = 0;
switch(ltable[WTBL][c] - WAVECMD)
{
case CMD_PORTAUP:
case CMD_PORTADOWN:
case CMD_TONEPORTA:
case CMD_VIBRATO:
d = STBL;
calcspeedtest(rtable[WTBL][c]);
break;
case CMD_SETPULSEPTR:
d = PTBL;
nopulse = 0;
break;
case CMD_SETFILTERPTR:
d = FTBL;
nofilter = 0;
break;
case CMD_DONOTHING:
case CMD_SETWAVEPTR:
case CMD_FUNKTEMPO:
sprintf(textbuffer, "ILLEGAL WAVETABLE COMMAND (ROW %02X, COMMAND %X)", c+1, ltable[WTBL][c] - WAVECMD);
clearscreen();
printtextc(MAX_ROWS/2, 15, textbuffer);
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
if (d != -1) exectable(d, rtable[WTBL][c]);
if ((tableerror) && (!tableerrortype))
{
tableerrortype = tableerror;
tableerrorcause = CAUSE_WAVECMD;
tableerrorsource1 = c+1;
tableerrorsource2 = d;
}
}
}
}
// Build the table-mapping
for (c = 0; c < MAX_TABLES; c++)
{
int e = 1;
for (d = 0; d < MAX_TABLELEN; d++)
{
if (tableused[c][d+1])
{
tablemap[c][d+1] = e;
e++;
}
}
}
// Check for table errors
if (tableerrorcause)
{
clearscreen();
switch(tableerrortype)
{
case TYPE_JUMP:
sprintf(textbuffer, "TABLE POINTER POINTS TO A JUMP! ");
break;
case TYPE_OVERFLOW:
sprintf(textbuffer, "TABLE EXECUTION OVERFLOWS! ");
break;
}
switch (tableerrorcause)
{
case CAUSE_PATTERN:
sprintf(textbuffer + strlen(textbuffer), "(PATTERN %02X, ROW %02d)", tableerrorsource1, tableerrorsource2);
break;
case CAUSE_WAVECMD:
sprintf(textbuffer + strlen(textbuffer), "WAVETABLE CMD (ROW %02X, ", tableerrorsource1);
goto TABLETYPE;
case CAUSE_INSTRUMENT:
sprintf(textbuffer + strlen(textbuffer), "(INSTRUMENT %02X, ", tableerrorsource1);
TABLETYPE:
switch (tableerrorsource2)
{
case WTBL:
strcat(textbuffer, "WAVE");
break;
case PTBL:
strcat(textbuffer, "PULSE");
break;
case FTBL:
strcat(textbuffer, "FILTER");
break;
}
strcat(textbuffer, ")");
break;
}
printtextc(MAX_ROWS/2, 15, textbuffer);
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
// Find duplicate ranges in tables
for (c = 0; c < MAX_TABLES; c++)
findtableduplicates(c);
// Select playroutine options
#ifndef GT2RELOC
clearscreen();
printblankc(0, 0, 15+16, MAX_COLUMNS);
if (!strlen(loadedsongfilename))
sprintf(textbuffer, "%s Packer/Relocator", programname);
else
sprintf(textbuffer, "%s Packer/Relocator - %s", programname, loadedsongfilename);
textbuffer[MAX_COLUMNS] = 0;
printtext(0, 0, 15+16, textbuffer);
printtext(1, 2, CTITLE, "SELECT PLAYROUTINE OPTIONS: (CURSORS=MOVE/CHANGE, ENTER=ACCEPT, ESC=CANCEL)");
selectdone = 0;
while (!selectdone)
{
for (c = 0; c < MAX_OPTIONS; c++)
{
int color = CNORMAL;
if (opt == c) color = CEDIT;
printtext(1, 3+c, color, playeroptname[c]);
if (playerversion & (PLAYER_BUFFERED << c))
printtext(24, 3+c, color, "Yes");
else
printtext(24, 3+c, color, "No ");
}
fliptoscreen();
waitkeynoupdate();
if (win_quitted)
{
exitprogram = 1;
goto PRCLEANUP;
}
switch(rawkey)
{
case KEY_LEFT:
case KEY_RIGHT:
case KEY_SPACE:
playerversion ^= (PLAYER_BUFFERED << opt);
if (opt)
{
if ((playerversion & PLAYER_SOUNDEFFECTS) || (playerversion & PLAYER_ZPGHOSTREGS) || (playerversion & PLAYER_FULLBUFFERED))
playerversion |= PLAYER_BUFFERED;
}
else
{
if (!(playerversion & PLAYER_BUFFERED))
{
playerversion &= ~PLAYER_SOUNDEFFECTS;
playerversion &= ~PLAYER_ZPGHOSTREGS;
playerversion &= ~PLAYER_FULLBUFFERED;
}
}
break;
case KEY_UP:
opt--;
if (opt < 0) opt = MAX_OPTIONS-1;
break;
case KEY_DOWN:
opt++;
if (opt >= MAX_OPTIONS) opt = 0;
break;
case KEY_ESC:
selectdone = -1;
break;
case KEY_ENTER:
selectdone = 1;
break;
}
}
if (selectdone == -1) goto PRCLEANUP;
#endif
// Disable optimizations if necessary
if (playerversion & PLAYER_NOOPTIMIZATION)
{
fixedparams = 0;
if (!numlegato) numlegato++;
simplepulse = 0;
firstnote = 0;
lastnote = MAX_NOTES-1;
nogate = 0;
noeffects = 0;
nofilter = 0;
nofiltermod = 0;
nopulse = 0;
nopulsemod = 0;
nowavedelay = 0;
nowavecmd = 0;
norepeat = 0;
notrans = 0;
noportamento = 0;
notoneporta = 0;
novib = 0;
noinsvib = 0;
nosetad = 0;
nosetsr = 0;
nosetwave = 0;
nosetwaveptr = 0;
nosetpulseptr = 0;
nosetfiltptr = 0;
nosetfiltcutoff = 0;
nosetfiltctrl = 0;
nosetmastervol = 0;
nofunktempo = 0;
noglobaltempo = 0;
nochanneltempo = 0;
nofirstwavecmd = 0;
nocalculatedspeed = 0;
nonormalspeed = 0;
nozerospeed = 0;
}
// Make sure buffering is used if it is needed
if ((playerversion & PLAYER_SOUNDEFFECTS) || (playerversion & PLAYER_ZPGHOSTREGS) || (playerversion & PLAYER_FULLBUFFERED))
playerversion |= PLAYER_BUFFERED;
// Sound effect or ghostreg players always use full 3 channels
if ((playerversion & PLAYER_SOUNDEFFECTS) || (playerversion & PLAYER_FULLBUFFERED) || (playerversion & PLAYER_ZPGHOSTREGS))
channels = 3;
// Allocate memory for song-orderlists
songtblsize = songs*6;
songwork = malloc(songdatasize);
if (!songwork)
{
clearscreen();
printtextc(MAX_ROWS/2, CTITLE, "OUT OF MEMORY IN PACKER/RELOCATOR!");
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
// Generate songorderlists & songtable
songdatasize = 0;
for (c = 0; c < songs; c++)
{
if ((songlen[c][0]) &&
(songlen[c][1]) &&
(songlen[c][2]))
{
for (d = 0; d < MAX_CHN; d++)
{
songoffset[c][d] = songdatasize;
songsize[c][d] = songlen[c][d] + 2;
for (e = 0; e < songlen[c][d]; e++)
{
// Pattern
if (songorder[c][d][e] < REPEAT)
songwork[songdatasize++] = pattmap[songorder[c][d][e]];
else
{
// Transpose
if (songorder[c][d][e] >= TRANSDOWN)
{
songwork[songdatasize++] = songorder[c][d][e];
}
// Repeat sequence: must be swapped
else
{
// See that repeat amount is more than 1
if (songorder[c][d][e] > REPEAT)
{
// Insanity check that a pattern indeed follows
if (songorder[c][d][e+1] < REPEAT)
{
songwork[songdatasize++] = pattmap[songorder[c][d][e+1]];
songwork[songdatasize++] = songorder[c][d][e];
e++;
}
else
songwork[songdatasize++] = songorder[c][d][e];
}
}
}
}
// Endmark & repeat position
songwork[songdatasize++] = songorder[c][d][e++];
songwork[songdatasize++] = songorder[c][d][e++];
}
}
else
{
for (d = 0; d < MAX_CHN; d++)
{
songoffset[c][d] = songdatasize;
songsize[c][d] = 0;
}
}
}
// Calculate total size of patterns
for (c = 0; c < MAX_PATT; c++)
{
if (pattused[c])
{
int result = packpattern(patttemp, pattern[c], pattlen[c]);
if (result < 0)
{
clearscreen();
sprintf(textbuffer, "PATTERN %02X IS TOO COMPLEX (OVER 256 BYTES PACKED)!", c);
printtextc(MAX_ROWS/2, 15, textbuffer);
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
pattdatasize += result;
}
}
patttblsize = patterns*2;
pattwork = malloc(pattdatasize);
if (!pattwork)
{
clearscreen();
printtextc(MAX_ROWS/2, CTITLE, "OUT OF MEMORY IN PACKER/RELOCATOR!");
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
// This time pack the patterns for real
pattdatasize = 0;
d = 0;
for (c = 0; c < MAX_PATT; c++)
{
if (pattused[c])
{
pattoffset[d] = pattdatasize;
pattsize[d] = packpattern(&pattwork[pattdatasize], pattern[c], pattlen[c]);
pattdatasize += pattsize[d];
d++;
}
}
// Then process instruments
instrsize = instruments*9;
instrwork = malloc(instrsize);
if (!instrwork)
{
clearscreen();
printtextc(MAX_ROWS/2, CTITLE, "OUT OF MEMORY IN PACKER/RELOCATOR!");
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
for (c = 1; c < MAX_INSTR; c++)
{
if (instrused[c])
{
d = instrmap[c] - 1;
instrwork[d] = instr[c].ad;
instrwork[d+instruments] = instr[c].sr;
instrwork[d+instruments*2] = tablemap[WTBL][instr[c].ptr[WTBL]];
instrwork[d+instruments*3] = tablemap[PTBL][instr[c].ptr[PTBL]];
instrwork[d+instruments*4] = tablemap[FTBL][instr[c].ptr[FTBL]];
if (instr[c].vibdelay)
{
instrwork[d+instruments*5] = tablemap[STBL][instr[c].ptr[STBL]];
instrwork[d+instruments*6] = instr[c].vibdelay - 1;
}
else
{
instrwork[d+instruments*5] = 0;
instrwork[d+instruments*6] = 0;
}
instrwork[d+instruments*7] = instr[c].gatetimer & 0x3f;
instrwork[d+instruments*8] = instr[c].firstwave;
if (instr[c].ptr[STBL])
{
novib = 0;
noinsvib = 0;
}
if (instr[c].ptr[PTBL])
nopulse = 0;
if (instr[c].ptr[FTBL])
nofilter = 0;
// See if all instruments use same gatetimer & firstwave parameters
if ((instr[c].gatetimer != instr[1].gatetimer) ||
(instr[c].firstwave != instr[1].firstwave))
fixedparams = 0;
// or if special firstwave commands are in use
if ((!instr[c].firstwave) || (instr[c].firstwave >= 0xfe))
fixedparams = 0;
}
}
// Disable sameparam optimization for multispeed stability
if (multiplier > 1)
{
fixedparams = 0;
numlegato++;
numnohr++;
}
if (fixedparams) instrsize -= instruments*2;
if (noinsvib) instrsize -= instruments*2;
if (nopulse) instrsize -= instruments;
if (nofilter) instrsize -= instruments;
// Process tables
for (c = 0; c < MAX_TABLELEN; c++)
{
if (tableused[WTBL][c+1])
{
wavetblsize += 2;
if ((ltable[WTBL][c] >= WAVEDELAY) && (ltable[WTBL][c] <= WAVELASTDELAY)) nowavedelay = 0;
if ((ltable[WTBL][c] >= WAVECMD) && (ltable[WTBL][c] <= WAVELASTCMD))
{
nowavecmd = 0;
noeffects = 0;
switch (ltable[WTBL][c] - WAVECMD)
{
case CMD_PORTAUP:
case CMD_PORTADOWN:
noportamento = 0;
break;
case CMD_TONEPORTA:
notoneporta = 0;
break;
case CMD_VIBRATO:
novib = 0;
break;
case CMD_SETAD:
nosetad = 0;
break;
case CMD_SETSR:
nosetsr = 0;
break;
case CMD_SETWAVE:
nosetwave = 0;
break;
case CMD_SETPULSEPTR:
nosetpulseptr = 0;
break;
case CMD_SETFILTERPTR:
nosetfiltptr = 0;
break;
case CMD_SETFILTERCUTOFF:
nosetfiltcutoff = 0;
break;
case CMD_SETFILTERCTRL:
nosetfiltctrl = 0;
break;
case CMD_SETMASTERVOL:
nosetmastervol = 0;
break;
}
}
if (ltable[WTBL][c] < WAVECMD)
{
if (rtable[WTBL][c] <= 0x80)
{
int newlastnote = rtable[WTBL][c] + patternlastnote;
if (newlastnote > MAX_NOTES - 1) newlastnote = MAX_NOTES - 1;
if (rtable[WTBL][c] >= 0x20) firstnote = 0;
if (newlastnote > lastnote) lastnote = newlastnote;
}
else
{
int newfirstnote = rtable[WTBL][c] & 0x7f;
int newlastnote = rtable[WTBL][c] & 0x7f;
if (newlastnote > MAX_NOTES - 1) newlastnote = MAX_NOTES - 1;
if (newfirstnote < firstnote) firstnote = newfirstnote;
if (newlastnote > lastnote) lastnote = newlastnote;
}
}
}
}
for (c = 0; c < MAX_TABLELEN; c++)
{
if (tableused[PTBL][c+1])
{
pulsetblsize += 2;
if ((ltable[PTBL][c] >= 0x80) && (ltable[PTBL][c] != 0xff))
{
if (rtable[PTBL][c] & 0xf) simplepulse = 0;
}
if (ltable[PTBL][c] < 0x80)
{
nopulsemod = 0;
if (rtable[PTBL][c] & 0xf) simplepulse = 0;
}
}
}
for (c = 0; c < MAX_TABLELEN; c++)
{
if (tableused[FTBL][c+1])
{
filttblsize += 2;
if (ltable[FTBL][c] < 0x80) nofiltermod = 0;
}
}
for (c = 0; c < MAX_TABLELEN; c++)
{
if (tableused[STBL][c+1]) speedtblsize += 2;
}
// Zero entry of speedtable
if ((!novib) || (!nofunktempo) || (!noportamento) || (!notoneporta))
speedtblsize += 2;
if (nopulse) pulsetblsize = 0;
if (nofilter) filttblsize = 0;
#ifdef GT2RELOC
fprintf(STDOUT, "Player address: $%04X\n", playeradr);
fprintf(STDOUT, "Zeropage address: $%04X\n", zeropageadr);
#else
sprintf(textbuffer, "SELECT START ADDRESS: (CURSORS=MOVE, ENTER=ACCEPT, ESC=CANCEL)");
printtext(1, 11, 15, textbuffer);
selectdone = 0;
while (!selectdone)
{
sprintf(textbuffer, "$%04X", playeradr);
printtext(1, 12, 10, textbuffer);
fliptoscreen();
waitkeynoupdate();
if (win_quitted)
{
exitprogram = 1;
goto PRCLEANUP;
}
switch(rawkey)
{
case KEY_LEFT:
playeradr -= 0x0400;
playeradr &= 0xff00;
break;
case KEY_UP:
playeradr += 0x0100;
playeradr &= 0xff00;
break;
case KEY_RIGHT:
playeradr += 0x0400;
playeradr &= 0xff00;
break;
case KEY_DOWN:
playeradr -= 0x0100;
playeradr &= 0xff00;
break;
case KEY_ESC:
selectdone = -1;
break;
case KEY_ENTER:
selectdone = 1;
break;
}
}
if (selectdone == -1) goto PRCLEANUP;
sprintf(textbuffer, "SELECT ZEROPAGE ADDRESS: (CURSORS=MOVE, ENTER=ACCEPT, ESC=CANCEL)");
printtext(1, 14, 15, textbuffer);
selectdone = 0;
while (!selectdone)
{
if (playerversion & PLAYER_ZPGHOSTREGS)
{
if (zeropageadr < 0x02) zeropageadr = 0xe5;
if (zeropageadr > 0xe5) zeropageadr = 0x02;
}
else
{
if (zeropageadr < 0x02) zeropageadr = 0xfe;
if (zeropageadr > 0xfe) zeropageadr = 0x02;
}
if (!(playerversion & PLAYER_ZPGHOSTREGS))
{
if (zeropageadr < 0x90)
sprintf(textbuffer, "$%02X-$%02X (Used by BASIC interpreter) ", zeropageadr, zeropageadr+1);
if ((zeropageadr >= 0x90) && (zeropageadr < 0xfb))
sprintf(textbuffer, "$%02X-$%02X (Used by KERNAL routines) ", zeropageadr, zeropageadr+1);
if ((zeropageadr >= 0xfb) && (zeropageadr < 0xfe))
sprintf(textbuffer, "$%02X-$%02X (Unused) ", zeropageadr, zeropageadr+1);
if (zeropageadr >= 0xfe)
sprintf(textbuffer, "$%02X-$%02X ($FF used by BASIC interpreter)", zeropageadr, zeropageadr+1);
}
else
{
sprintf(textbuffer, "$%02X-$%02X (ghostregs start at %02X)", zeropageadr, zeropageadr+26, zeropageadr);
}
printtext(1, 15, 10, textbuffer);
fliptoscreen();
waitkeynoupdate();
if (win_quitted)
{
exitprogram = 1;
goto PRCLEANUP;
}
switch(rawkey)
{
case KEY_LEFT:
zeropageadr -= 0x10;
break;
case KEY_UP:
zeropageadr++;
break;
case KEY_RIGHT:
zeropageadr += 0x10;
break;
case KEY_DOWN:
zeropageadr--;
break;
case KEY_ESC:
selectdone = -1;
break;
case KEY_ENTER:
selectdone = 1;
break;
}
}
if (selectdone == -1) goto PRCLEANUP;
#endif
// Validate frequencytable parameters
if (lastnote < firstnote)
lastnote = firstnote;
if (firstnote < 0) firstnote = 0;
if (!nocalculatedspeed)
lastnote++; // Calculated speeds need the next frequency value
if (lastnote > MAX_NOTES-1) lastnote = MAX_NOTES-1;
// For sound effect support, always use the full table
if (playerversion & PLAYER_SOUNDEFFECTS)
{
firstnote = 0;
lastnote = MAX_NOTES-1;
}
// Insert baseaddresses
insertdefine("base", playeradr);
insertdefine("zpbase", zeropageadr);
insertdefine("SIDBASE", sidaddress);
// Insert conditionals
insertdefine("SOUNDSUPPORT", (playerversion & PLAYER_SOUNDEFFECTS) ? 1 : 0);
insertdefine("VOLSUPPORT", (playerversion & PLAYER_VOLUME) ? 1 : 0);
insertdefine("BUFFEREDWRITES", (playerversion & PLAYER_BUFFERED) ? 1 : 0);
insertdefine("GHOSTREGS", (playerversion & (PLAYER_ZPGHOSTREGS|PLAYER_FULLBUFFERED)) ? 1 : 0);
insertdefine("ZPGHOSTREGS", (playerversion & PLAYER_ZPGHOSTREGS) ? 1 : 0);
insertdefine("FIXEDPARAMS", fixedparams);
insertdefine("SIMPLEPULSE", simplepulse);
insertdefine("PULSEOPTIMIZATION", optimizepulse);
insertdefine("REALTIMEOPTIMIZATION", optimizerealtime);
insertdefine("NOAUTHORINFO", (playerversion & PLAYER_AUTHORINFO) ? 0 : 1);
insertdefine("NOEFFECTS", noeffects);
insertdefine("NOGATE", nogate);
insertdefine("NOFILTER", nofilter);
insertdefine("NOFILTERMOD", nofiltermod);
insertdefine("NOPULSE", nopulse);
insertdefine("NOPULSEMOD", nopulsemod);
insertdefine("NOWAVEDELAY", nowavedelay);
insertdefine("NOWAVECMD", nowavecmd);
insertdefine("NOREPEAT", norepeat);
insertdefine("NOTRANS", notrans);
insertdefine("NOPORTAMENTO", noportamento);
insertdefine("NOTONEPORTA", notoneporta);
insertdefine("NOVIB", novib);
insertdefine("NOINSTRVIB", noinsvib);
insertdefine("NOSETAD", nosetad);
insertdefine("NOSETSR", nosetsr);
insertdefine("NOSETWAVE", nosetwave);
insertdefine("NOSETWAVEPTR", nosetwaveptr);
insertdefine("NOSETPULSEPTR", nosetpulseptr);
insertdefine("NOSETFILTPTR", nosetfiltptr);
insertdefine("NOSETFILTCTRL", nosetfiltctrl);
insertdefine("NOSETFILTCUTOFF", nosetfiltcutoff);
insertdefine("NOSETMASTERVOL", nosetmastervol);
insertdefine("NOFUNKTEMPO", nofunktempo);
insertdefine("NOGLOBALTEMPO", noglobaltempo);
insertdefine("NOCHANNELTEMPO", nochanneltempo);
insertdefine("NOFIRSTWAVECMD", nofirstwavecmd);
insertdefine("NOCALCULATEDSPEED", nocalculatedspeed);
insertdefine("NONORMALSPEED", nonormalspeed);
insertdefine("NOZEROSPEED", nozerospeed);
// Insert parameters
insertdefine("NUMCHANNELS", channels);
insertdefine("NUMSONGS", songs);
insertdefine("FIRSTNOTE", firstnote);
insertdefine("FIRSTNOHRINSTR", numnormal + 1);
insertdefine("FIRSTLEGATOINSTR", numnormal + numnohr + 1);
insertdefine("NUMHRINSTR", numnormal);
insertdefine("NUMNOHRINSTR", numnohr);
insertdefine("NUMLEGATOINSTR", numlegato);
insertdefine("ADPARAM", adparam >> 8);
insertdefine("SRPARAM", adparam & 0xff);
if ((instr[MAX_INSTR-1].ad >= 2) && (!(instr[MAX_INSTR-1].ptr[WTBL])))
insertdefine("DEFAULTTEMPO", instr[MAX_INSTR-1].ad - 1);
else
insertdefine("DEFAULTTEMPO", multiplier ? (multiplier*6-1) : 5);
// Fixed firstwave & gatetimer
if (fixedparams)
{
insertdefine("FIRSTWAVEPARAM", instr[1].firstwave);
insertdefine("GATETIMERPARAM", instr[1].gatetimer & 0x3f);
}
// Insert source code of player
if (adparam >= 0xf000)
playername = "altplayer.s";
if (!insertfile(playername))
{
clearscreen();
printtextc(MAX_ROWS/2, CTITLE, "COULD NOT OPEN PLAYROUTINE!");
fliptoscreen();
waitkeynoupdate();
goto PRCLEANUP;
}
// Modify ghostregs to not be zeropage if needed
if ((playerversion & PLAYER_FULLBUFFERED) && (playerversion & PLAYER_ZPGHOSTREGS) == 0)
{
int bufsize = membuf_get_size(&src);
char* bufdata = (char*)membuf_get(&src);
int c;
for (c = 0; c < bufsize; c++)
{
if (bufdata[c] == '<')
{
if (memcmp(bufdata + c + 1, "ghost", 5) == 0)
bufdata[c] = ' ';
}
}
}
// Insert frequencytable
insertlabel("mt_freqtbllo");
insertbytes(&freqtbllo[firstnote], lastnote-firstnote+1);
insertlabel("mt_freqtblhi");
insertbytes(&freqtblhi[firstnote], lastnote-firstnote+1);
// Insert songtable
insertlabel("mt_songtbllo");
for (c = 0; c < songs*3; c++)
{
sprintf(textbuffer, "mt_song%d", c);
insertaddrlo(textbuffer);
}
insertlabel("mt_songtblhi");
for (c = 0; c < songs*3; c++)
{
sprintf(textbuffer, "mt_song%d", c);
insertaddrhi(textbuffer);
}
// Insert patterntable
insertlabel("mt_patttbllo");
for (c = 0; c < patterns; c++)
{
sprintf(textbuffer, "mt_patt%d", c);
insertaddrlo(textbuffer);
}
insertlabel("mt_patttblhi");
for (c = 0; c < patterns; c++)
{
sprintf(textbuffer, "mt_patt%d", c);
insertaddrhi(textbuffer);
}
// Insert instruments
insertlabel("mt_insad");
insertbytes(&instrwork[0], instruments);
insertlabel("mt_inssr");
insertbytes(&instrwork[instruments], instruments);
insertlabel("mt_inswaveptr");
insertbytes(&instrwork[instruments*2], instruments);
if (!nopulse)
{
insertlabel("mt_inspulseptr");
insertbytes(&instrwork[instruments*3], instruments);
}
if (!nofilter)
{
insertlabel("mt_insfiltptr");
insertbytes(&instrwork[instruments*4], instruments);
}
if (!noinsvib)
{
insertlabel("mt_insvibparam");
insertbytes(&instrwork[instruments*5], instruments);
insertlabel("mt_insvibdelay");
insertbytes(&instrwork[instruments*6], instruments);
}
if (!fixedparams)
{
insertlabel("mt_insgatetimer");
insertbytes(&instrwork[instruments*7], instruments);
insertlabel("mt_insfirstwave");
insertbytes(&instrwork[instruments*8], instruments);
}
// Insert tables
for (c = 0; c < MAX_TABLES; c++)
{
if ((c == PTBL) && (nopulse)) goto SKIPTABLE;
if ((c == FTBL) && (nofilter)) goto SKIPTABLE;
// Write table left side
// Extra zero for speedtable
if ((c == STBL) && ((!novib) || (!nofunktempo) || (!noportamento) || (!notoneporta))) insertbyte(0);
// Table label
insertlabel(tableleftname[c]);
// Table data
for (d = 0; d < MAX_TABLELEN; d++)
{
if (tableused[c][d+1])
{
switch (c)
{
// In wavetable, convert waveform values for the playroutine
case WTBL:
{
unsigned char wave = ltable[c][d];
if ((ltable[c][d] >= WAVESILENT) && (ltable[c][d] <= WAVELASTSILENT)) wave &= 0xf;
if ((ltable[c][d] > WAVELASTDELAY) && (ltable[c][d] <= WAVELASTSILENT) && (!nowavedelay)) wave += 0x10;
insertbyte(wave);
}
break;
case PTBL:
if ((simplepulse) && (ltable[c][d] != 0xff) && (ltable[c][d] > 0x80))
insertbyte(0x80);
else
insertbyte(ltable[c][d]);
break;
// In filtertable, modify passband bits
case FTBL:
if ((ltable[c][d] != 0xff) && (ltable[c][d] > 0x80))
insertbyte(((ltable[c][d] & 0x70) >> 1) | 0x80);
else
insertbyte(ltable[c][d]);
break;
default:
insertbyte(ltable[c][d]);
break;
}
}
}
// Write table right side, remapping jumps as necessary
// Extra zero for speedtable
if ((c == STBL) && ((!novib) || (!nofunktempo) || (!noportamento) || (!notoneporta))) insertbyte(0);
// Table label
insertlabel(tablerightname[c]);
for (d = 0; d < MAX_TABLELEN; d++)
{
if (tableused[c][d+1])
{
if ((ltable[c][d] != 0xff) || (c == STBL))
{
switch(c)
{
case WTBL:
if ((ltable[c][d] >= WAVECMD) && (ltable[c][d] <= WAVELASTCMD))
{
// Remap table-referencing commands
switch (ltable[c][d] - WAVECMD)
{
case CMD_PORTAUP:
case CMD_PORTADOWN:
case CMD_TONEPORTA:
case CMD_VIBRATO:
insertbyte(tablemap[STBL][rtable[c][d]]);
break;
case CMD_SETPULSEPTR:
insertbyte(tablemap[PTBL][rtable[c][d]]);
break;
case CMD_SETFILTERPTR:
insertbyte(tablemap[FTBL][rtable[c][d]]);
break;
default:
insertbyte(rtable[c][d]);
break;
}
}
else
{
// For normal notes, reverse all right side high bits
insertbyte(rtable[c][d] ^ 0x80);
}
break;
case PTBL:
if (simplepulse)
{
if (ltable[c][d] >= 0x80)
insertbyte((ltable[c][d] & 0x0f) | (rtable[c][d] & 0xf0));
else
{
int pulsespeed = rtable[c][d] >> 4;
if (rtable[c][d] & 0x80)
{
pulsespeed |= 0xf0;
pulsespeed--;
}
pulsespeed = swapnybbles(pulsespeed);
insertbyte(pulsespeed);
}
}
else
insertbyte(rtable[c][d]);
break;
default:
insertbyte(rtable[c][d]);
break;
}
}
else
insertbyte(tablemap[c][rtable[c][d]]);
}
}
SKIPTABLE: ;
}
// Insert orderlists
for (c = 0; c < songs; c++)
{
for (d = 0; d < MAX_CHN; d++)
{
sprintf(textbuffer, "mt_song%d", c*3+d);
insertlabel(textbuffer);
insertbytes(&songwork[songoffset[c][d]], songsize[c][d]);
}
}
// Insert patterns
for (c = 0; c < patterns; c++)
{
sprintf(textbuffer, "mt_patt%d", c);
insertlabel(textbuffer);
insertbytes(&pattwork[pattoffset[c]], pattsize[c]);
}
/* {
FILE *handle = fopen("debug.s", "wb");
fwrite(membuf_get(&src), membuf_memlen(&src), 1, handle);
fclose(handle);
} */
// Assemble; on error fail in a rude way (the parser does so too)
if (assemble(&src, &dest)) exit(1);
packeddata = membuf_get(&dest);
packedsize = membuf_memlen(&dest);
playersize = packedsize - songtblsize - songdatasize - patttblsize - pattdatasize - instrsize - wavetblsize - pulsetblsize - filttblsize - speedtblsize;
// Copy author info
if (playerversion & PLAYER_AUTHORINFO)
{
for (c = 0; c < 32; c++)
{
packeddata[32+c] = authorname[c];
// Convert 0 to space
if (packeddata[32+c] == 0) packeddata[32+c] = 0x20;
}
}
// Print results
#ifdef GT2RELOC
fprintf(STDOUT, "packing results:\n");
fprintf(STDOUT, "Playroutine: %d bytes\n", playersize);
fprintf(STDOUT, "Songtable: %d bytes\n", songtblsize);
fprintf(STDOUT, "Song-orderlists: %d bytes\n", songdatasize);
fprintf(STDOUT, "Patterntable: %d bytes\n", patttblsize);
fprintf(STDOUT, "Patterns: %d bytes\n", pattdatasize);
fprintf(STDOUT, "Instruments: %d bytes\n", instrsize);
fprintf(STDOUT, "Tables: %d bytes\n", wavetblsize+pulsetblsize+filttblsize+speedtblsize);
fprintf(STDOUT, "Total size: %d bytes\n", packedsize);
songhandle = fopen(packedsongname, "wb");
if (!songhandle)
{
fprintf(STDERR, "error: could not open output file '%s'.\n", packedsongname);
goto PRCLEANUP;
}
#else
clearscreen();
printblankc(0, 0, 15+16, MAX_COLUMNS);
if (!strlen(loadedsongfilename))
sprintf(textbuffer, "%s Packer/Relocator", programname);
else
sprintf(textbuffer, "%s Packer/Relocator - %s", programname, loadedsongfilename);
textbuffer[80] = 0;
printtext(0, 0, 15+16, textbuffer);
sprintf(textbuffer, "PACKING RESULTS:");
printtext(1, 2, 15, textbuffer);
sprintf(textbuffer, "Playroutine: %d bytes", playersize);
printtext(1, 3, 7, textbuffer);
sprintf(textbuffer, "Songtable: %d bytes", songtblsize);
printtext(1, 4, 7, textbuffer);
sprintf(textbuffer, "Song-orderlists: %d bytes", songdatasize);
printtext(1, 5, 7, textbuffer);
sprintf(textbuffer, "Patterntable: %d bytes", patttblsize);
printtext(1, 6, 7, textbuffer);
sprintf(textbuffer, "Patterns: %d bytes", pattdatasize);
printtext(1, 7, 7, textbuffer);
sprintf(textbuffer, "Instruments: %d bytes", instrsize);
printtext(1, 8, 7, textbuffer);
sprintf(textbuffer, "Tables: %d bytes", wavetblsize+pulsetblsize+filttblsize+speedtblsize);
printtext(1, 9, 7, textbuffer);
sprintf(textbuffer, "Total size: %d bytes", packedsize);
printtext(1, 11, 7, textbuffer);
fliptoscreen();
// Now ask for fileformat
printtext(1, 13, CTITLE, "SELECT FORMAT TO SAVE IN: (CURSORS=MOVE, ENTER=ACCEPT, ESC=CANCEL)");
selectdone = 0;
while (!selectdone)
{
switch(fileformat)
{
case FORMAT_SID:
printtext(1, 14, CEDIT, "SID - SIDPlay music file format ");
strcpy(packedfilter, "*.sid");
break;
case FORMAT_PRG:
printtext(1, 14, CEDIT, "PRG - C64 native format ");
strcpy(packedfilter, "*.prg");
break;
case FORMAT_BIN:
printtext(1, 14, CEDIT, "BIN - Raw binary format (no startaddress)");
strcpy(packedfilter, "*.bin");
break;
}
fliptoscreen();
waitkeynoupdate();
if (win_quitted)
{
exitprogram = 1;
goto PRCLEANUP;
}
switch(rawkey)
{
case KEY_LEFT:
case KEY_DOWN:
fileformat--;
if (fileformat < FORMAT_SID) fileformat = FORMAT_BIN;
break;
case KEY_RIGHT:
case KEY_UP:
fileformat++;
if (fileformat > FORMAT_BIN) fileformat = FORMAT_SID;
break;
case KEY_ESC:
selectdone = -1;
break;
case KEY_ENTER:
selectdone = 1;
break;
}
}
if (selectdone == -1) goto PRCLEANUP;
// By default, copy loaded song name up to the extension
memset(packedsongname, 0, sizeof packedsongname);
for (c = 0; c < strlen(loadedsongfilename); c++)
{
if (loadedsongfilename[c] == '.') break;
packedsongname[c] = loadedsongfilename[c];
}
switch (fileformat)
{
case FORMAT_PRG:
strcat(packedsongname, ".prg");
break;
case FORMAT_BIN:
strcat(packedsongname, ".bin");
break;
case FORMAT_SID:
strcat(packedsongname, ".sid");
break;
}
// Now ask for filename, retry if unsuccessful
while (!songhandle)
{
if (!fileselector(packedsongname, packedpath, packedfilter, "Save Music+Playroutine", 3))
goto PRCLEANUP;
if (strlen(packedsongname) < MAX_FILENAME-4)
{
int extfound = 0;
for (c = strlen(packedsongname)-1; c >= 0; c--)
{
if (packedsongname[c] == '.') extfound = 1;
}
if (!extfound)
{
switch (fileformat)
{
case FORMAT_PRG:
strcat(packedsongname, ".prg");
break;
case FORMAT_BIN:
strcat(packedsongname, ".bin");
break;
case FORMAT_SID:
strcat(packedsongname, ".sid");
break;
}
}
}
songhandle = fopen(packedsongname, "wb");
}
#endif
if (fileformat == FORMAT_PRG)
{
fwritele16(songhandle, playeradr);
}
if (fileformat == FORMAT_SID)
{
unsigned char ident[] = {'P', 'S', 'I', 'D', 0x00, 0x02, 0x00, 0x7c};
unsigned char byte;
// Identification
fwrite(ident, sizeof ident, 1, songhandle);
// Load address
byte = 0x00;
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
// Init address
if ((multiplier > 1) || (!multiplier))
{
unsigned speedvalue;
byte = (playeradr-10) >> 8;
fwrite8(songhandle, byte);
byte = (playeradr-10) & 0xff;
fwrite8(songhandle, byte);
if (multiplier)
{
if (ntsc) speedvalue = 0x42c6/multiplier;
else speedvalue = 0x4cc7/multiplier;
}
else
{
if (ntsc) speedvalue = 0x42c6*2;
else speedvalue = 0x4cc7*2;
}
speedcode[1] = speedvalue & 0xff;
speedcode[6] = speedvalue >> 8;
}
else
{
byte = (playeradr) >> 8;
fwrite8(songhandle, byte);
byte = (playeradr) & 0xff;
fwrite8(songhandle, byte);
}
// Play address
byte = (playeradr+3) >> 8;
fwrite8(songhandle, byte);
byte = (playeradr+3) & 0xff;
fwrite8(songhandle, byte);
// Number of subtunes
byte = 0x00;
fwrite8(songhandle, byte);
byte = songs;
fwrite8(songhandle, byte);
// Default subtune
byte = 0x00;
fwrite8(songhandle, byte);
byte = 0x01;
fwrite8(songhandle, byte);
// Song speed bits
byte = 0x00;
if ((ntsc) || (multiplier > 1) || (!multiplier)) byte = 0xff;
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
// Songname etc.
fwrite(songname, sizeof songname, 1, songhandle);
fwrite(authorname, sizeof authorname, 1, songhandle);
fwrite(copyrightname, sizeof copyrightname, 1, songhandle);
// Flags
byte = 0x00;
fwrite8(songhandle, byte);
if (ntsc) byte = 8;
else byte = 4;
if (sidmodel) byte |= 32;
else byte |= 16;
fwrite8(songhandle, byte);
// Reserved longword
byte = 0x00;
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
fwrite8(songhandle, byte);
// Load address
if ((multiplier > 1) || (!multiplier))
{
byte = (playeradr-10) & 0xff;
fwrite8(songhandle, byte);
byte = (playeradr-10) >> 8;
fwrite8(songhandle, byte);
}
else
{
byte = (playeradr) & 0xff;
fwrite8(songhandle, byte);
byte = (playeradr) >> 8;
fwrite8(songhandle, byte);
}
if ((multiplier > 1) || (!multiplier)) fwrite(speedcode, 10, 1, songhandle);
}
fwrite(packeddata, packedsize, 1, songhandle);
fclose(songhandle);
PRCLEANUP:
membuf_free(&src);
membuf_free(&dest);
if (pattwork) free(pattwork);
if (songwork) free(songwork);
if (instrwork) free(instrwork);
printmainscreen();
key = 0;
rawkey = 0;
}
int packpattern(unsigned char *dest, unsigned char *src, int rows)
{
unsigned char temp1[MAX_PATTROWS*4];
unsigned char temp2[512];
unsigned char instr = 0;
int command = -1;
int databyte = -1;
int destsizeim = 0;
int destsize = 0;
int c, d;
// First optimize instrument changes
for (c = 0; c < rows; c++)
{
if ((c) && (src[c*4+1]) && (src[c*4+1] == instr))
{
temp1[c*4] = src[c*4];
temp1[c*4+1] = 0;
temp1[c*4+2] = src[c*4+2];
temp1[c*4+3] = src[c*4+3];
}
else
{
temp1[c*4] = src[c*4];
temp1[c*4+1] = src[c*4+1];
temp1[c*4+2] = src[c*4+2];
temp1[c*4+3] = src[c*4+3];
if (src[c*4+1])
instr = src[c*4+1];
}
switch(temp1[c*4+2])
{
// Remap speedtable commands
case CMD_PORTAUP:
case CMD_PORTADOWN:
noportamento = 0;
temp1[c*4+3] = tablemap[STBL][temp1[c*4+3]];
break;
case CMD_TONEPORTA:
notoneporta = 0;
temp1[c*4+3] = tablemap[STBL][temp1[c*4+3]];
break;
case CMD_VIBRATO:
novib = 0;
temp1[c*4+3] = tablemap[STBL][temp1[c*4+3]];
break;
case CMD_SETAD:
nosetad = 0;
break;
case CMD_SETSR:
nosetsr = 0;
break;
case CMD_SETWAVE:
nosetwave = 0;
break;
// Remap table commands
case CMD_SETWAVEPTR:
nosetwaveptr = 0;
temp1[c*4+3] = tablemap[WTBL][temp1[c*4+3]];
break;
case CMD_SETPULSEPTR:
nosetpulseptr = 0;
nopulse = 0;
temp1[c*4+3] = tablemap[PTBL][temp1[c*4+3]];
break;
case CMD_SETFILTERPTR:
nosetfiltptr = 0;
nofilter = 0;
temp1[c*4+3] = tablemap[FTBL][temp1[c*4+3]];
break;
case CMD_SETFILTERCTRL:
nosetfiltctrl = 0;
nofilter = 0;
break;
case CMD_SETFILTERCUTOFF:
nosetfiltcutoff = 0;
nofilter = 0;
break;
case CMD_SETMASTERVOL:
nosetmastervol = 0;
// If no authorinfo being saved, erase timingmarks (not supported)
if (!(playerversion & PLAYER_AUTHORINFO))
{
if (temp1[c*4+3] > 0x0f)
{
temp1[c*4+2] = 0;
temp1[c*4+3] = 0;
}
}
break;
case CMD_FUNKTEMPO:
nofunktempo = 0;
temp1[c*4+3] = tablemap[STBL][temp1[c*4+3]];
break;
case CMD_SETTEMPO:
if (temp1[c*4+3] >= 0x80) nochanneltempo = 0;
else noglobaltempo = 0;
// Decrease databyte of all tempo commands for the playroutine
// Do not touch funktempo
if ((temp1[c*4+3] & 0x7f) >= 3)
temp1[c*4+3]--;
break;
}
}
if (noeffects)
{
command = 0;
databyte = 0;
}
// Write in playroutine format
for (c = 0; c < rows; c++)
{
// Instrument change with mapping
if (temp1[c*4+1])
{
temp2[destsizeim++] = instrmap[INSTRCHG+temp1[c*4+1]];
}
// Rest+FX
if (temp1[c*4] == REST)
{
if ((temp1[c*4+2] != command) || (temp1[c*4+3] != databyte))
{
command = temp1[c*4+2];
databyte = temp1[c*4+3];
temp2[destsizeim++] = FXONLY+command;
if (command)
temp2[destsizeim++] = databyte;
}
else
temp2[destsizeim++] = REST;
}
else
{
// Normal note
if ((temp1[c*4+2] != command) || (temp1[c*4+3] != databyte))
{
command = temp1[c*4+2];
databyte = temp1[c*4+3];
temp2[destsizeim++] = FX+command;
if (command)
temp2[destsizeim++] = databyte;
}
temp2[destsizeim++] = temp1[c*4];
}
}
// Final step: optimize long singlebyte rests with "packed rest"
for (c = 0; c < destsizeim;)
{
int packok = 1;
// Never pack first row or sequencer goes crazy
if (!c) packok = 0;
// There must be no instrument or command changes on the row to be packed
if (temp2[c] < FX)
{
dest[destsize++] = temp2[c++];
packok = 0;
}
if ((temp2[c] >= FXONLY) && (temp2[c] < FIRSTNOTE))
{
int fxnum = temp2[c] - FXONLY;
dest[destsize++] = temp2[c++];
if (fxnum) dest[destsize++] = temp2[c++];
packok = 0;
goto NEXTROW;
}
if (temp2[c] < FXONLY)
{
int fxnum = temp2[c] - FX;
dest[destsize++] = temp2[c++];
if (fxnum) dest[destsize++] = temp2[c++];
packok = 0;
}
if (temp2[c] != REST) packok = 0;
if (!packok)
dest[destsize++] = temp2[c++];
else
{
for (d = c; d < destsizeim; )
{
if (temp2[d] == REST)
{
d++;
if (d-c == 64) break;
}
else break;
}
d -= c;
if (d > 1)
{
dest[destsize++] = -d;
c += d;
}
else
dest[destsize++] = temp2[c++];
}
NEXTROW: {}
}
// See if pattern too big
if (destsize > 256) return -1;
// If less than 256 bytes, insert endmark
if (destsize < 256)
dest[destsize++] = 0x00;
return destsize;
}
int testoverlap(int area1start, int area1size, int area2start, int area2size)
{
int area1last = area1start+area1size-1;
int area2last = area2start+area2size-1;
if (area1start == area2start) return 1;
if (area1start < area2start)
{
if (area1last < area2start) return 0;
else return 1;
}
else
{
if (area2last < area1start) return 0;
else return 1;
}
}
unsigned char swapnybbles(unsigned char n)
{
unsigned char highnybble = n >> 4;
unsigned char lownybble = n & 0xf;
return (lownybble << 4) | highnybble;
}
int insertfile(char *name)
{
int size;
int handle = io_open(name);
if (handle == -1) return 0;
size = io_lseek(handle, 0, SEEK_END);
io_lseek(handle, 0, SEEK_SET);
while (size--)
{
membuf_append_char(&src, io_read8(handle));
}
io_close(handle);
return 1;
}
void inserttext(const char *text)
{
membuf_append(&src, text, strlen(text));
}
void insertdefine(const char *name, int value)
{
char insertbuffer[80];
sprintf(insertbuffer, "%-16s = %d\n", name, value);
inserttext(insertbuffer);
}
void insertlabel(const char *name)
{
char insertbuffer[80];
sprintf(insertbuffer, "%s:\n", name);
inserttext(insertbuffer);
}
void insertbytes(const unsigned char *bytes, int size)
{
char insertbuffer[80];
int row = 0;
while (size--)
{
if (!row)
{
inserttext(" .BYTE (");
sprintf(insertbuffer, "$%02x", *bytes);
inserttext(insertbuffer);
bytes++;
row++;
}
else
{
sprintf(insertbuffer, ",$%02x", *bytes);
inserttext(insertbuffer);
bytes++;
row++;
if (row == MAX_BYTES_PER_ROW)
{
inserttext(")\n");
row = 0;
}
}
}
if (row) inserttext(")\n");
}
void insertbyte(unsigned char byte)
{
char insertbuffer[80];
sprintf(insertbuffer, " .BYTE ($%02x)\n", byte);
inserttext(insertbuffer);
}
void insertaddrlo(const char *name)
{
char insertbuffer[80];
sprintf(insertbuffer, " .BYTE (%s %% 256)\n", name);
inserttext(insertbuffer);
}
void insertaddrhi(const char *name)
{
char insertbuffer[80];
sprintf(insertbuffer, " .BYTE (%s / 256)\n", name);
inserttext(insertbuffer);
}
void findtableduplicates(int num)
{
int c,d,e;
if (num == STBL)
{
for (c = 1; c <= MAX_TABLELEN; c++)
{
if (tableused[num][c])
{
for (d = c+1; d <= MAX_TABLELEN; d++)
{
if (tableused[num][d])
{
if ((ltable[num][d-1] == ltable[num][c-1]) && (rtable[num][d-1] == rtable[num][c-1]))
{
// Duplicate found, remove and map to the original
tableused[num][d] = 0;
for (e = d; e <= MAX_TABLELEN; e++)
if (tableused[num][e]) tablemap[num][e]--;
tablemap[num][d] = tablemap[num][c];
}
}
}
}
}
}
else
{
for (c = 1; c <= MAX_TABLELEN; c++)
{
if (isusedandselfcontained(num, c))
{
for (d = c + gettablepartlen(num, c - 1); d <= MAX_TABLELEN; )
{
int len = gettablepartlen(num, d - 1);
if (isusedandselfcontained(num, d))
{
for (e = 0; e < len; e++)
{
if (e < len-1)
{
// Is table data the same?
if ((ltable[num][d+e-1] != ltable[num][c+e-1]) || (rtable[num][d+e-1] != rtable[num][c+e-1]))
break;
}
else
{
// Do both parts have a jump in the end?
if (ltable[num][d+e-1] != ltable[num][c+e-1])
break;
// Do both parts end?
if (rtable[num][d+e-1] == 0)
{
if (rtable[num][c+e-1] != 0)
break;
}
else
{
// Do both parts loop in the same way?
if ((rtable[num][d+e-1] - d) != (rtable[num][c+e-1] - c))
break;
}
}
}
if (e == len)
{
// Duplicate found, remove and map to the original
for (e = 0; e < len; e++)
tableused[num][d+e] = 0;
for (e = d; e < MAX_TABLELEN; e++)
if (tableused[num][e]) tablemap[num][e] -= len;
for (e = 0; e < len; e++)
tablemap[num][d+e] = tablemap[num][c+e];
}
}
d += len;
}
}
}
}
}
int isusedandselfcontained(int num, int start)
{
int len = gettablepartlen(num, start - 1);
int end = start + len - 1;
int c;
// Don't use jumps only
if (len == 1) return 0;
// Check that whole table is used
for (c = start; c <= end; c++)
{
if (tableused[num][c] == 0) return 0;
}
// Check for jump to outside
if (rtable[num][end-1] != 0)
{
if ((rtable[num][end-1] < start) || (rtable[num][end-1] > end)) return 0;
}
// Check for jump from outside
for (c = 1; c < start; c++)
if ((tableused[num][c]) && (ltable[num][c-1] == 0xff) && (rtable[num][c-1] >= start) && (rtable[num][c-1] <= end)) return 0;
for (c = end+1; c <= MAX_TABLELEN; c++)
if ((tableused[num][c]) && (ltable[num][c-1] == 0xff) && (rtable[num][c-1] >= start) && (rtable[num][c-1] <= end)) return 0;
// OK!
return 1;
}
void calcspeedtest(unsigned char pos)
{
if (!pos)
{
nozerospeed = 0;
return;
}
if (ltable[STBL][pos-1] >= 0x80) nocalculatedspeed = 0;
else nonormalspeed = 0;
}