Difference between revisions of "MYM"

From CPCWiki - THE Amstrad CPC encyclopedia!
Jump to: navigation, search
(File format)
Line 11: Line 11:
 
== File format ==
 
== File format ==
  
I have not been able to find a formal description of this file format.
+
I have not been able to find a formal specification of this file format.
  
 
The best we have is the commented source code of the original converters from YM to MYM files and vice versa.
 
The best we have is the commented source code of the original converters from YM to MYM files and vice versa.

Revision as of 22:11, 16 September 2024

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 specification of this file format.

The best we have is the commented source code of the original converters from YM to MYM files and vice versa.

ym2mym.c

/*
    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 */


mym2ym.c

/*
    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 */