// mod->cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
  
#define INSTRUMENTS_COUNT 31

short int periodes[][36]=
{
	{// Tuning 0, Normal
		856,808,762,720,678,640,604,570,538,508,480,453,
		428,404,381,360,339,320,302,285,269,254,240,226,
		214,202,190,180,170,160,151,143,135,127,120,113
	},
	{// Tuning 1
		850,802,757,715,674,637,601,567,535,505,477,450,
		425,401,379,357,337,318,300,284,268,253,239,225,
		213,201,189,179,169,159,150,142,134,126,119,113
	},
	{// Tuning 2
		844,796,752,709,670,632,597,563,532,502,474,447,
		422,398,376,355,335,316,298,282,266,251,237,224,
		211,199,188,177,167,158,149,141,133,125,118,112
	},
	{//´Tuning 3
		838,791,746,704,665,628,592,559,528,498,470,444,
		419,395,373,352,332,314,296,280,264,249,235,222,
		209,198,187,176,166,157,148,140,132,125,118,111
	},
	{// Tuning 4
		832,785,741,699,660,623,588,555,524,495,467,441,
		416,392,370,350,330,312,294,278,262,247,233,220,
		208,196,185,175,165,156,147,139,131,124,117,110
	},
	{// Tuning 5
		826,779,736,694,655,619,584,551,520,491,463,437,
		413,390,368,347,328,309,292,276,260,245,232,219,
		206,195,184,174,164,155,146,138,130,123,116,109
	},
	{// Tuning 6
		820,774,730,689,651,614,580,547,516,487,460,434,
		410,387,365,345,325,307,290,274,258,244,230,217,
		205,193,183,172,163,154,145,137,129,122,115,109
	},
	{// Tuning 7
		814,768,725,684,646,610,575,543,513,484,457,431,
		407,384,363,342,323,305,288,272,256,242,228,216,
		204,192,181,171,161,152,144,136,128,121,114,108
	},
	{// Tuning -8
		907,856,808,762,720,678,640,604,570,538,508,480,
		453,428,404,381,360,339,320,302,285,269,254,240,
		226,214,202,190,180,170,160,151,143,135,127,120
	},
	{// Tuning -7
		900,850,802,757,715,675,636,601,567,535,505,477,
		450,425,401,379,357,337,318,300,284,268,253,238,
		225,212,200,189,179,169,159,150,142,134,126,119
	},
	{// Tuning -6
		894,844,796,752,709,670,632,597,563,532,502,474,
		447,422,398,376,355,335,316,298,282,266,251,237,
		223,211,199,188,177,167,158,149,141,133,125,118
	},
	{// Tuning -5
		887,838,791,746,704,665,628,592,559,528,498,470,
		444,419,395,373,352,332,314,296,280,264,249,235,
		222,209,198,187,176,166,157,148,140,132,125,118
	},
	{// Tuning -4
		881,832,785,741,699,660,623,588,555,524,494,467,
		441,416,392,370,350,330,312,294,278,262,247,233,
		220,208,196,185,175,165,156,147,139,131,123,117
	},
	{// Tuning -3
		875,826,779,736,694,655,619,584,551,520,491,463,
		437,413,390,368,347,328,309,292,276,260,245,232,
		219,206,195,184,174,164,155,146,138,130,123,116
	},
	{// Tuning -2
		868,820,774,730,689,651,614,580,547,516,487,460,
		434,410,387,365,345,325,307,290,274,258,244,230,
		217,205,193,183,172,163,154,145,137,129,122,115
	},
	{// Tuning -1
		862,814,768,725,684,646,610,575,543,513,484,457,
		431,407,384,363,342,323,305,288,272,256,242,228,
		216,203,192,181,171,161,152,144,136,128,121,114
	}
};

struct  sample_s
{
     char					samplename[22];
     unsigned short int		samplelength;  
     char					finetune;
     char					volume;
     unsigned short int		loopstart;
     unsigned short int		loopend;
	 char					*data;
};

struct mod_s
{
	 char    songname[20];
	 char    pattern[128][1024]; 
	 char    highestpattern; 
	 char    songlen;
	 char	 tracker;
	 char	 songpositions[128];
	 char	 idlong[4];

