# include "all.h"






///// ------------------------




///-----------------------








////////////////////////////////////////////////////



typedef struct 
{
    unsigned char PT2_Delay;
    unsigned char PT2_NumberOfPositions;
    unsigned char PT2_LoopPosition;
    unsigned char PT2_SamplesPointers0[64];
    unsigned char PT2_OrnamentsPointers0[32];
    unsigned char PT2_PatternsPointer0, PT2_PatternsPointer1;
    char PT2_MusicName[30];
    unsigned char PT2_PositionList[65535 - 131];
} PT2_File;

typedef struct 
{
    unsigned char DelayCounter, Delay, CurrentPosition;
} PT2_Parameters;


typedef struct 
{
    unsigned int Address_In_Pattern, OrnamentPointer, SamplePointer, Ton;
    unsigned char Loop_Ornament_Position, Ornament_Length, Position_In_Ornament, Loop_Sample_Position, Sample_Length, Position_In_Sample, Volume, Number_Of_Notes_To_Skip, Note, Slide_To_Note,Amplitude;
    signed char Current_Ton_Sliding, Ton_Delta;
    long int GlissType;
    bool Envelope_Enabled, Enabled;
    signed char Glissade, Addition_To_Noise, Note_Skip_Counter;
} PT2_Channel_Parameters;

typedef struct 
{
    PT2_Parameters PT2;
    PT2_Channel_Parameters PT2_A, PT2_B, PT2_C;
} PT2_SongInfo;


#define PT2_A ((PT2_SongInfo *)info->data)->PT2_A
#define PT2_B ((PT2_SongInfo *)info->data)->PT2_B
#define PT2_C ((PT2_SongInfo *)info->data)->PT2_C
#define PT2 ((PT2_SongInfo *)info->data)->PT2

#define PT2_SamplesPointers(x) (header->PT2_SamplesPointers0 [(x) * 2] | (header->PT2_SamplesPointers0 [(x) * 2 + 1] << 8))
#define PT2_OrnamentsPointers(x) (header->PT2_OrnamentsPointers0 [(x) * 2] | (header->PT2_OrnamentsPointers0 [(x) * 2 + 1] << 8))

//#define PT2_OrnamentsPointers(x) (header->PT2_OrnamentsPointers0 [(x) * 2] + (header->PT2_OrnamentsPointers0 [(x) * 2 + 1] * 256))

#define PT2_PositionList(x) (header->PT2_PositionList [(x)])
#define PT2_PatternsPointer (header->PT2_PatternsPointer0 | (header->PT2_PatternsPointer1 << 8))
#define PT2_Delay (header->PT2_Delay)
#define PT2_NumberOfPositions (header->PT2_NumberOfPositions)
#define PT2_LoopPosition (header->PT2_LoopPosition)

PT2_SongInfo PT2_SongInfoV;

void PT2_Init(AYSongInfo * info)
{
    unsigned char *module = info->module;
    PT2_File * header = (PT2_File *)module;
    
    info->data = (void *)& PT2_SongInfoV;
    
	
    memset(&PT2_A, 0, sizeof(PT2_Channel_Parameters));
    memset(&PT2_B, 0, sizeof(PT2_Channel_Parameters));
    memset(&PT2_C, 0, sizeof(PT2_Channel_Parameters));
    
    PT2.DelayCounter = 1;
    PT2.Delay = PT2_Delay;
    PT2.CurrentPosition = 0;

    PT2_A.Address_In_Pattern = ay_sys_getword(&module[PT2_PatternsPointer+ PT2_PositionList(0) * 6]);
    PT2_B.Address_In_Pattern = ay_sys_getword(&module [PT2_PatternsPointer + PT2_PositionList(0) * 6 + 2]);
    PT2_C.Address_In_Pattern = ay_sys_getword(&module [PT2_PatternsPointer + PT2_PositionList(0) * 6 + 4]);

    PT2_A.OrnamentPointer = PT2_OrnamentsPointers(0);
    PT2_A.Ornament_Length = module[PT2_A.OrnamentPointer];
    PT2_A.OrnamentPointer++;
    PT2_A.Loop_Ornament_Position = module[PT2_A.OrnamentPointer];
    PT2_A.OrnamentPointer++;
    PT2_A.Envelope_Enabled = false;
    PT2_A.Position_In_Sample = 0;
    PT2_A.Position_In_Ornament = 0;
    PT2_A.Addition_To_Noise = 0;
    PT2_A.Glissade = 0;
    PT2_A.Current_Ton_Sliding=0;
    PT2_A.GlissType = 0;
    PT2_A.Enabled = false;
    PT2_A.Number_Of_Notes_To_Skip = 0;
    PT2_A.Note_Skip_Counter = 0;
    PT2_A.Volume = 15;
    PT2_A.Ton = 0;

    PT2_B.OrnamentPointer = PT2_A.OrnamentPointer;
    PT2_B.Loop_Ornament_Position = PT2_A.Loop_Ornament_Position;
    PT2_B.Ornament_Length = PT2_A.Ornament_Length;
    PT2_B.Envelope_Enabled = false;
    PT2_B.Position_In_Sample = 0;
    PT2_B.Position_In_Ornament = 0;
    PT2_B.Addition_To_Noise = 0;
    PT2_B.Glissade = 0;
    PT2_B.Current_Ton_Sliding = 0;
    PT2_B.GlissType = 0;
    PT2_B.Enabled = false;
    PT2_B.Number_Of_Notes_To_Skip = 0;
    PT2_B.Note_Skip_Counter = 0;
    PT2_B.Volume = 15;
    PT2_B.Ton = 0;

    PT2_C.OrnamentPointer = PT2_A.OrnamentPointer;
    PT2_C.Loop_Ornament_Position = PT2_A.Loop_Ornament_Position;
    PT2_C.Ornament_Length = PT2_A.Ornament_Length;
    PT2_C.Envelope_Enabled = false;
    PT2_C.Position_In_Sample = 0;
    PT2_C.Position_In_Ornament = 0;
    PT2_C.Addition_To_Noise = 0;
    PT2_C.Glissade = 0;
    PT2_C.Current_Ton_Sliding = 0;
    PT2_C.GlissType = 0;
    PT2_C.Enabled = false;
    PT2_C.Number_Of_Notes_To_Skip = 0;
    PT2_C.Note_Skip_Counter = 0;
    PT2_C.Volume = 15;
    PT2_C.Ton = 0;
    
//    ay_resetay(&info, 0);
}

unsigned char PT2_Detect0(unsigned char *module, unsigned int length, AYSongInfo * s)
{
    int j, j1, j2;
    long int F_Length;
    

    
    PT2_File * header = (PT2_File *)module;
    
        
    //return true;
    
    
    if(length < 132)
        return false;
        
     s->err  = 10;
        
    if(PT2_PatternsPointer> length)
        return false;
        
     s->err  = 20;
    
    if(module[PT2_PatternsPointer-1] != 255)
        return false;
    
    s->err  = 30;    
        
    if(PT2_SamplesPointers(0) != 0)
        return false;
        
    s->err  = 50;    
    
    j = 0;
    memcpy((char *)&j, (char *)&module[PT2_OrnamentsPointers(0)], 3);
    if(j != 1)
    {
       // memcpy(&s->counter, &module[0x0779], 2); 
        return false;
    }
        
    //s->err = 90;

    j = ay_sys_getword(&module[PT2_PatternsPointer]);
    if(j > length)
        return false;
    if(j - (long int)(PT2_PatternsPointer) <= 0)
        return false;
    if(((j - (long int)(PT2_PatternsPointer)) % 6) != 2)
        return false;

   // s->err  = 190;


    j1 = 0;
    j2 = 0;
    while(((j2 < 256) && (j2 <= length - 131)) && (header->PT2_PositionList[j2] < 128))
    {
        if((unsigned long)(j1) < header->PT2_PositionList[j2])
            j1 = header->PT2_PositionList[j2];
        j2++;
    }
    if(((j - (long int)(PT2_PatternsPointer)) / 6) != j1 + 1)
        return false;

   // s->err  = 290;


    j = 15;
    while((j > 0) && (PT2_OrnamentsPointers(j) == 0))
        j--;
    F_Length = PT2_OrnamentsPointers(j) + module[PT2_OrnamentsPointers(j)] + 2;
    if(F_Length > (length+256))
    {
       //  s->err  =F_Length;
        return false;
        }

    PT2_NumberOfPositions = j2;
    return true;
}

bool PT2_Detect1(unsigned char *module, unsigned int length, AYSongInfo * s)
{
    int j, j1, j2, j3;
    long int F_Length;
    
    
   // return false;
    
    PT2_File *header = (PT2_File *)module;
    
    if(length < 132)
        return false;
    if(PT2_PatternsPointer > length)
        return false;
    if(module[PT2_PatternsPointer-1] != 255)
        return false;
    if(PT2_SamplesPointers(0) != 0)
        return false;

     

    j3 = PT2_SamplesPointers(0);
    if(PT2_OrnamentsPointers(0) - j3 - 2 > length)
        return false;
    
     s->err = 400;
        
    if(PT2_OrnamentsPointers(0)- j3 < 0)
        return false;

     s->err = 500;

    j = 0;
    memcpy((char *)&j, (char *)&module[PT2_OrnamentsPointers(0)], 3);
    if(j != 1)
        return false;
  
    s->err = 1000;

    j = ay_sys_getword(&module[PT2_PatternsPointer]);
    j -= j3;
    if(j > length)
        return false;
    
    s->err = 2000; 
       
    if(j - (long int)(PT2_PatternsPointer) <= 0)
        return false;
        
    s->err = 3000;
        
    if(((j - (long int)(PT2_PatternsPointer)) % 6) != 2)
        return false;

   s->err = 4000;

    j1 = 0;
    j2 = 0;
    while(((j2 < 256) && (j2 <= length - 131)) && (header->PT2_PositionList[j2] < 128))
    {
        if((unsigned long)(j1) < header->PT2_PositionList[j2])
            j1 = header->PT2_PositionList[j2];
        j2++;
    }
    if(((j - (long int)(PT2_PatternsPointer)) / 6) != j1 + 1)
        return false;

     //s->err = 5000;

    j = 15;
    while((j > 0) && (PT2_OrnamentsPointers(j) == j3))
        j--;
    F_Length = PT2_OrnamentsPointers(j) - j3 + module[PT2_OrnamentsPointers(j) - j3] + 2;
    if(F_Length > length + 400)
    {
        s->err = F_Length;
        return false;
    }

    PT2_NumberOfPositions = j2;
    for(j = 0; j < 32; j++)
    {
        ay_sys_writeword(&header->PT2_SamplesPointers0[j * 2], PT2_SamplesPointers(j) - j3);
    }
    for(j = 0; j < 16; j++)
    {
        ay_sys_writeword(&header->PT2_OrnamentsPointers0[j * 2], PT2_OrnamentsPointers(j) - j3);
    }
    for(j2 = 0; j2 < j1 * 3 + 1; j2++)
    {
        j = ay_sys_getword(&module[PT2_PatternsPointer+j2 * 2]);
        j -= j3;
        ay_sys_writeword(&module[PT2_PatternsPointer+j2 * 2], j);
    }
    return true;
}

