Changes
MYM
,Created page with "MYM is a file format for ripped PSG tunes, created by Marq/Lieves!Tuore & Fit (marq@iki.fi) == Players == 2 players exist for the Amstrad CPC: one by Andy Cadley and another..."
MYM is a file format for ripped PSG tunes, created by Marq/Lieves!Tuore & Fit (marq@iki.fi)
== Players ==
2 players exist for the Amstrad CPC: one by Andy Cadley and another one by Morpheus.
They can be downloaded at http://www.kameli.net/lt/prod.htm
== File format ==
I have not been able to find a formal description of this file format.
The best we have is the source code of the original converter from YM to MYM files and vice versa.
=== ym2mym.c ===
<pre>
/*
ym2mym.c
Converts _unpacked_ YM tunes to a format better suitable for
MSX1. Supports YM2 and YM3 types well plus YM5 somehow.
30.1.2000 Marq/Lieves!Tuore & Fit (marq@iki.fi)
3.2.2000 - Added a rude YM5 loader. Skips most of the header.
Output format:
Rows in the tune, 16 bits (lobyte first)
For each register, 0 - fragment contains only unchanged data
1 - fragment contains packed data
In a packed fragment, 0 - register value is the same as before
11 - raw register data follows. Only regbits[i]
bits, not full 8
10 - offset + number of bytes from preceding
data. As many bits as are required to hold
fragment offset & counter data (OFFNUM).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REGS 14
#define FRAG 128 /* Nuber of rows to compress at a time */
#define OFFNUM 14 /* Bits needed to store off+num of FRAG */
void writebits(unsigned data,int bits,FILE *f);
int main(int argc,char *argv[])
{
unsigned char *data[REGS]; /* The unpacked YM data */
unsigned current[REGS];
char id[5]={0,0,0,0,0},ym_new=0,
LeOnArD[8]; /* Check string */
FILE *f;
long n,i,row,change,pack,biggest=0,hits,oldrow,remain,offi,
regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}, /* Bits per PSG reg */
/* AND values to mask out extra bits from register data */
regand[REGS]={255,15,255,15, 255,15,31,255, 31,31,31,255, 255,255};
unsigned long length, /* Song length */
ldata; /* Needed in the loader */
if(argc!=3)
{
printf("Usage: ym2mym source.ym destination.mym\n");
printf("Raw YM files only. Uncompress with LHA.\n");
return(EXIT_FAILURE);
}
if((f=fopen(argv[1],"rb"))==NULL)
{
printf("File open error.\n");
return(EXIT_FAILURE);
}
fseek(f,0,SEEK_END);
length=ftell(f)-4;
fseek(f,0,SEEK_SET);
fread(id,1,4,f);
if(strcmp(id,"YM2!")) /* YM2 is ok */
if(strcmp(id,"YM3!")) /* YM3 is ok */
if(strcmp(id,"YM3b")) /* YM3b is ok */
{
if(!strcmp(id,"YM5!")) /* YM5 is ok but needs a */
ym_new=1; /* different loader */
else
{
printf("Unknown file format.\n");
exit(EXIT_FAILURE);
}
}
else
{
fread(id,1,4,f); /* Skip restart for YM3b */
length-=4;
}
if(ym_new) /* New YM5 format loader */
{
fread(LeOnArD,1,8,f); /* Skip checkstring */
for(n=length=0;n<4;n++) /* Number of VBL's */
{
length<<=8;
length+=fgetc(f);
}
length*=REGS;
fread(&ldata,1,3,f); /* Skip first 3 bytes of info */
if(!(fgetc(f)&1))
{
printf("Only interleaved data supported.\n");
return(EXIT_FAILURE);
}
if(fgetc(f) || fgetc(f)) /* Number of digidrums */
{
printf("Digidrums not supported.\n");
return(EXIT_FAILURE);
}
fread(&ldata,1,4,f); /* Skip external freq */
fread(&ldata,1,2,f); /* Skip VBL freq */
fread(&ldata,1,4,f); /* Skip loop position */
fread(&ldata,1,2,f); /* Skip additional data */
while(fgetc(f)) /* Skip song name */
;
while(fgetc(f)) /* Skip author name */
;
while(fgetc(f)) /* Skip comments */
;
}
/* Old YM2/YM3 format loader */
for(n=0;n<REGS;n++) /* Allocate memory & read data */
{
/* Allocate extra fragment to make packing easier */
if((data[n]=malloc(length/REGS+FRAG))==NULL)
{
printf("Out of memory.\n");
return(EXIT_FAILURE);
}
memset(data[n],0,length/REGS+FRAG);
fread(data[n],1,length/REGS,f);
}
if(ym_new) /* Let's mask the extra YM5 data out */
{
for(n=0;n<REGS;n++)
for(row=0;row<length/REGS;row++)
data[n][row]&=regand[n];
}
fclose(f);
if((f=fopen(argv[2],"wb"))==NULL)
{
printf("Cannot open destination file.\n");
return(EXIT_FAILURE);
}
for(n=0;n<REGS;n++) /* Set current values to impossible */
current[n]=0xffff;
fputc(length/REGS&0xff,f); /* Write tune length */
fputc(length/REGS>>8,f);
for(n=0;n<length/REGS;n+=FRAG) /* Go through fragments... */
{
for(i=0;i<REGS;i++) /* ... for each register */
{
for(row=change=0;row<FRAG;row++)
if(data[i][n+row]!=current[i])
change=1;
if(!change) /* No changes in the whole fragment */
{
writebits(0,1,f);
continue; /* Skip the next pass */
}
else
writebits(1,1,f);
for(row=0;row<FRAG;row++)
{
if(data[i][n+row]!=current[i])
{
change=1;
current[i]=data[i][n+row];
biggest=0;
if(n) /* Skip first fragment */
{
offi=0;
remain=FRAG-row;
/* Go through the preceding data and try to find
similar data */
for(oldrow=0;oldrow<FRAG;oldrow++)
{
hits=0;
for(pack=0;pack<remain;pack++)
{
if(data[i][n+row+pack]==data[i][n-FRAG+row+oldrow+pack]
&& oldrow+pack<FRAG)
hits++;
else
break;
}
if(hits>biggest) /* Bigger sequence found */
{
biggest=hits;
offi=oldrow;
}
}
}
if(biggest>1) /* Could we pack data? */
{
row+=biggest-1;
current[i]=data[i][n+row];
writebits(2,2,f);
writebits((offi<<OFFNUM/2)+(biggest-1),OFFNUM,f);
}
else /* Nope, write raw bits */
{
writebits(3,2,f);
writebits(data[i][n+row],regbits[i],f);
}
}
else /* Same as former value, write 0 */
writebits(0,1,f);
}
}
}
writebits(0,0,f); /* Pad to byte size */
fclose(f);
return(EXIT_SUCCESS);
}
/* Writes bits to a file. If bits is 0, pads to byte size. */
void writebits(unsigned data,int bits,FILE *f)
{
static unsigned char byte=0;
static int off=0;
int n;
if(!bits && off)
{
off=byte=0;
fputc(byte,f);
return;
}
/* Go through the bits and write a whole byte if needed */
for(n=0;n<bits;n++)
{
if(data&(1<<bits-1-n))
byte|=0x80>>off;
if(++off==8)
{
fputc(byte,f);
off=byte=0;
}
}
}
/* EOS */
</pre>
<br>
=== mym2ym.c ===
<pre>
/*
mym2ym.c
Converts MYM files back to upacked YM3.
31.1.2000 Marq/Lieves!Tuore & Fit (marq@iki.fi)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REGS 14
#define FRAG 128 /* Nuber of rows to compress at a time */
#define OFFNUM 14 /* Bits needed to store off+num of FRAG */
unsigned readbits(int bits,FILE *f);
int main(int argc,char *argv[])
{
unsigned char *data[REGS], /* The unpacked YM data */
c;
unsigned current[REGS];
FILE *f;
long n,i,row,index,compoff,compnum,
bytes=0,
regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}; /* Bits per PSG reg */
unsigned long rows;
if(argc!=3)
{
printf("Usage: mym2ym source.mym destination.ym\n");
printf("Raw YM files only. Compress with LHA.\n");
return(EXIT_FAILURE);
}
if((f=fopen(argv[1],"rb"))==NULL)
{
printf("File open error.\n");
return(EXIT_FAILURE);
}
rows=fgetc(f); /* Read the number of rows */
rows+=fgetc(f)<<8u;
for(n=0;n<REGS;n++) /* Allocate memory for rows */
{
if((data[n]=malloc(rows+FRAG))==NULL)
{
printf("Out of memory.\n");
exit(EXIT_FAILURE);
}
}
for(n=0;n<rows;n+=FRAG) /* Go through rows... */
{
for(i=0;i<REGS;i++) /* ... and registers */
{
index=0;
if(!readbits(1,f)) /* Totally unchanged fragment */
{
for(row=0;row<FRAG;row++)
data[i][n+row]=current[i];
continue;
}
while(index<FRAG) /* Packed fragment */
{
if(!readbits(1,f)) /* Unchanged register */
{
data[i][n+index]=current[i];
index++;
}
else
{
if(readbits(1,f)) /* Raw data */
{
c=readbits(regbits[i],f);
current[i]=data[i][n+index]=c;
index++;
}
else /* Reference to previous data */
{
compoff=readbits(OFFNUM/2,f)+index;
compnum=readbits(OFFNUM/2,f)+1;
for(row=0;row<compnum;row++)
{
c=data[i][n-FRAG+compoff+row];
data[i][n+index]=current[i]=c;
index++;
}
}
}
}
}
}
fclose(f);
if((f=fopen(argv[2],"wb"))==NULL)
{
printf("Cannot open destination file.\n");
return(EXIT_FAILURE);
}
/* Write uncompressed data to YM3 format */
fwrite("YM2!",1,4,f);
for(n=0;n<REGS;n++)
fwrite(data[n],1,rows,f);
fclose(f);
return(EXIT_SUCCESS);
}
/* Reads bits from a while */
unsigned readbits(int bits,FILE *f)
{
static unsigned char byte;
static int off=7;
unsigned n,data=0;
/* Go through the bits and read a whole byte if needed */
for(n=0;n<bits;n++)
{
data<<=1;
if(++off==8)
{
byte=fgetc(f);
off=0;
}
if(byte&(0x80>>off))
data++;
}
return(data);
}
/* EOS */
</pre>
== Players ==
2 players exist for the Amstrad CPC: one by Andy Cadley and another one by Morpheus.
They can be downloaded at http://www.kameli.net/lt/prod.htm
== File format ==
I have not been able to find a formal description of this file format.
The best we have is the source code of the original converter from YM to MYM files and vice versa.
=== ym2mym.c ===
<pre>
/*
ym2mym.c
Converts _unpacked_ YM tunes to a format better suitable for
MSX1. Supports YM2 and YM3 types well plus YM5 somehow.
30.1.2000 Marq/Lieves!Tuore & Fit (marq@iki.fi)
3.2.2000 - Added a rude YM5 loader. Skips most of the header.
Output format:
Rows in the tune, 16 bits (lobyte first)
For each register, 0 - fragment contains only unchanged data
1 - fragment contains packed data
In a packed fragment, 0 - register value is the same as before
11 - raw register data follows. Only regbits[i]
bits, not full 8
10 - offset + number of bytes from preceding
data. As many bits as are required to hold
fragment offset & counter data (OFFNUM).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REGS 14
#define FRAG 128 /* Nuber of rows to compress at a time */
#define OFFNUM 14 /* Bits needed to store off+num of FRAG */
void writebits(unsigned data,int bits,FILE *f);
int main(int argc,char *argv[])
{
unsigned char *data[REGS]; /* The unpacked YM data */
unsigned current[REGS];
char id[5]={0,0,0,0,0},ym_new=0,
LeOnArD[8]; /* Check string */
FILE *f;
long n,i,row,change,pack,biggest=0,hits,oldrow,remain,offi,
regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}, /* Bits per PSG reg */
/* AND values to mask out extra bits from register data */
regand[REGS]={255,15,255,15, 255,15,31,255, 31,31,31,255, 255,255};
unsigned long length, /* Song length */
ldata; /* Needed in the loader */
if(argc!=3)
{
printf("Usage: ym2mym source.ym destination.mym\n");
printf("Raw YM files only. Uncompress with LHA.\n");
return(EXIT_FAILURE);
}
if((f=fopen(argv[1],"rb"))==NULL)
{
printf("File open error.\n");
return(EXIT_FAILURE);
}
fseek(f,0,SEEK_END);
length=ftell(f)-4;
fseek(f,0,SEEK_SET);
fread(id,1,4,f);
if(strcmp(id,"YM2!")) /* YM2 is ok */
if(strcmp(id,"YM3!")) /* YM3 is ok */
if(strcmp(id,"YM3b")) /* YM3b is ok */
{
if(!strcmp(id,"YM5!")) /* YM5 is ok but needs a */
ym_new=1; /* different loader */
else
{
printf("Unknown file format.\n");
exit(EXIT_FAILURE);
}
}
else
{
fread(id,1,4,f); /* Skip restart for YM3b */
length-=4;
}
if(ym_new) /* New YM5 format loader */
{
fread(LeOnArD,1,8,f); /* Skip checkstring */
for(n=length=0;n<4;n++) /* Number of VBL's */
{
length<<=8;
length+=fgetc(f);
}
length*=REGS;
fread(&ldata,1,3,f); /* Skip first 3 bytes of info */
if(!(fgetc(f)&1))
{
printf("Only interleaved data supported.\n");
return(EXIT_FAILURE);
}
if(fgetc(f) || fgetc(f)) /* Number of digidrums */
{
printf("Digidrums not supported.\n");
return(EXIT_FAILURE);
}
fread(&ldata,1,4,f); /* Skip external freq */
fread(&ldata,1,2,f); /* Skip VBL freq */
fread(&ldata,1,4,f); /* Skip loop position */
fread(&ldata,1,2,f); /* Skip additional data */
while(fgetc(f)) /* Skip song name */
;
while(fgetc(f)) /* Skip author name */
;
while(fgetc(f)) /* Skip comments */
;
}
/* Old YM2/YM3 format loader */
for(n=0;n<REGS;n++) /* Allocate memory & read data */
{
/* Allocate extra fragment to make packing easier */
if((data[n]=malloc(length/REGS+FRAG))==NULL)
{
printf("Out of memory.\n");
return(EXIT_FAILURE);
}
memset(data[n],0,length/REGS+FRAG);
fread(data[n],1,length/REGS,f);
}
if(ym_new) /* Let's mask the extra YM5 data out */
{
for(n=0;n<REGS;n++)
for(row=0;row<length/REGS;row++)
data[n][row]&=regand[n];
}
fclose(f);
if((f=fopen(argv[2],"wb"))==NULL)
{
printf("Cannot open destination file.\n");
return(EXIT_FAILURE);
}
for(n=0;n<REGS;n++) /* Set current values to impossible */
current[n]=0xffff;
fputc(length/REGS&0xff,f); /* Write tune length */
fputc(length/REGS>>8,f);
for(n=0;n<length/REGS;n+=FRAG) /* Go through fragments... */
{
for(i=0;i<REGS;i++) /* ... for each register */
{
for(row=change=0;row<FRAG;row++)
if(data[i][n+row]!=current[i])
change=1;
if(!change) /* No changes in the whole fragment */
{
writebits(0,1,f);
continue; /* Skip the next pass */
}
else
writebits(1,1,f);
for(row=0;row<FRAG;row++)
{
if(data[i][n+row]!=current[i])
{
change=1;
current[i]=data[i][n+row];
biggest=0;
if(n) /* Skip first fragment */
{
offi=0;
remain=FRAG-row;
/* Go through the preceding data and try to find
similar data */
for(oldrow=0;oldrow<FRAG;oldrow++)
{
hits=0;
for(pack=0;pack<remain;pack++)
{
if(data[i][n+row+pack]==data[i][n-FRAG+row+oldrow+pack]
&& oldrow+pack<FRAG)
hits++;
else
break;
}
if(hits>biggest) /* Bigger sequence found */
{
biggest=hits;
offi=oldrow;
}
}
}
if(biggest>1) /* Could we pack data? */
{
row+=biggest-1;
current[i]=data[i][n+row];
writebits(2,2,f);
writebits((offi<<OFFNUM/2)+(biggest-1),OFFNUM,f);
}
else /* Nope, write raw bits */
{
writebits(3,2,f);
writebits(data[i][n+row],regbits[i],f);
}
}
else /* Same as former value, write 0 */
writebits(0,1,f);
}
}
}
writebits(0,0,f); /* Pad to byte size */
fclose(f);
return(EXIT_SUCCESS);
}
/* Writes bits to a file. If bits is 0, pads to byte size. */
void writebits(unsigned data,int bits,FILE *f)
{
static unsigned char byte=0;
static int off=0;
int n;
if(!bits && off)
{
off=byte=0;
fputc(byte,f);
return;
}
/* Go through the bits and write a whole byte if needed */
for(n=0;n<bits;n++)
{
if(data&(1<<bits-1-n))
byte|=0x80>>off;
if(++off==8)
{
fputc(byte,f);
off=byte=0;
}
}
}
/* EOS */
</pre>
<br>
=== mym2ym.c ===
<pre>
/*
mym2ym.c
Converts MYM files back to upacked YM3.
31.1.2000 Marq/Lieves!Tuore & Fit (marq@iki.fi)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REGS 14
#define FRAG 128 /* Nuber of rows to compress at a time */
#define OFFNUM 14 /* Bits needed to store off+num of FRAG */
unsigned readbits(int bits,FILE *f);
int main(int argc,char *argv[])
{
unsigned char *data[REGS], /* The unpacked YM data */
c;
unsigned current[REGS];
FILE *f;
long n,i,row,index,compoff,compnum,
bytes=0,
regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}; /* Bits per PSG reg */
unsigned long rows;
if(argc!=3)
{
printf("Usage: mym2ym source.mym destination.ym\n");
printf("Raw YM files only. Compress with LHA.\n");
return(EXIT_FAILURE);
}
if((f=fopen(argv[1],"rb"))==NULL)
{
printf("File open error.\n");
return(EXIT_FAILURE);
}
rows=fgetc(f); /* Read the number of rows */
rows+=fgetc(f)<<8u;
for(n=0;n<REGS;n++) /* Allocate memory for rows */
{
if((data[n]=malloc(rows+FRAG))==NULL)
{
printf("Out of memory.\n");
exit(EXIT_FAILURE);
}
}
for(n=0;n<rows;n+=FRAG) /* Go through rows... */
{
for(i=0;i<REGS;i++) /* ... and registers */
{
index=0;
if(!readbits(1,f)) /* Totally unchanged fragment */
{
for(row=0;row<FRAG;row++)
data[i][n+row]=current[i];
continue;
}
while(index<FRAG) /* Packed fragment */
{
if(!readbits(1,f)) /* Unchanged register */
{
data[i][n+index]=current[i];
index++;
}
else
{
if(readbits(1,f)) /* Raw data */
{
c=readbits(regbits[i],f);
current[i]=data[i][n+index]=c;
index++;
}
else /* Reference to previous data */
{
compoff=readbits(OFFNUM/2,f)+index;
compnum=readbits(OFFNUM/2,f)+1;
for(row=0;row<compnum;row++)
{
c=data[i][n-FRAG+compoff+row];
data[i][n+index]=current[i]=c;
index++;
}
}
}
}
}
}
fclose(f);
if((f=fopen(argv[2],"wb"))==NULL)
{
printf("Cannot open destination file.\n");
return(EXIT_FAILURE);
}
/* Write uncompressed data to YM3 format */
fwrite("YM2!",1,4,f);
for(n=0;n<REGS;n++)
fwrite(data[n],1,rows,f);
fclose(f);
return(EXIT_SUCCESS);
}
/* Reads bits from a while */
unsigned readbits(int bits,FILE *f)
{
static unsigned char byte;
static int off=7;
unsigned n,data=0;
/* Go through the bits and read a whole byte if needed */
for(n=0;n<bits;n++)
{
data<<=1;
if(++off==8)
{
byte=fgetc(f);
off=0;
}
if(byte&(0x80>>off))
data++;
}
return(data);
}
/* EOS */
</pre>