	 struct  sample_s   sammpleInfo[INSTRUMENTS_COUNT];

};

#define TOBE_W(a) ((((a)>>8)&0xff)|(((a)<<8)&0xff00))



void manipulateMOD(struct mod_s	*mod)
{
	int i,j,k;


	memcpy(&mod->idlong,"MOMO",4);

	/*
	
	// BOOST SAMPLES 
	
	for(i=0;i<INSTRUMENTS_COUNT;i++)
	{
		if(mod->sammpleInfo[i].samplelength!=0)
		{
			char *modsample = mod->sammpleInfo[i].data;
			
			for(j=0;j<mod->sammpleInfo[i].samplelength;j++)
			{
				int b = modsample[j]*16;

				if(b>127)  b=127;
				if(b<-128) b=-128;

				modsample[j]= b;
			}
		}
	
	}*/

	// set new loops

	for(i=0;i<INSTRUMENTS_COUNT;i++)
	{
		if(mod->sammpleInfo[i].samplelength!=0)
		{
			mod->sammpleInfo[i].samplelength+=1;
			
			if(mod->sammpleInfo[i].loopstart==0 && mod->sammpleInfo[i].loopend==2)
			{
				mod->sammpleInfo[i].loopstart=mod->sammpleInfo[i].samplelength+1;
				mod->sammpleInfo[i].loopend=0;
			}
		}
	}

	for(i=0;i<mod->highestpattern+1;i++)
	{
        for(j=0;j<64;j++)                //pattern pos
        {
                unsigned char *line = (unsigned char*)&mod->pattern[i][j*16];  
                
                for(k=0;k<4;k++)
                {        
                   unsigned char	samplenummer,nf;
                   unsigned int		periode,effekt,l,periindex=0;
				   int				finetune;
                 
				     /*
                 _____byte 1_____   byte2_    _____byte 3_____   byte4_
                /                \ /      \  /                \ /      \
                0000          0000-00000000  0000          0000-00000000
                
                Upper four    12 bits for    Lower four    Effect command.
                bits of sam-  note period.   bits of sam-
                ple number.                  ple number.
                */
                   
                   samplenummer = (line[(k*4)+0]&0xf0) | (line[(k*4)+2]>>4);
                   periode      = ((line[(k*4)+0]&0xf)<<8) | line[(k*4)+1];
                   effekt       = (line[(k*4)+2]&0xf)<<8|line[(k*4)+3];
                              
					//printf("%x",samplenummer);

				   finetune = mod->sammpleInfo[samplenummer].finetune;
                   if(finetune<0)
				   {
						finetune = 16+mod->sammpleInfo[samplenummer].finetune;
				   }

				   for(l=0,nf=0;l<36;l++)
				   {
						if(periode==periodes[0][l])
						{
							//printf("(%d,%d,%d) ",samplenummer,l,finetune);
							//printf("%d-%d=%d ",finetune,l,periode);
							nf=1;
							periindex=l;
							break;
						}
				   }

				   //printf("%d ",periindex+(finetune*36));

				   if(nf==0 && periode!=0)
				   {
					   printf("Unknow Note patt %d pos %d chanel %d! %d [%d(%d) %d]\n",i,j,k,samplenummer+1,finetune,mod->sammpleInfo[samplenummer].finetune,periode);
				   }
                
				   /* 
					Byte 1 = 7..0  periodenindex	
					Byte 2 = 6..5  periodemindex, 4..0  samplenummer 
					Byte 3 = 11..8 effekt
					Byte 4 = 7..0  effekt
				   */

				   if(periode==0)
				   {
					    periindex=0x3ff; //(1023)
				   }
				   else
				   {
						periindex=periindex+(finetune*36);
				   }

				   line[(k*4)+3]=periindex & 0xff; //0..35 , 36 off
				   line[(k*4)+2]=samplenummer | ((periindex>>8)<<5);
				   line[(k*4)+1]=(effekt>>8)&0xff;
				   line[(k*4)+0]=(effekt&0xff);

                }
                //printf("\n");
			
		}

		//
	//	system("pause");
	}
	
}