bool PT2_Detect(unsigned char *module, unsigned int length, AYSongInfo * s)
{
    if(!PT2_Detect0(module, length, s))
        if(!PT2_Detect1(module, length, s))
            return false;
            
    strcpy(s->player, "Pro Tracker v2");
           
	s->slow = 1;
		   
    return true;
}

const unsigned int PT2_Table[96] =
{ 0xef8, 0xe10, 0xd60, 0xc80, 0xbd8, 0xb28, 0xa88, 0x9f0, 0x960, 0x8e0, 0x858, 0x7e0, 0x77c, 0x708, 0x6b0, 0x640, 0x5ec, 0x594, 0x544, 0x4f8, 0x4b0, 0x470, 0x42c, 0x3fd, 0x3be, 0x384, 0x358, 
 0x320, 0x2f6, 0x2ca, 0x2a2, 0x27c, 0x258, 0x238, 0x216, 0x1f8, 0x1df, 0x1c2, 0x1ac, 0x190, 0x17b, 0x165, 0x151, 0x13e, 0x12c, 0x11c, 0x10a, 0xfc, 0xef, 0xe1, 0xd6, 0xc8, 0xbd, 0xb2, 0xa8, 0x9f,
 0x96, 0x8e, 0x85, 0x7e, 0x77, 0x70, 0x6b, 0x64, 0x5e, 0x59, 0x54, 0x4f, 0x4b, 0x47, 0x42, 0x3f, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, 0x2a, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1c, 0x1a, 0x19,
 0x17, 0x16, 0x15, 0x13, 0x12, 0x11, 0x10, 0xf };

void PT2_PatternInterpreter(AYSongInfo * info, PT2_Channel_Parameters * chan)
{
    unsigned char * module = info->module;
    PT2_File * header = (PT2_File *)module;
    bool quit, gliss;
	unsigned char val;
	
    quit = false;
    gliss = false;
    
	do
    {
        val = module[chan->Address_In_Pattern];
        if(val >= 0xe1)
        {
            chan->SamplePointer = PT2_SamplesPointers(val - 0xe0);
            chan->Sample_Length = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Loop_Sample_Position = module[chan->SamplePointer];
            chan->SamplePointer++;
        }
        else if(val == 0xe0)
        {
            chan->Position_In_Sample = 0;
            chan->Position_In_Ornament = 0;
            chan->Current_Ton_Sliding = 0;
            chan->GlissType = 0;
            chan->Enabled = false;
            quit = true;
        }
        else if(val >= 0x80 && val <= 0xdf)
        {
            chan->Position_In_Sample = 0;
            chan->Position_In_Ornament = 0;
            chan->Current_Ton_Sliding = 0;
            if(gliss)
            {
                chan->Slide_To_Note = val - 0x80;
                if(chan->GlissType == 1)
                    chan->Note = chan->Slide_To_Note;
            }
            else
            {
                chan->Note = val - 0x80;
                chan->GlissType = 0;
            }
            chan->Enabled = true;
            quit = true;
        }
        else if(val == 0x7f)
            chan->Envelope_Enabled = false;
        else if(val >= 0x71 && val <= 0x7e)
        {
            chan->Envelope_Enabled = true;
            ay_writeay(AY_ENV_SHAPE, val - 0x70);
            chan->Address_In_Pattern++;
            ay_writeay(AY_ENV_FINE, module[chan->Address_In_Pattern]);
            chan->Address_In_Pattern++;
            ay_writeay(AY_ENV_COARSE, module[chan->Address_In_Pattern]);
        }
        else if(val == 0x70)
            quit = true;
        else if(val >= 0x60 && val <= 0x6f)
        {
            chan->OrnamentPointer = PT2_OrnamentsPointers(val - 0x60);
            chan->Ornament_Length = module[chan->OrnamentPointer];
            chan->OrnamentPointer++;
            chan->Loop_Ornament_Position = module[chan->OrnamentPointer];
            chan->OrnamentPointer++;
            chan->Position_In_Ornament = 0;
        }
        else if(val >= 0x20 && val <= 0x5f)
		            chan->Number_Of_Notes_To_Skip = val - 0x20;
        else if(val >= 0x10 && val <= 0x1f)
            chan->Volume = val - 0x10;
        else if(val == 0xf)
        {
            chan->Address_In_Pattern++;
            PT2.Delay = module[chan->Address_In_Pattern];
        }
        else if(val == 0xe)
        {
            chan->Address_In_Pattern++;
            chan->Glissade = module[chan->Address_In_Pattern];
            chan->GlissType = 1;
            gliss = true;
        }
        else if(val == 0xd)
        {
            chan->Address_In_Pattern++;
            chan->Glissade = abs((signed char)(module[chan->Address_In_Pattern]));
            chan->Address_In_Pattern += 2; //Not use precalculated Ton_Delta
            //to avoide error with first note of pattern
            chan->GlissType = 2;
            gliss = true;
        }
        else if(val == 0xc)
            chan->GlissType = 0;
        else
        {
            chan->Address_In_Pattern++;
            chan->Addition_To_Noise = module[chan->Address_In_Pattern];
        }
        chan->Address_In_Pattern++;
    }
    while(!quit);
    if(gliss && (chan->GlissType == 2))
    {
        chan->Ton_Delta = abs(PT2_Table[chan->Slide_To_Note] - PT2_Table[chan->Note]);
        if(chan->Slide_To_Note > chan->Note)
            chan->Glissade = -chan->Glissade;
    }
    chan->Note_Skip_Counter = chan->Number_Of_Notes_To_Skip;
}

void PT2_GetRegisters(AYSongInfo * info, PT2_Channel_Parameters * chan, unsigned char * TempMixer)
{
    unsigned char j, b0, b1;
    unsigned char *module = info->module;
    PT2_File *header = (PT2_File *)module;
	
    if(chan->Enabled)
    {
        b0 = module[chan->SamplePointer + chan->Position_In_Sample * 3];
        b1 = module[chan->SamplePointer + chan->Position_In_Sample * 3 + 1];
        chan->Ton = module[chan->SamplePointer + chan->Position_In_Sample * 3 + 2] + (unsigned short)((b1 & 15) << 8);
        if((b0 & 4) == 0)
            chan->Ton = -chan->Ton;
        j = chan->Note + module[chan->OrnamentPointer + chan->Position_In_Ornament];
        if(j > 95)
            j = 95;
        chan->Ton = (chan->Ton + chan->Current_Ton_Sliding + PT2_Table[j]) & 0xfff;
        if(chan->GlissType == 2)
        {
            chan->Ton_Delta = chan->Ton_Delta - abs(chan->Glissade);
            if(chan->Ton_Delta < 0)
            {
                chan->Note = chan->Slide_To_Note;
                chan->GlissType = 0;
                chan->Current_Ton_Sliding = 0;
            }
        }
        if(chan->GlissType != 0)
            chan->Current_Ton_Sliding += chan->Glissade;
        chan->Amplitude = (chan->Volume * 17 + (unsigned char)(chan->Volume > 7)) * (b1 >> 4) / 256;
        if(chan->Envelope_Enabled)
            chan->Amplitude = chan->Amplitude | 16;
        if((module[chan->SamplePointer + chan->Position_In_Sample * 3] & 1) != 0)
            *TempMixer = *TempMixer | 64;
        else
            ay_writeay(AY_NOISE_PERIOD, ((b0 >> 3) + chan->Addition_To_Noise) & 31);
        if((b0 & 2) != 0)
            *TempMixer = *TempMixer | 8;
        chan->Position_In_Sample++;
        if(chan->Position_In_Sample == chan->Sample_Length)
            chan->Position_In_Sample = chan->Loop_Sample_Position;
        chan->Position_In_Ornament++;
        if(chan->Position_In_Ornament == chan->Ornament_Length)
            chan->Position_In_Ornament = chan->Loop_Ornament_Position;
    }
    else
        chan->Amplitude = 0;
		
    *TempMixer = *TempMixer >> 1;
}

void PT2_Play(AYSongInfo * info)
{
    unsigned char TempMixer;
    unsigned char * module = info->module;
    PT2_File * header = (PT2_File *)module;
    PT2.DelayCounter--;
    if(PT2.DelayCounter == 0)
    {
        PT2_A.Note_Skip_Counter--;
        if(PT2_A.Note_Skip_Counter < 0)
        {
            if(module[PT2_A.Address_In_Pattern] == 0)
            {
                PT2.CurrentPosition++;
                if(PT2.CurrentPosition == PT2_NumberOfPositions)
                    PT2.CurrentPosition = PT2_LoopPosition;
                PT2_A.Address_In_Pattern = ay_sys_getword(&module[PT2_PatternsPointer+ PT2_PositionList(PT2.CurrentPosition) * 6]);
                PT2_B.Address_In_Pattern = ay_sys_getword(&module[PT2_PatternsPointer + PT2_PositionList(PT2.CurrentPosition) * 6 + 2]);
                PT2_C.Address_In_Pattern = ay_sys_getword(&module[PT2_PatternsPointer + PT2_PositionList(PT2.CurrentPosition) * 6 + 4]);
            }
            PT2_PatternInterpreter(info, &PT2_A);
        }
        PT2_B.Note_Skip_Counter--;
        if(PT2_B.Note_Skip_Counter < 0)
        PT2_PatternInterpreter(info, &PT2_B);
        PT2_C.Note_Skip_Counter--;
        if(PT2_C.Note_Skip_Counter < 0)
        PT2_PatternInterpreter(info, &PT2_C);
        PT2.DelayCounter = PT2.Delay;
    }

    TempMixer = 0;
    PT2_GetRegisters(info, &PT2_A, &TempMixer);
    PT2_GetRegisters(info, &PT2_B, &TempMixer);
    PT2_GetRegisters(info, &PT2_C, &TempMixer);

	///*
    ay_writeay(AY_MIXER, TempMixer);

    ay_writeay(AY_CHNL_A_FINE, PT2_A.Ton & 0xff);
    ay_writeay(AY_CHNL_A_COARSE, (PT2_A.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_B_FINE, PT2_B.Ton & 0xff);
    ay_writeay(AY_CHNL_B_COARSE, (PT2_B.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_C_FINE, PT2_C.Ton & 0xff);
    ay_writeay(AY_CHNL_C_COARSE, (PT2_C.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_A_VOL, PT2_A.Amplitude);
    ay_writeay(AY_CHNL_B_VOL, PT2_B.Amplitude);
    ay_writeay(AY_CHNL_C_VOL, PT2_C.Amplitude);
	//*/
}

void PT2_GetInfo(AYSongInfo * info)
{
    unsigned char *module = info->module;
    int a1, a2, a3, a11, a22, a33;
    unsigned long int j1, j2, j3;
    long i, tm = 0;
    unsigned char b;
    unsigned char ptDelay = module[0];
    unsigned char ptNumPos = module[1];
    unsigned int ptLoopPos = module[2];
    unsigned int ptPatPt = ay_sys_getword(&module[99]);
    const unsigned char *ptPosList = (unsigned char *)&module[131];

	
    b = ptDelay;
    a1 = a2 = a3 = a11 = a22 = a33 = 0;
    for(i = 0; i < ptNumPos; i++)
    {
        if(i == ptLoopPos)
        {
           // info->Loop = tm;
        }
        j1 = ay_sys_getword(&module[ptPatPt + ptPosList[i] * 6]);
        j2 = ay_sys_getword(&module[ptPatPt + ptPosList[i] * 6 + 2]);
        j3 = ay_sys_getword(&module[ptPatPt + ptPosList[i] * 6 + 4]);
        do
        {
            a1--;
            if(a1 < 0)
            {
                if(module[j1] == 0)
                    break;
                do
                {
                    unsigned char val = (unsigned char)module[j1];
                    if(val == 0x70 || (val >= 0x80 && val <= 0xe0))
                    {
                        a1 = a11;
                        j1++;
                        break;
                    }
                    else if(val >= 0x71 && val <= 0x7e)
                    {
                        j1 += 2;
                    }
                    else if(val >= 0x20 && val <= 0x5f)
                    {
                        a11 = module[j1] - 0x20;
                    }
                    else if(val == 0xf)
                    {
                        j1++;
                        b = module[j1];
                    }
                    else if((val >= 1 && val <= 0xb) || val == 0xe)
                    {
                        j1++;
                    }
                    else if(val == 0xd)
                    {
                        j1 += 3;
                    }
                    j1++;
                }
                while(true);
            }

            a2--;
            if(a2 < 0)
            {
                do
                {
                    unsigned char val = (unsigned char)module[j2];
                    if(val == 0x70 || (val >= 0x80 && val <= 0xe0))
                    {
                        a2 = a22;
                        j2++;
                        break;
                    }
                    else if(val >= 0x71 && val <= 0x7e)
                    {
                        j2 += 2;
                    }
                    else if(val >= 0x20 && val <= 0x5f)
                    {
                        a22 = module[j2] - 0x20;
                    }
                    else if(val == 0xf)
                    {
                        j2++;
                        b = module[j2];
                    }
                    else if((val >= 1 && val <= 0xb) || val == 0xe)
                    {
                        j2++;
                    }
                    else if(val == 0xd)
                    {
                        j2 += 3;
                    }
                    j2++;
                }
                while(true);
            }
            a3--;
            if(a3 < 0)
            {
                do
                {
                    unsigned char val = (unsigned char)module[j3];
                    if(val == 0x70 || (val >= 0x80 && val <= 0xe0))
                    {
                        a3 = a33;
                        j3++;
                        break;
                    }
                    else if(val >= 0x71 && val <= 0x7e)
                    {
                        j3 += 2;
                    }
                    else if(val >= 0x20 && val <= 0x5f)
                    {
                        a33 = module[j3] - 0x20;
                    }
                    else if(val == 0xf)
                    {
                        j3++;
                        b = module[j3];
                    }
                    else if((val >= 1 && val <= 0xb) || val == 0xe)
                    {
                        j3++;
                    }
                    else if(val == 0xd)
                    {
                        j3 += 3;
                    }
                    j3++;
                }
                while(true);
            }
            tm += b;
        }
        while(true);
    }
	
    info->len = tm;
    strncpy(info->Name, &module[101], 30);
    info->Autor[0]=0;
}






//======================== PT1 =============================

typedef struct
{
    unsigned char PT1_Delay;
    unsigned char PT1_NumberOfPositions;
    unsigned char PT1_LoopPosition;
    unsigned char PT1_SamplesPointers0[32]; //word
    unsigned char PT1_OrnamentsPointers0[32]; //word
    unsigned char PT1_PatternsPointer0, PT1_PatternsPointer1;
    signed char PT1_MusicName[30];
    unsigned char PT1_PositionList[65536 - 99];
} PT1_File;

#define PT1_SamplesPointers(x) (header->PT1_SamplesPointers0 [(x) * 2] | (header->PT1_SamplesPointers0 [(x) * 2 + 1] << 8))
#define PT1_OrnamentsPointers(x) (header->PT1_OrnamentsPointers0 [(x) * 2] | (header->PT1_OrnamentsPointers0 [(x) * 2 + 1] << 8))
#define PT1_PatternsPointer (header->PT1_PatternsPointer0 | (header->PT1_PatternsPointer1 << 8))

typedef struct 
{
    unsigned int Address_In_Pattern, OrnamentPointer, SamplePointer, Ton;
    unsigned char Number_Of_Notes_To_Skip, Volume, Loop_Sample_Position, Position_In_Sample, Sample_Length, Amplitude, Note;
    signed char Note_Skip_Counter;
    bool Envelope_Enabled, Enabled;
} PT1_Channel_Parameters;

typedef struct 
{
    unsigned char Delay, DelayCounter, CurrentPosition;
} PT1_Parameters;

typedef struct 
{
    PT1_Parameters PT1;
    PT1_Channel_Parameters PT1_A, PT1_B, PT1_C;
} PT1_SongInfo;

PT1_SongInfo PT1_SongInfoV;

#define PT1_A ((PT1_SongInfo *)info->data)->PT1_A
#define PT1_B ((PT1_SongInfo *)info->data)->PT1_B
#define PT1_C ((PT1_SongInfo *)info->data)->PT1_C
#define PT1 ((PT1_SongInfo *)info->data)->PT1

void PT1_Init(AYSongInfo * info)
{
    unsigned char *module = info->module;
    
    PT1_File *header = (PT1_File *)module;
    
    info->data = (void *)& PT1_SongInfoV;
    
    
    memset(&PT1_A, 0, sizeof(PT1_Channel_Parameters));
    memset(&PT1_B, 0, sizeof(PT1_Channel_Parameters));
    memset(&PT1_C, 0, sizeof(PT1_Channel_Parameters));

    PT1.DelayCounter = 1;
    PT1.Delay = header->PT1_Delay;
    PT1.CurrentPosition = 0;
    PT1_A.Address_In_Pattern = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[0] * 6]);
    PT1_B.Address_In_Pattern = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[0] * 6 + 2]);
    PT1_C.Address_In_Pattern = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[0] * 6 + 4]);

    PT1_A.OrnamentPointer = PT1_OrnamentsPointers(0);
    PT1_A.Envelope_Enabled = false;
    PT1_A.Position_In_Sample = 0;
    PT1_A.Enabled = false;
    PT1_A.Number_Of_Notes_To_Skip = 0;
    PT1_A.Note_Skip_Counter = 0;
    PT1_A.Volume = 15;
    PT1_A.Ton = 0;

    PT1_B.OrnamentPointer = PT1_A.OrnamentPointer;
    PT1_B.Envelope_Enabled = false;
    PT1_B.Position_In_Sample = 0;
    PT1_B.Enabled = false;
    PT1_B.Number_Of_Notes_To_Skip = 0;
    PT1_B.Note_Skip_Counter = 0;
    PT1_B.Volume = 15;
    PT1_B.Ton = 0;

    PT1_C.OrnamentPointer = PT1_A.OrnamentPointer;
    PT1_C.Envelope_Enabled = false;
    PT1_C.Position_In_Sample = 0;
    PT1_C.Enabled = false;
    PT1_C.Number_Of_Notes_To_Skip = 0;
    PT1_C.Note_Skip_Counter = 0;
    PT1_C.Volume = 15;
    PT1_C.Ton = 0;

	info->slow = 1;

//    ay_resetay(&info, 0);
}

void PT1_PatternInterpreter(AYSongInfo * info, PT1_Channel_Parameters * chan)
{
    unsigned char *module = info->module;
    PT1_File *header = (PT1_File *)module;
    bool quit = false;
    do
    {
        unsigned char val = module[chan->Address_In_Pattern];
        if(val <= 0x5f)
        {
            chan->Note = val;
            chan->Enabled = true;
            chan->Position_In_Sample = 0;
            quit = true;
        }
        else if(val >= 0x60 && val <= 0x6f)
        {
            chan->SamplePointer = PT1_SamplesPointers(val - 0x60);
            chan->Sample_Length = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Loop_Sample_Position = module[chan->SamplePointer];
            chan->SamplePointer++;
        }
        else if(val >= 0x70 && val <= 0x7f)
            chan->OrnamentPointer = PT1_OrnamentsPointers(val - 0x70);
        else if(val == 0x80)
        {
            chan->Enabled = false;
            quit = true;
        }
        else if(val == 0x81)
            chan->Envelope_Enabled = false;
        else if(val >= 0x82 && val <= 0x8f)
        {
            chan->Envelope_Enabled = true;
            ay_writeay(AY_ENV_SHAPE, val - 0x81);
            chan->Address_In_Pattern++;
            ay_writeay(AY_ENV_FINE, module[chan->Address_In_Pattern]);
            chan->Address_In_Pattern++;
            ay_writeay(AY_ENV_COARSE, module[chan->Address_In_Pattern]);
        }
        else if(val == 0x90)
            quit = true;
        else if(val >= 0x91 && val <= 0xa0)
            PT1.Delay = val - 0x91;
        else if(val >= 0xa1 && val <= 0xb0)
            chan->Volume = val - 0xa1;
        else
            chan->Number_Of_Notes_To_Skip = val - 0xb1;
        chan->Address_In_Pattern++;
    }
    while(!quit);
    chan->Note_Skip_Counter = chan->Number_Of_Notes_To_Skip;
}