void writeMOD(FILE *wh,struct mod_s	*mod)
{
	long i;

	puts("\nwriting mod\n");

	fwrite(&mod->songname,1,20,wh);

	for(i=0;i<INSTRUMENTS_COUNT;i++)
    {
    
		short int samplelength = TOBE_W(mod->sammpleInfo[i].samplelength/2);
        short int loopstart    = TOBE_W(mod->sammpleInfo[i].loopstart/2);
        short int loopend      = TOBE_W(mod->sammpleInfo[i].loopend/2);


		// Mod ist normalerweise BigEndian da Amiga (68k), GBA ist LE deshalb drehe ich einige daten
		// TO LITTLE ENDIAN BYTE

		samplelength = TOBE_W(samplelength);		
		loopstart = TOBE_W(loopstart);
		loopend = TOBE_W(loopend);

		char finetune = mod->sammpleInfo[i].finetune;
		
		if(	finetune<0)
		{
			finetune = 16+mod->sammpleInfo[i].finetune;
		}

        fwrite(&mod->sammpleInfo[i].samplename        ,1,22,wh);
        fwrite(&samplelength      ,1,2,wh);
        fwrite(&finetune          ,1,1,wh);
        fwrite(&mod->sammpleInfo[i].volume            ,1,1,wh);
        fwrite(&loopstart         ,1,2,wh);
        fwrite(&loopend           ,1,2,wh);
	}    
       
	fwrite(&mod->songlen,1,1,wh);
    fwrite(&mod->tracker,1,1,wh);
    fwrite(&mod->songpositions[0],1,128,wh);
    fwrite(&mod->idlong,1,4,wh);

	for(i=0;i<(mod->highestpattern+1);i++)    //pattern
    {
        fwrite(mod->pattern[i],1,1024,wh);
	}

	for(i=0;i<INSTRUMENTS_COUNT;i++)
	{
		if(mod->sammpleInfo[i].samplelength!=0)
		{
			fwrite(mod->sammpleInfo[i].data,1,mod->sammpleInfo[i].samplelength,wh);
		}
	
	}
}