void PT1_GetRegisters(AYSongInfo * info, PT1_Channel_Parameters * chan, unsigned char * TempMixer)
{
    unsigned char *module = info->module;
    unsigned char j, b;
    if(chan->Enabled)
    {
        j = chan->Note + module[chan->OrnamentPointer + chan->Position_In_Sample];
        if(j > 95)
            j = 95;
        b = module[chan->SamplePointer + chan->Position_In_Sample * 3];
        chan->Ton = ((((unsigned short)(b)) << 4) & 0xf00) + module[chan->SamplePointer + chan->Position_In_Sample * 3 + 2];
        chan->Amplitude = (chan->Volume * 17 + (chan->Volume > 7 ? 1 : 0)) * (b & 15) / 256;
        b = module[chan->SamplePointer + chan->Position_In_Sample * 3 + 1];
        if((b & 32) == 0)
            chan->Ton = -chan->Ton;
        chan->Ton = (chan->Ton + PT2_Table[j] + (j == 46 ? 1 : 0)) & 0xfff;
        if(chan->Envelope_Enabled)
            chan->Amplitude |= 16;
        if((signed char)b < 0)
            *TempMixer = *TempMixer | 64;
        else
            ay_writeay(AY_NOISE_PERIOD, b & 31);
        if((b & 64) != 0)
            *TempMixer = *TempMixer | 8;
        chan->Position_In_Sample++;
        if(chan->Position_In_Sample == chan->Sample_Length)
            chan->Position_In_Sample = chan->Loop_Sample_Position;
    }
    else
        chan->Amplitude = 0;
    *TempMixer = *TempMixer >> 1;

}

void PT1_Play(AYSongInfo * info)
{
    unsigned char *module = info->module;
    PT1_File *header = (PT1_File *)module;
    unsigned char TempMixer;
    PT1.DelayCounter--;
    if(PT1.DelayCounter == 0)
    {
        PT1_A.Note_Skip_Counter--;
        if(PT1_A.Note_Skip_Counter < 0)
        {
            if(module[PT1_A.Address_In_Pattern] == 255)
            {
                PT1.CurrentPosition++;
                if(PT1.CurrentPosition == header->PT1_NumberOfPositions)
                    PT1.CurrentPosition = header->PT1_LoopPosition;
                PT1_A.Address_In_Pattern = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[PT1.CurrentPosition] * 6]);
                PT1_B.Address_In_Pattern = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[PT1.CurrentPosition] * 6 + 2]);
                PT1_C.Address_In_Pattern = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[PT1.CurrentPosition] * 6 + 4]);
            }
            PT1_PatternInterpreter(info, &PT1_A);
        }
        PT1_B.Note_Skip_Counter--;
        if(PT1_B.Note_Skip_Counter < 0)
            PT1_PatternInterpreter(info, &PT1_B);
        PT1_C.Note_Skip_Counter--;
        if(PT1_C.Note_Skip_Counter < 0)
            PT1_PatternInterpreter(info, &PT1_C);
        PT1.DelayCounter = PT1.Delay;
    }
    TempMixer = 0;
    PT1_GetRegisters(info, &PT1_A, &TempMixer);
    PT1_GetRegisters(info, &PT1_B, &TempMixer);
    PT1_GetRegisters(info, &PT1_C, &TempMixer);

    ay_writeay(AY_MIXER, TempMixer);

    ay_writeay(AY_CHNL_A_FINE, PT1_A.Ton & 0xff);
    ay_writeay(AY_CHNL_A_COARSE, (PT1_A.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_B_FINE, PT1_B.Ton & 0xff);
    ay_writeay(AY_CHNL_B_COARSE, (PT1_B.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_C_FINE, PT1_C.Ton & 0xff);
    ay_writeay(AY_CHNL_C_COARSE, (PT1_C.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_A_VOL, PT1_A.Amplitude);
    ay_writeay(AY_CHNL_B_VOL, PT1_B.Amplitude);
    ay_writeay(AY_CHNL_C_VOL, PT1_C.Amplitude);
}

void PT1_GetInfo(AYSongInfo * info)
{
    unsigned char * module = info->module;
    PT1_File *header = (PT1_File *)module;
    short a1, a2, a3, a11, a22, a33;
    unsigned long j1, j2, j3;
    long i, tm = 0;
    unsigned char b;
    unsigned char *ptr;
    
    
    b = header->PT1_Delay;
    a1 = a2 = a3 = a11 = a22 = a33 = 0;
    for(i = 0; i < header->PT1_NumberOfPositions; i++)
    {
        j1 = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[i] * 6]);
        j2 = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[i] * 6 + 2]);
        j3 = ay_sys_getword(&module[PT1_PatternsPointer + header->PT1_PositionList[i] * 6 + 4]);
        while(true)
        {
            a1--;
            if(a1 < 0)
            {

                if(module[j1] == 255)
                    break;
                while(true)
                {
                    unsigned char val = module[j1];
                    if(val == 0x80 || val == 0x90 || val <= 0x5f)
                    {
                        a1 = a11;
                        j1++;
                        break;
                    }
                    if(val >= 0x82 && val <= 0x8f)
                    {
                        j1 += 2;
                    }
                    if(val >= 0xb1 && val <= 0xfe)
                    {
                        a11 = val - 0xb1;
                    }
                    if(val >= 0x91 && val <= 0xa0)
                    {
                        b = val - 0x91;
                    }
                    j1++;
                }
            }
            a2--;
            if(a2 < 0)
            {

                while(true)
                {
                    unsigned char val = module[j2];
                    if(val == 0x80 || val == 0x90 || val <= 0x5f)
                    {
                        a2 = a22;
                        j2++;
                        break;
                    }
                    if(val >= 0x82 && val <= 0x8f)
                    {
                        j2 += 2;
                    }
                    if(val >= 0xb1 && val <= 0xfe)
                    {
                        a22 = val - 0xb1;
                    }
                    if(val >= 0x91 && val <= 0xa0)
                    {
                        b = val - 0x91;
                    }
                    j2++;
                }
            }
            a3--;
            if(a3 < 0)
            {

                while(true)
                {
                    unsigned char val = module[j3];
                    if(val == 0x80 || val == 0x90 || val <= 0x5f)
                    {
                        a3 = a33;
                        j3++;
                        break;
                    }
                    if(val >= 0x82 && val <= 0x8f)
                    {
                        j3 += 2;
                    }
                    if(val >= 0xb1 && val <= 0xfe)
                    {
                        a33 = val - 0xb1;
                    }
                    if(val >= 0x91 && val <= 0xa0)
                    {
                        b = val - 0x91;
                    }
                    j3++;
                }
            }
            tm += b;
        }
    }
    
    info->len = tm;
    ptr = module + 69;
    strncpy(info->Name, ptr, 30);
    info->Autor[0]=0;
}



bool PT1_Detect(unsigned char * module, unsigned int length, AYSongInfo * s)
{
    long j, j1, j2;
    long F_Length;
    
    PT1_File *header = (PT1_File *)module;
    if(length < 0x66)
        return false;
    if(PT1_PatternsPointer >= length)
        return false;

    j = 0;
    j1 = 65535;
    for(j2 = 0; j2 <= 15; j2++)
    {
        if(j < PT1_SamplesPointers(j2))
            j = PT1_SamplesPointers(j2);
        if((PT1_OrnamentsPointers(j2) != 0) && (j1 > PT1_OrnamentsPointers(j2)))
            j1 = PT1_OrnamentsPointers(j2);
    }

    if(j1 < 0x67)
        return false;
    if(j < 0x67)
        return false;
    if(j > 65534)
        return false;
    if(j > length)
        return false;
    if((j + module[j] * 3 + 2) != j1)
        return false;

    j = 0;
    for(j2 = 0; j2 <= 15; j2++)
        if(j < PT1_OrnamentsPointers(j2))
            j = PT1_OrnamentsPointers(j2);
    if(j < 0x67)
        return false;
    F_Length = j + 64;
    if(F_Length > 65536)
        return false;
    if(F_Length > length + 1)
        return false;

    j = 0x63;
    while((j <= PT1_PatternsPointer) && (module[j] != 255))
        j++;
    if(j + 1 != PT1_PatternsPointer)
        return false;

    header->PT1_NumberOfPositions = j - 0x63;
    
    strcpy(s->player, "Pro Tracker v1");
    
    return true;
}













//////






/////////////////////////////////////////////////////
//////////////////////////////////////////////////////




//Table #0 of Pro Tracker 3.3x - 3.4r
const unsigned short PT3NoteTable_PT_33_34r[] =
{ 0x0C21, 0x0B73, 0x0ACE, 0x0A33, 0x09A0, 0x0916, 0x0893, 0x0818, 0x07A4, 0x0736, 0x06CE, 0x066D, 0x0610, 0x05B9, 0x0567, 0x0519, 0x04D0, 0x048B, 0x0449, 0x040C, 0x03D2, 0x039B, 0x0367, 0x0336, 0x0308, 0x02DC, 0x02B3, 0x028C, 0x0268, 0x0245, 0x0224, 0x0206, 0x01E9, 0x01CD, 0x01B3, 0x019B, 0x0184, 0x016E, 0x0159, 0x0146, 0x0134, 0x0122, 0x0112, 0x0103, 0x00F4, 0x00E6, 0x00D9, 0x00CD, 0x00C2, 0x00B7, 0x00AC, 0x00A3, 0x009A, 0x0091, 0x0089, 0x0081, 0x007A, 0x0073, 0x006C, 0x0066, 0x0061, 0x005B, 0x0056, 0x0051, 0x004D, 0x0048, 0x0044, 0x0040, 0x003D, 0x0039, 0x0036, 0x0033, 0x0030, 0x002D, 0x002B, 0x0028, 0x0026, 0x0024, 0x0022, 0x0020, 0x001E, 0x001C, 0x001B, 0x0019, 0x0018, 0x0016, 0x0015, 0x0014, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F, 0x000E, 0x000D, 0x000C };

//{Table #0 of Pro Tracker 3.4x - 3.5x}
const unsigned short PT3NoteTable_PT_34_35[] =
{ 0x0C22, 0x0B73, 0x0ACF, 0x0A33, 0x09A1, 0x0917, 0x0894, 0x0819, 0x07A4, 0x0737, 0x06CF, 0x066D, 0x0611, 0x05BA, 0x0567, 0x051A, 0x04D0, 0x048B, 0x044A, 0x040C, 0x03D2, 0x039B, 0x0367, 0x0337, 0x0308, 0x02DD, 0x02B4, 0x028D, 0x0268, 0x0246, 0x0225, 0x0206, 0x01E9, 0x01CE, 0x01B4, 0x019B, 0x0184, 0x016E, 0x015A, 0x0146, 0x0134, 0x0123, 0x0112, 0x0103, 0x00F5, 0x00E7, 0x00DA, 0x00CE, 0x00C2, 0x00B7, 0x00AD, 0x00A3, 0x009A, 0x0091, 0x0089, 0x0082, 0x007A, 0x0073, 0x006D, 0x0067, 0x0061, 0x005C, 0x0056, 0x0052, 0x004D, 0x0049, 0x0045, 0x0041, 0x003D, 0x003A, 0x0036, 0x0033, 0x0031, 0x002E, 0x002B, 0x0029, 0x0027, 0x0024, 0x0022, 0x0020, 0x001F, 0x001D, 0x001B, 0x001A, 0x0018, 0x0017, 0x0016, 0x0014, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F, 0x000E, 0x000D, 0x000C };

//{Table #1 of Pro Tracker 3.3x - 3.5x)}
const unsigned short PT3NoteTable_ST[] =
{ 0x0EF8, 0x0E10, 0x0D60, 0x0C80, 0x0BD8, 0x0B28, 0x0A88, 0x09F0, 0x0960, 0x08E0, 0x0858, 0x07E0, 0x077C, 0x0708, 0x06B0, 0x0640, 0x05EC, 0x0594, 0x0544, 0x04F8, 0x04B0, 0x0470, 0x042C, 0x03FD, 0x03BE, 0x0384, 0x0358, 0x0320, 0x02F6, 0x02CA, 0x02A2, 0x027C, 0x0258, 0x0238, 0x0216, 0x01F8, 0x01DF, 0x01C2, 0x01AC, 0x0190, 0x017B, 0x0165, 0x0151, 0x013E, 0x012C, 0x011C, 0x010A, 0x00FC, 0x00EF, 0x00E1, 0x00D6, 0x00C8, 0x00BD, 0x00B2, 0x00A8, 0x009F, 0x0096, 0x008E, 0x0085, 0x007E, 0x0077, 0x0070, 0x006B, 0x0064, 0x005E, 0x0059, 0x0054, 0x004F, 0x004B, 0x0047, 0x0042, 0x003F, 0x003B, 0x0038, 0x0035, 0x0032, 0x002F, 0x002C, 0x002A, 0x0027, 0x0025, 0x0023, 0x0021, 0x001F, 0x001D, 0x001C, 0x001A, 0x0019, 0x0017, 0x0016, 0x0015, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F };

//{Table #2 of Pro Tracker 3.4r}
const unsigned short PT3NoteTable_ASM_34r[] =
{ 0x0D3E, 0x0C80, 0x0BCC, 0x0B22, 0x0A82, 0x09EC, 0x095C, 0x08D6, 0x0858, 0x07E0, 0x076E, 0x0704, 0x069F, 0x0640, 0x05E6, 0x0591, 0x0541, 0x04F6, 0x04AE, 0x046B, 0x042C, 0x03F0, 0x03B7, 0x0382, 0x034F, 0x0320, 0x02F3, 0x02C8, 0x02A1, 0x027B, 0x0257, 0x0236, 0x0216, 0x01F8, 0x01DC, 0x01C1, 0x01A8, 0x0190, 0x0179, 0x0164, 0x0150, 0x013D, 0x012C, 0x011B, 0x010B, 0x00FC, 0x00EE, 0x00E0, 0x00D4, 0x00C8, 0x00BD, 0x00B2, 0x00A8, 0x009F, 0x0096, 0x008D, 0x0085, 0x007E, 0x0077, 0x0070, 0x006A, 0x0064, 0x005E, 0x0059, 0x0054, 0x0050, 0x004B, 0x0047, 0x0043, 0x003F, 0x003C, 0x0038, 0x0035, 0x0032, 0x002F, 0x002D, 0x002A, 0x0028, 0x0026, 0x0024, 0x0022, 0x0020, 0x001E, 0x001D, 0x001B, 0x001A, 0x0019, 0x0018, 0x0015, 0x0014, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F, 0x000E };

//{Table #2 of Pro Tracker 3.4x - 3.5x}
const unsigned short PT3NoteTable_ASM_34_35[] =
{ 0x0D10, 0x0C55, 0x0BA4, 0x0AFC, 0x0A5F, 0x09CA, 0x093D, 0x08B8, 0x083B, 0x07C5, 0x0755, 0x06EC, 0x0688, 0x062A, 0x05D2, 0x057E, 0x052F, 0x04E5, 0x049E, 0x045C, 0x041D, 0x03E2, 0x03AB, 0x0376, 0x0344, 0x0315, 0x02E9, 0x02BF, 0x0298, 0x0272, 0x024F, 0x022E, 0x020F, 0x01F1, 0x01D5, 0x01BB, 0x01A2, 0x018B, 0x0174, 0x0160, 0x014C, 0x0139, 0x0128, 0x0117, 0x0107, 0x00F9, 0x00EB, 0x00DD, 0x00D1, 0x00C5, 0x00BA, 0x00B0, 0x00A6, 0x009D, 0x0094, 0x008C, 0x0084, 0x007C, 0x0075, 0x006F, 0x0069, 0x0063, 0x005D, 0x0058, 0x0053, 0x004E, 0x004A, 0x0046, 0x0042, 0x003E, 0x003B, 0x0037, 0x0034, 0x0031, 0x002F, 0x002C, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001F, 0x001D, 0x001C, 0x001A, 0x0019, 0x0017, 0x0016, 0x0015, 0x0014, 0x0012, 0x0011, 0x0010, 0x000F, 0x000E, 0x000D };

//{Table #3 of Pro Tracker 3.4r}
const unsigned short PT3NoteTable_REAL_34r[] =
{ 0x0CDA, 0x0C22, 0x0B73, 0x0ACF, 0x0A33, 0x09A1, 0x0917, 0x0894, 0x0819, 0x07A4, 0x0737, 0x06CF, 0x066D, 0x0611, 0x05BA, 0x0567, 0x051A, 0x04D0, 0x048B, 0x044A, 0x040C, 0x03D2, 0x039B, 0x0367, 0x0337, 0x0308, 0x02DD, 0x02B4, 0x028D, 0x0268, 0x0246, 0x0225, 0x0206, 0x01E9, 0x01CE, 0x01B4, 0x019B, 0x0184, 0x016E, 0x015A, 0x0146, 0x0134, 0x0123, 0x0113, 0x0103, 0x00F5, 0x00E7, 0x00DA, 0x00CE, 0x00C2, 0x00B7, 0x00AD, 0x00A3, 0x009A, 0x0091, 0x0089, 0x0082, 0x007A, 0x0073, 0x006D, 0x0067, 0x0061, 0x005C, 0x0056, 0x0052, 0x004D, 0x0049, 0x0045, 0x0041, 0x003D, 0x003A, 0x0036, 0x0033, 0x0031, 0x002E, 0x002B, 0x0029, 0x0027, 0x0024, 0x0022, 0x0020, 0x001F, 0x001D, 0x001B, 0x001A, 0x0018, 0x0017, 0x0016, 0x0014, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F, 0x000E, 0x000D };

//{Table #3 of Pro Tracker 3.4x - 3.5x}
const unsigned short PT3NoteTable_REAL_34_35[] =
{ 0x0CDA, 0x0C22, 0x0B73, 0x0ACF, 0x0A33, 0x09A1, 0x0917, 0x0894, 0x0819, 0x07A4, 0x0737, 0x06CF, 0x066D, 0x0611, 0x05BA, 0x0567, 0x051A, 0x04D0, 0x048B, 0x044A, 0x040C, 0x03D2, 0x039B, 0x0367, 0x0337, 0x0308, 0x02DD, 0x02B4, 0x028D, 0x0268, 0x0246, 0x0225, 0x0206, 0x01E9, 0x01CE, 0x01B4, 0x019B, 0x0184, 0x016E, 0x015A, 0x0146, 0x0134, 0x0123, 0x0112, 0x0103, 0x00F5, 0x00E7, 0x00DA, 0x00CE, 0x00C2, 0x00B7, 0x00AD, 0x00A3, 0x009A, 0x0091, 0x0089, 0x0082, 0x007A, 0x0073, 0x006D, 0x0067, 0x0061, 0x005C, 0x0056, 0x0052, 0x004D, 0x0049, 0x0045, 0x0041, 0x003D, 0x003A, 0x0036, 0x0033, 0x0031, 0x002E, 0x002B, 0x0029, 0x0027, 0x0024, 0x0022, 0x0020, 0x001F, 0x001D, 0x001B, 0x001A, 0x0018, 0x0017, 0x0016, 0x0014, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F, 0x000E, 0x000D };

//{Volume table of Pro Tracker 3.3x - 3.4x}
const unsigned char PT3VolumeTable_33_34[16][16] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02 },
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03 },
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04 },
{ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05 },
{ 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06 },
{ 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07 },
{ 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08 },
{ 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09 },
{ 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0A },
{ 0x00, 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x09, 0x0A, 0x0B },
{ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0B, 0x0C },
{ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D },
{ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } };

//{Volume table of Pro Tracker 3.5x}
const unsigned char PT3VolumeTable_35[16][16] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02 },
{ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03 },
{ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04 },
{ 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05 },
{ 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06 },
{ 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07 },
{ 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08 },
{ 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09 },
{ 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x07, 0x08, 0x09, 0x09, 0x0A },
{ 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0A, 0x0B },
{ 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0A, 0x0B, 0x0C },
{ 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0D },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } };



typedef struct 
{
    unsigned short Address_In_Pattern, OrnamentPointer, SamplePointer, Ton;
    unsigned char Loop_Ornament_Position, Ornament_Length, Position_In_Ornament, Loop_Sample_Position, Sample_Length, Position_In_Sample, Volume, Number_Of_Notes_To_Skip, Note, Slide_To_Note, Amplitude;
    bool Envelope_Enabled, Enabled, SimpleGliss;
    short Current_Amplitude_Sliding, Current_Noise_Sliding, Current_Envelope_Sliding, Ton_Slide_Count, Current_OnOff, OnOff_Delay, OffOn_Delay, Ton_Slide_Delay, Current_Ton_Sliding, Ton_Accumulator, Ton_Slide_Step, Ton_Delta;
    signed char Note_Skip_Counter;
} PT3_Channel_Parameters;

typedef struct 
{
    unsigned char Env_Base_lo;
    unsigned char Env_Base_hi;
    short Cur_Env_Slide, Env_Slide_Add;
    signed char Cur_Env_Delay, Env_Delay;
    unsigned char Noise_Base, Delay, AddToNoise, DelayCounter, CurrentPosition;
    long Version;
} PT3_Parameters;

typedef struct 
{
    PT3_Parameters PT3;
    PT3_Channel_Parameters PT3_A, PT3_B, PT3_C;
} PT3_SongInfo;

#define PT3_A ((PT3_SongInfo *)data)->PT3_A
#define PT3_B ((PT3_SongInfo *)data)->PT3_B
#define PT3_C ((PT3_SongInfo *)data)->PT3_C
#define PT3 ((PT3_SongInfo *)data)->PT3

#define GET_COMMON_VARS unsigned char *module; PT3_File *header; void *data; module = info->module; header = (PT3_File *)info->module; data = info->data;   



/*
unsigned char *PT3_FindSig(unsigned char *buffer, long length)
{
    unsigned char *ptr;
    unsigned long remaining;

    char sig[] =
    { 0x50, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x33, 0x2e };
    char sig1[] =
    { 0x56, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x20, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x49, 0x49 };
    if(length < sizeof(sig) || length < sizeof(sig1))
        return 0;
    ptr = buffer;
    remaining = length - sizeof(sig);
    while(remaining > sizeof(sig))
    {
        if(!memcmp(ptr, sig, sizeof(sig)) || !memcmp(ptr, sig1, sizeof(sig1))) //found
        {
            return ptr;
        }
        ptr++;
        remaining--;
    }
    return 0;
}
*/

PT3_SongInfo data0;

void PT3_Init(AYSongInfo * info)
{
    //unsigned char *ptr;
    long version;
    long i;
    unsigned char b;
    unsigned long y;
    void *data;
    unsigned char *module = info->module;
    PT3_File *header = (PT3_File *)module;
    
    
    info->data =  &data0;
    
    memset(info->data, 0, sizeof(PT3_SongInfo));

    version = 6;

    if((header->PT3_MusicName[13] >= '0') && (header->PT3_MusicName[13] <= '9'))
    {
        version = header->PT3_MusicName[13] - 0x30;
    }
    
   // ptr = PT3_FindSig(module + 0x63, info->Length - 0x63);
    
    data = info->data;
    module = info->module;

    for(y = 0; y < 2; y++)
    {
        i = header->PT3_PositionList[0];
        b = header->PT3_MusicName[0x62];
        if(b != 0x20)
        {
            i = b * 3 - 3 - i;
        }
        PT3.Version = version;
        PT3.DelayCounter = 1;
        PT3.Delay = header->PT3_Delay;
        PT3_A.Address_In_Pattern = ay_sys_getword(&module[PT3_PatternsPointer + i * 2]);
        PT3_B.Address_In_Pattern = ay_sys_getword(&module[PT3_PatternsPointer + i * 2 + 2]);
        PT3_C.Address_In_Pattern = ay_sys_getword(&module[PT3_PatternsPointer + i * 2 + 4]);

        PT3_A.OrnamentPointer = PT3_OrnamentsPointers(0);
        PT3_A.Loop_Ornament_Position = module[PT3_A.OrnamentPointer];
        PT3_A.OrnamentPointer++;
        PT3_A.Ornament_Length = module[PT3_A.OrnamentPointer];
        PT3_A.OrnamentPointer++;
        PT3_A.SamplePointer = PT3_SamplesPointers(1);
        PT3_A.Loop_Sample_Position = module[PT3_A.SamplePointer];
        PT3_A.SamplePointer++;
        PT3_A.Sample_Length = module[PT3_A.SamplePointer];
        PT3_A.SamplePointer++;
        PT3_A.Volume = 15;
        PT3_A.Note_Skip_Counter = 1;

        PT3_B.OrnamentPointer = PT3_A.OrnamentPointer;
        PT3_B.Loop_Ornament_Position = PT3_A.Loop_Ornament_Position;
        PT3_B.Ornament_Length = PT3_A.Ornament_Length;
        PT3_B.SamplePointer = PT3_A.SamplePointer;
        PT3_B.Loop_Sample_Position = PT3_A.Loop_Sample_Position;
        PT3_B.Sample_Length = PT3_A.Sample_Length;
        PT3_B.Volume = 15;
        PT3_B.Note_Skip_Counter = 1;

        PT3_C.OrnamentPointer = PT3_A.OrnamentPointer;
        PT3_C.Loop_Ornament_Position = PT3_A.Loop_Ornament_Position;
        PT3_C.Ornament_Length = PT3_A.Ornament_Length;
        PT3_C.SamplePointer = PT3_A.SamplePointer;
        PT3_C.Loop_Sample_Position = PT3_A.Loop_Sample_Position;
        PT3_C.Sample_Length = PT3_A.Sample_Length;
        PT3_C.Volume = 15;
        PT3_C.Note_Skip_Counter = 1;

        data = info->data;
        module = info->module;
        header = (PT3_File *)module;
    }

	info->slow = 1;
}

int PT3_GetNoteFreq(AYSongInfo * info, unsigned char j)
{
    GET_COMMON_VARS;
    switch(header->PT3_TonTableId)
    {
        case 0:
            if(PT3.Version <= 3)
                return PT3NoteTable_PT_33_34r[j];
            else
                return PT3NoteTable_PT_34_35[j];
        case 1:
            return PT3NoteTable_ST[j];
        case 2:
            if(PT3.Version <= 3)
                return PT3NoteTable_ASM_34r[j];
            else
                return PT3NoteTable_ASM_34_35[j];
        default:
            if(PT3.Version <= 3)
                return PT3NoteTable_REAL_34r[j];
            else
                return PT3NoteTable_REAL_34_35[j];
    }
}