void decodeMOD(char *data,struct mod_s	*mod)
{
    int     i,j,k;
    long	samplestartbase;
    
    memcpy(mod->songname,&data[0],20);
    
    printf("Songname: '%s'\n",mod->songname);
    
    //** sample infos **
    
    for(i=0;i<INSTRUMENTS_COUNT;i++)
    {
    
        memcpy(&mod->sammpleInfo[i].samplename        ,&data[20+i*30],22);
        memcpy(&mod->sammpleInfo[i].samplelength      ,&data[42+i*30],2);
        memcpy(&mod->sammpleInfo[i].finetune          ,&data[44+i*30],1);
        memcpy(&mod->sammpleInfo[i].volume            ,&data[45+i*30],1);
        memcpy(&mod->sammpleInfo[i].loopstart         ,&data[46+i*30],2);
        memcpy(&mod->sammpleInfo[i].loopend           ,&data[48+i*30],2);
        
        mod->sammpleInfo[i].samplelength = TOBE_W(mod->sammpleInfo[i].samplelength) * 2;
        mod->sammpleInfo[i].loopstart    = TOBE_W(mod->sammpleInfo[i].loopstart)*2;
        mod->sammpleInfo[i].loopend      = TOBE_W(mod->sammpleInfo[i].loopend)*2;
        
        mod->sammpleInfo[i].finetune = mod->sammpleInfo[i].finetune & 0xf;
        
        if(mod->sammpleInfo[i].finetune>7)
        {
                mod->sammpleInfo[i].finetune = -16+mod->sammpleInfo[i].finetune;
        }
        
        printf("Sampleinfo (%d): Name     '%s'\n",i,mod->sammpleInfo[i].samplename);
        printf("                 Length   %d\n",mod->sammpleInfo[i].samplelength);
        printf("                 Finetune %d\n",mod->sammpleInfo[i].finetune);
        printf("                 Volume   %d\n",mod->sammpleInfo[i].volume);
        printf("                 LoopSt   $%x\n",mod->sammpleInfo[i].loopstart);
        printf("                 LoopEnd  $%x\n",mod->sammpleInfo[i].loopend+mod->sammpleInfo[i].loopstart);
        puts("");
    }
    
    memcpy(&mod->songlen,&data[950],1);
    memcpy(&mod->tracker,&data[951],1);
    memcpy(&mod->songpositions,&data[952],128);
    memcpy(&mod->idlong,&data[1080],4);
    
    printf("Songlength:  '%d'\n",mod->songlen);
    printf("Songtracker: '%d'\n",mod->tracker);
    printf("TrackerId  : '%c%c%c%c'\n",mod->idlong[0],mod->idlong[1],mod->idlong[2],mod->idlong[3]);

    printf("Song positions: ");
    for(i=0;i<mod->songlen;i++)
    {
        printf("%x ",mod->songpositions[i]);
    }
    puts("");

    mod->highestpattern=0;
    for(i=0;i<mod->songlen;i++)
    {
        if(mod->highestpattern<mod->songpositions[i]) mod->highestpattern=mod->songpositions[i];
    }

    printf("Highest Pattern: %x\n",mod->highestpattern);

    //** patterns **

    for(i=0;i<(mod->highestpattern+1);i++)    //pattern
    {
        memcpy(mod->pattern[i],&data[1084+(i*1024)],1024);
        
        //puts("");
        
        for(j=0;j<64;j++)                //pattern pos
        {
                //printf("%2.2d - ",j);
        
                /*
                 _____byte 1_____   byte2_    _____byte 3_____   byte4_
                /                \ /      \  /                \ /      \
                0000          0000-00000000  0000          0000-00000000
                
                Upper four    12 bits for    Lower four    Effect command.
                bits of sam-  note period.   bits of sam-
                ple number.                  ple number.
                */
                        
                unsigned char *line = (unsigned char*)&mod->pattern[i][j*16];  
                
                for(k=0;k<4;k++)
                {        
                   unsigned char samplenummer;
                   unsigned int  periode,effekt;
                 
                   
                   samplenummer = (line[(k*4)+0]&0xf0) | (line[(k*4)+2]>>4);
                   periode      = ((line[(k*4)+0]&0xf)<<8) | line[(k*4)+1];
                   effekt       = (line[(k*4)+2]&0xf)<<8|line[(k*4)+3];
                         

                   //printf("%3.3d-%2.2x %3.3x | ",periode,samplenummer,effekt);

				}
                //printf("\n");
        }
    }

	samplestartbase = 1084+((mod->highestpattern+1)*1024);

	puts("");

	for(i=0;i<INSTRUMENTS_COUNT;i++)
	{
		printf("Instrument %2.2x at $%x (len=%d)\n",i,samplestartbase,mod->sammpleInfo[i].samplelength);
		
		if(mod->sammpleInfo[i].samplelength!=0)
		{
			mod->sammpleInfo[i].data = (char *) calloc(mod->sammpleInfo[i].samplelength,1);
			memcpy(mod->sammpleInfo[i].data,&data[samplestartbase],mod->sammpleInfo[i].samplelength);
		}
	
		samplestartbase += mod->sammpleInfo[i].samplelength;
	}
}

// MAIN 

int main(int argc, char* argv[])
{
  FILE *rh,*wh;
  
 struct mod_s	mod;

	if(argc!=3)
	{
		printf("%s input.mod output.mod\n",argv[0]);
		exit(0);
	}

  rh=fopen(argv[1],"rb");//"D:\\GameBoyAdvance\\gbadev\\goldroad\\ss\\test_source\\modinfo\\unit5.mod"/*boner.mod"*/,"rb");
  if(rh!=NULL)
  {
    
    char *data;
    long length;
    
    fseek(rh,0,SEEK_END);
    length=ftell(rh);
    fseek(rh,0,SEEK_SET);
    
    data = (char*) malloc(length);
    if(data!=NULL)
    {
        fread(data,1,length,rh);
        
        puts("decode\n");
        decodeMOD(data,&mod);
        
		manipulateMOD(&mod);

		wh=fopen(argv[2],"wb");//"D:\\GameBoyAdvance\\gbadev\\goldroad\\ss\\test_source\\test.mod","wb");
		if(wh!=NULL)
		{
			writeMOD(wh,&mod);
			fclose(wh);
		}


        free(data);
    }
    else
    {
        puts("no mem");
    }
    
    fclose(rh);
  }
  else
  {
    puts("Cant open file");
  }
  
  fflush(stdout);
 	
  return 0;
}