void PT3_PatternIntterpreter(AYSongInfo * info, PT3_Channel_Parameters * chan)
{
    bool quit;
    unsigned char flag9, flag8, flag5, flag4, flag3, flag2, flag1;
    unsigned char counter;
    int prnote, prsliding;

    GET_COMMON_VARS;
  
    prnote = chan->Note;
    prsliding = chan->Current_Ton_Sliding;
    quit = false;
    counter = 0;
    flag9 = flag8 = flag5 = flag4 = flag3 = flag2 = flag1 = 0;
    do
    {
        unsigned char val = module[chan->Address_In_Pattern];
        if(val >= 0xf0)
        {
            chan->OrnamentPointer = PT3_OrnamentsPointers((val - 0xf0));
            chan->Loop_Ornament_Position = module[chan->OrnamentPointer];
            chan->OrnamentPointer++;
            chan->Ornament_Length = module[chan->OrnamentPointer];
            chan->OrnamentPointer++;
            chan->Address_In_Pattern++;
            chan->SamplePointer = PT3_SamplesPointers((module [chan->Address_In_Pattern] / 2));
            chan->Loop_Sample_Position = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Sample_Length = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Envelope_Enabled = false;
            chan->Position_In_Ornament = 0;
        }
        else if(val >= 0xd1 && val <= 0xef)
        {
            chan->SamplePointer = PT3_SamplesPointers((val - 0xd0));
            chan->Loop_Sample_Position = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Sample_Length = module[chan->SamplePointer];
            chan->SamplePointer++;
        }
        else if(val == 0xd0)
        {
            quit = true;
        }
        else if(val >= 0xc1 && val <= 0xcf)
        {
            chan->Volume = val - 0xc0;
        }
        else if(val == 0xc0)
        {
            chan->Position_In_Sample = 0;
            chan->Current_Amplitude_Sliding = 0;
            chan->Current_Noise_Sliding = 0;
            chan->Current_Envelope_Sliding = 0;
            chan->Position_In_Ornament = 0;
            chan->Ton_Slide_Count = 0;
            chan->Current_Ton_Sliding = 0;
            chan->Ton_Accumulator = 0;
            chan->Current_OnOff = 0;
            chan->Enabled = false;
            quit = true;
        }
        else if(val >= 0xb2 && val <= 0xbf)
        {
            chan->Envelope_Enabled = true;
            ay_writeay(AY_ENV_SHAPE, val - 0xb1);
            chan->Address_In_Pattern++;
            PT3.Env_Base_hi = module[chan->Address_In_Pattern];
            chan->Address_In_Pattern++;
            PT3.Env_Base_lo = module[chan->Address_In_Pattern];
            chan->Position_In_Ornament = 0;
            PT3.Cur_Env_Slide = 0;
            PT3.Cur_Env_Delay = 0;
        }
        else if(val == 0xb1)
        {
            chan->Address_In_Pattern++;
            chan->Number_Of_Notes_To_Skip = module[chan->Address_In_Pattern];
        }
        else if(val == 0xb0)
        {
            chan->Envelope_Enabled = false;
            chan->Position_In_Ornament = 0;
        }
        else if(val >= 0x50 && val <= 0xaf)
        {
            chan->Note = val - 0x50;
            chan->Position_In_Sample = 0;
            chan->Current_Amplitude_Sliding = 0;
            chan->Current_Noise_Sliding = 0;
            chan->Current_Envelope_Sliding = 0;
            chan->Position_In_Ornament = 0;
            chan->Ton_Slide_Count = 0;
            chan->Current_Ton_Sliding = 0;
            chan->Ton_Accumulator = 0;
            chan->Current_OnOff = 0;
            chan->Enabled = true;
            quit = true;
        }
        else if(val >= 0x40 && val <= 0x4f)
        {
            chan->OrnamentPointer = PT3_OrnamentsPointers((val - 0x40));
            chan->Loop_Ornament_Position = module[chan->OrnamentPointer];
            chan->OrnamentPointer++;
            chan->Ornament_Length = module[chan->OrnamentPointer];
            chan->OrnamentPointer++;
            chan->Position_In_Ornament = 0;
        }
        else if(val >= 0x20 && val <= 0x3f)
        {
            PT3.Noise_Base = val - 0x20;
        }
        else if(val >= 0x10 && val <= 0x1f)
        {
            if(val == 0x10)
                chan->Envelope_Enabled = false;
            else
            {
                ay_writeay(AY_ENV_SHAPE, val - 0x10);
                chan->Address_In_Pattern++;
                PT3.Env_Base_hi = module[chan->Address_In_Pattern];
                chan->Address_In_Pattern++;
                PT3.Env_Base_lo = module[chan->Address_In_Pattern];
                chan->Envelope_Enabled = true;
                PT3.Cur_Env_Slide = 0;
                PT3.Cur_Env_Delay = 0;
            }
            chan->Address_In_Pattern++;
            chan->SamplePointer = PT3_SamplesPointers((module [chan->Address_In_Pattern] / 2));
            chan->Loop_Sample_Position = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Sample_Length = module[chan->SamplePointer];
            chan->SamplePointer++;
            chan->Position_In_Ornament = 0;
        }
        else if(val == 0x9)
        {
            counter++;
            flag9 = counter;
        }
        else if(val == 0x8)
        {
            counter++;
            flag8 = counter;
        }
        else if(val == 0x5)
        {
            counter++;
            flag5 = counter;
        }
        else if(val == 0x4)
        {
            counter++;
            flag4 = counter;
        }
        else if(val == 0x3)
        {
            counter++;
            flag3 = counter;
        }
        else if(val == 0x2)
        {
            counter++;
            flag2 = counter;
        }
        else if(val == 0x1)
        {
            counter++;
            flag1 = counter;
        }
        chan->Address_In_Pattern++;
    }
    while(!quit);

    while(counter > 0)
    {
        if(counter == flag1)
        {
            chan->Ton_Slide_Delay = module[chan->Address_In_Pattern];
            chan->Ton_Slide_Count = chan->Ton_Slide_Delay;
            chan->Address_In_Pattern++;
            chan->Ton_Slide_Step = ay_sys_getword(&module[chan->Address_In_Pattern]);
            chan->Address_In_Pattern += 2;
            chan->SimpleGliss = true;
            chan->Current_OnOff = 0;
            if((chan->Ton_Slide_Count == 0) && (PT3.Version >= 7))
                chan->Ton_Slide_Count++;
        }
        else if(counter == flag2)
        {
            chan->SimpleGliss = false;
            chan->Current_OnOff = 0;
            chan->Ton_Slide_Delay = module[chan->Address_In_Pattern];
            chan->Ton_Slide_Count = chan->Ton_Slide_Delay;
            chan->Address_In_Pattern += 3;
            chan->Ton_Slide_Step = abs((short)(ay_sys_getword(&module[chan->Address_In_Pattern])));
            chan->Address_In_Pattern += 2;
            chan->Ton_Delta = PT3_GetNoteFreq(info, chan->Note) - PT3_GetNoteFreq(info, prnote);
            chan->Slide_To_Note = chan->Note;
            chan->Note = prnote;
            if(PT3.Version >= 6)
                chan->Current_Ton_Sliding = prsliding;
            if((chan->Ton_Delta - chan->Current_Ton_Sliding) < 0)
                chan->Ton_Slide_Step = -chan->Ton_Slide_Step;
        }
        else if(counter == flag3)
        {
            chan->Position_In_Sample = module[chan->Address_In_Pattern];
            chan->Address_In_Pattern++;
        }
        else if(counter == flag4)
        {
            chan->Position_In_Ornament = module[chan->Address_In_Pattern];
            chan->Address_In_Pattern++;
        }
        else if(counter == flag5)
        {
            chan->OnOff_Delay = module[chan->Address_In_Pattern];
            chan->Address_In_Pattern++;
            chan->OffOn_Delay = module[chan->Address_In_Pattern];
            chan->Current_OnOff = chan->OnOff_Delay;
            chan->Address_In_Pattern++;
            chan->Ton_Slide_Count = 0;
            chan->Current_Ton_Sliding = 0;
        }
        else if(counter == flag8)
        {
            PT3.Env_Delay = module[chan->Address_In_Pattern];
            PT3.Cur_Env_Delay = PT3.Env_Delay;
            chan->Address_In_Pattern++;
            PT3.Env_Slide_Add = ay_sys_getword(&module[chan->Address_In_Pattern]);
            chan->Address_In_Pattern += 2;
        }
        else if(counter == flag9)
        {
            PT3.Delay = module[chan->Address_In_Pattern];
            chan->Address_In_Pattern++;
        }
        counter--;
    }
    chan->Note_Skip_Counter = chan->Number_Of_Notes_To_Skip;
}

void PT3_ChangeRegisters(AYSongInfo * info, PT3_Channel_Parameters * chan, char * AddToEnv, unsigned char * TempMixer)
{
    unsigned char j, b1, b0;
    unsigned short w;
  
    GET_COMMON_VARS;
  
    if(chan->Enabled)
    {
        chan->Ton = ay_sys_getword(&module[chan->SamplePointer + chan->Position_In_Sample * 4 + 2]);
        chan->Ton += chan->Ton_Accumulator;
        b0 = module[chan->SamplePointer + chan->Position_In_Sample * 4];
        b1 = module[chan->SamplePointer + chan->Position_In_Sample * 4 + 1];
        if((b1 & 0x40) != 0)
        {
            chan->Ton_Accumulator = chan->Ton;
        }
        j = chan->Note + module[chan->OrnamentPointer + chan->Position_In_Ornament];
        if((signed char)(j) < 0)
            j = 0;
        else if(j > 95)
            j = 95;
        w = PT3_GetNoteFreq(info, j);
        chan->Ton = (chan->Ton + chan->Current_Ton_Sliding + w) & 0xfff;
        if(chan->Ton_Slide_Count > 0)
        {
            chan->Ton_Slide_Count--;
            if(chan->Ton_Slide_Count == 0)
            {
                chan->Current_Ton_Sliding += chan->Ton_Slide_Step;
                chan->Ton_Slide_Count = chan->Ton_Slide_Delay;
                if(!chan->SimpleGliss)
                {
                    if(((chan->Ton_Slide_Step < 0) && (chan->Current_Ton_Sliding <= chan->Ton_Delta)) || ((chan->Ton_Slide_Step >= 0) && (chan->Current_Ton_Sliding >= chan->Ton_Delta)))
                    {
                        chan->Note = chan->Slide_To_Note;
                        chan->Ton_Slide_Count = 0;
                        chan->Current_Ton_Sliding = 0;
                    }
                }
            }
        }
        chan->Amplitude = b1 & 0xf;
        if((b0 & 0x80) != 0)
        {
            if((b0 & 0x40) != 0)
            {
                if(chan->Current_Amplitude_Sliding < 15)
                    chan->Current_Amplitude_Sliding++;
            }
            else if(chan->Current_Amplitude_Sliding > -15)
            {
                chan->Current_Amplitude_Sliding--;
            }
        }
        chan->Amplitude += chan->Current_Amplitude_Sliding;
        if((signed char)(chan->Amplitude) < 0)
            chan->Amplitude = 0;
        else if(chan->Amplitude > 15)
            chan->Amplitude = 15;
        if(PT3.Version <= 4)
            chan->Amplitude = PT3VolumeTable_33_34[chan->Volume][chan->Amplitude];
        else
            chan->Amplitude = PT3VolumeTable_35[chan->Volume][chan->Amplitude];
        if(((b0 & 1) == 0) && chan->Envelope_Enabled)
            chan->Amplitude = chan->Amplitude | 16;
        if((b1 & 0x80) != 0)
        {
            if((b0 & 0x20) != 0)
                j = ((b0 >> 1) | 0xf0) + chan->Current_Envelope_Sliding;
            else
                j = ((b0 >> 1) & 0xf) + chan->Current_Envelope_Sliding;
            if((b1 & 0x20) != 0)
                chan->Current_Envelope_Sliding = j;
            AddToEnv += j;
        }
        else
        {
            PT3.AddToNoise = (b0 >> 1) + chan->Current_Noise_Sliding;
            if((b1 & 0x20) != 0)
                chan->Current_Noise_Sliding = PT3.AddToNoise;
        }
        *TempMixer = ((b1 >> 1) & 0x48) | *TempMixer;
        chan->Position_In_Sample++;
        if(chan->Position_In_Sample >= chan->Sample_Length)
            chan->Position_In_Sample = chan->Loop_Sample_Position;
        chan->Position_In_Ornament++;
        if(chan->Position_In_Ornament >= chan->Ornament_Length)
            chan->Position_In_Ornament = chan->Loop_Ornament_Position;
    }
    else
        chan->Amplitude = 0;
    *TempMixer = *TempMixer >> 1;
    if(chan->Current_OnOff > 0)
    {
        chan->Current_OnOff--;
        if(chan->Current_OnOff == 0)
        {
            chan->Enabled = !chan->Enabled;
            if(chan->Enabled)
                chan->Current_OnOff = chan->OnOff_Delay;
            else
                chan->Current_OnOff = chan->OffOn_Delay;
        }
    }
}

void PT3_Play_Chip(AYSongInfo * info)
{
    unsigned char TempMixer;
    char AddToEnv;
    unsigned short cur_env;

    GET_COMMON_VARS;
 
    PT3.DelayCounter--;
    if(PT3.DelayCounter == 0)
    {
        PT3_A.Note_Skip_Counter--;
        if(PT3_A.Note_Skip_Counter == 0)
        {
            if(module[PT3_A.Address_In_Pattern] == 0)
            {
                PT3.CurrentPosition++;
                if(PT3.CurrentPosition == header->PT3_NumberOfPositions)
                    PT3.CurrentPosition = header->PT3_LoopPosition;
                PT3_A.Address_In_Pattern = ay_sys_getword(&module[PT3_PatternsPointer + header->PT3_PositionList[PT3.CurrentPosition] * 2]);
                PT3_B.Address_In_Pattern = ay_sys_getword(&module[PT3_PatternsPointer + header->PT3_PositionList[PT3.CurrentPosition] * 2 + 2]);
                PT3_C.Address_In_Pattern = ay_sys_getword(&module[PT3_PatternsPointer + header->PT3_PositionList[PT3.CurrentPosition] * 2 + 4]);
                PT3.Noise_Base = 0;
            }
            PT3_PatternIntterpreter(info, &PT3_A);
        }
        PT3_B.Note_Skip_Counter--;
        if(PT3_B.Note_Skip_Counter == 0)
            PT3_PatternIntterpreter(info, &PT3_B);
        PT3_C.Note_Skip_Counter--;
        if(PT3_C.Note_Skip_Counter == 0)
            PT3_PatternIntterpreter(info, &PT3_C);
        PT3.DelayCounter = PT3.Delay;
    }

    AddToEnv = 0;
    TempMixer = 0;
    PT3_ChangeRegisters(info, &PT3_A, &AddToEnv, &TempMixer);
    PT3_ChangeRegisters(info, &PT3_B, &AddToEnv, &TempMixer);
    PT3_ChangeRegisters(info, &PT3_C, &AddToEnv, &TempMixer);

    ay_writeay(AY_MIXER, TempMixer);

    ay_writeay(AY_CHNL_A_FINE, PT3_A.Ton & 0xff);
    ay_writeay(AY_CHNL_A_COARSE, (PT3_A.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_B_FINE, PT3_B.Ton & 0xff);
    ay_writeay(AY_CHNL_B_COARSE, (PT3_B.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_C_FINE, PT3_C.Ton & 0xff);
    ay_writeay(AY_CHNL_C_COARSE, (PT3_C.Ton >> 8) & 0xf);
    ay_writeay(AY_CHNL_A_VOL, PT3_A.Amplitude);
    ay_writeay(AY_CHNL_B_VOL, PT3_B.Amplitude);
    ay_writeay(AY_CHNL_C_VOL, PT3_C.Amplitude);

    ay_writeay(AY_NOISE_PERIOD, (PT3.Noise_Base + PT3.AddToNoise) & 31);
    cur_env = ay_sys_getword(&PT3.Env_Base_lo) + AddToEnv + PT3.Cur_Env_Slide;
    ay_writeay(AY_ENV_FINE, cur_env & 0xff);
    ay_writeay(AY_ENV_COARSE, (cur_env >> 8) & 0xff);

    if(PT3.Cur_Env_Delay > 0)
    {
        PT3.Cur_Env_Delay--;
        if(PT3.Cur_Env_Delay == 0)
        {
            PT3.Cur_Env_Delay = PT3.Env_Delay;
            PT3.Cur_Env_Slide += PT3.Env_Slide_Add;
        }
    }
}

void PT3_Play(AYSongInfo * info)
{
    PT3_Play_Chip(info);
}

unsigned long PT3_GetTime(unsigned char *module, unsigned long * Loop)
{
    char a1, a2, a3, a11, a22, a33;
    unsigned long j1, j2, j3;
    long c1, c2, c3, c4, c5, c8;
    long i, j, tm = 0;
    unsigned char b;
    PT3_File *header = (PT3_File *)module;
    unsigned char ptDelay = header->PT3_Delay;
    unsigned char ptNumPos = header->PT3_NumberOfPositions;
    unsigned short ptLoopPos = header->PT3_LoopPosition;
    unsigned short ptPatPt = PT3_PatternsPointer;
    const unsigned char *ptPosList = (unsigned char *)&header->PT3_PositionList[0];

    b = ptDelay;
    a11 = a22 = a33 = 1;
    for(i = 0; i < ptNumPos; i++)
    {
        if(i == ptLoopPos)
        {
            *Loop = tm;
        }
        j1 = ay_sys_getword(&module[ptPatPt + ptPosList[i] * 2]);
        j2 = ay_sys_getword(&module[ptPatPt + ptPosList[i] * 2 + 2]);
        j3 = ay_sys_getword(&module[ptPatPt + ptPosList[i] * 2 + 4]);
        a1 = a2 = a3 = 1;
        do
        {
            a1--;
            if(a1 == 0)
            {
                if(module[j1] == 0)
                    break;
                j = c1 = c2 = c3 = c4 = c5 = c8 = 0;
                do
                {
                    unsigned char val = module[j1];
                    if(val == 0xd0 || val == 0xc0 || (val >= 0x50 && val <= 0xaf))
                    {
                        a1 = a11;
                        j1++;
                        break;
                    }
                    else if(val == 0x10 || val >= 0xf0)
                    {
                        j1++;
                    }
                    else if(val >= 0xb2 && val <= 0xbf)
                    {
                        j1 += 2;
                    }
                    else if(val == 0xb1)
                    {
                        j1++;
                        a11 = module[j1];
                    }
                    else if(val >= 0x11 && val <= 0x1f)
                    {
                        j1 += 3;
                    }
                    else
                    {
                        switch(val)
                        {
                            case 1:
                                j++;
                                c1 = j;
                                break;
                            case 2:
                                j++;
                                c2 = j;
                                break;
                            case 3:
                                j++;
                                c3 = j;
                                break;
                            case 4:
                                j++;
                                c4 = j;
                                break;
                            case 5:
                                j++;
                                c5 = j;
                                break;
                            case 8:
                                j++;
                                c8 = j;
                                break;
                            case 9:
                                j++;
                                break;
                            default:
                                break;
                        }
                    }
                    j1++;
                }
                while(true);

                while(j > 0)
                {
                    if(j == c1 || j == c8)
                    {
                        j1 += 3;
                    }
                    else if(j == c2)
                    {
                        j1 += 5;
                    }
                    else if(j == c3 || j == c4)
                    {
                        j1++;
                    }
                    else if(j == c5)
                    {
                        j1 += 2;
                    }
                    else
                    {
                        b = module[j1];
                        j1++;
                    }
                    j--;
                }
            }
            a2--;
            if(a2 == 0)
            {
                j = c1 = c2 = c3 = c4 = c5 = c8 = 0;
                do
                {
                    unsigned char val = module[j2];
                    if(val == 0xd0 || val == 0xc0 || (val >= 0x50 && val <= 0xaf))
                    {
                        a2 = a22;
                        j2++;
                        break;
                    }
                    else if(val == 0x10 || val >= 0xf0)
                    {
                        j2++;
                    }
                    else if(val >= 0xb2 && val <= 0xbf)
                    {
                        j2 += 2;
                    }
                    else if(val == 0xb1)
                    {
                        j2++;
                        a22 = module[j2];
                    }
                    else if(val >= 0x11 && val <= 0x1f)
                    {
                        j2 += 3;
                    }
                    else
                    {
                        switch(val)
                        {
                            case 1:
                                j++;
                                c1 = j;
                                break;
                            case 2:
                                j++;
                                c2 = j;
                                break;
                            case 3:
                                j++;
                                c3 = j;
                                break;
                            case 4:
                                j++;
                                c4 = j;
                                break;
                            case 5:
                                j++;
                                c5 = j;
                                break;
                            case 8:
                                j++;
                                c8 = j;
                                break;
                            case 9:
                                j++;
                                break;
                            default:
                                break;
                        }
                    }
                    j2++;
                }
                while(true);
                while(j > 0)
                {
                    if(j == c1 || j == c8)
                    {
                        j2 += 3;
                    }
                    else if(j == c2)
                    {
                        j2 += 5;
                    }
                    else if(j == c3 || j == c4)
                    {
                        j2++;
                    }
                    else if(j == c5)
                    {
                        j2 += 2;
                    }
                    else
                    {
                        b = module[j2];
                        j2++;
                    }
                    j--;
                }
            }
            a3--;
            if(a3 == 0)
            {
                j = c1 = c2 = c3 = c4 = c5 = c8 = 0;
                do
                {
                    unsigned char val = module[j3];
                    if(val == 0xd0 || val == 0xc0 || (val >= 0x50 && val <= 0xaf))
                    {
                        a3 = a33;
                        j3++;
                        break;
                    }
                    else if(val == 0x10 || val >= 0xf0)
                    {
                        j3++;
                    }
                    else if(val >= 0xb2 && val <= 0xbf)
                    {
                        j3 += 2;
                    }
                    else if(val == 0xb1)
                    {
                        j3++;
                        a33 = module[j3];
                    }
                    else if(val >= 0x11 && val <= 0x1f)
                    {
                        j3 += 3;
                    }
                    else
                    {
                        switch(val)
                        {
                            case 1:
                                j++;
                                c1 = j;
                                break;
                            case 2:
                                j++;
                                c2 = j;
                                break;
                            case 3:
                                j++;
                                c3 = j;
                                break;
                            case 4:
                                j++;
                                c4 = j;
                                break;
                            case 5:
                                j++;
                                c5 = j;
                                break;
                            case 8:
                                j++;
                                c8 = j;
                                break;
                            case 9:
                                j++;
                                break;
                            default:
                                break;
                        }
                    }
                    j3++;
                }
                while(true);
                while(j > 0)
                {
                    if(j == c1 || j == c8)
                    {
                        j3 += 3;
                    }
                    else if(j == c2)
                    {
                        j3 += 5;
                    }
                    else if(j == c3 || j == c4)
                    {
                        j3++;
                    }
                    else if(j == c5)
                    {
                        j3 += 2;
                    }
                    else
                    {
                        b = module[j3];
                        j3++;
                    }
                    j--;
                }
            }
            tm += b;
        }
        while(true);

    }
    return tm;
}


void waste2 (void)
{
	char a;


	a++;  a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++; a++;
}






