// function VTM2PT3(PT3:PSpeccyModule;VTM:PModule;
// function PT32VTM

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>

static unsigned char buffer[65536];

static short ChPtr[3];
static unsigned char Skip[3];
static unsigned char SkipCounter[3];
static unsigned char PrevOrn[3];
static int NsBase;
static short pt3_pat_ptr;

struct additional_cmd_type {
	unsigned char Number;
	unsigned char Delay;
	unsigned char Parameter;

};

struct channel_line_type {
	int Note;
	unsigned char Sample;		// 0..31
	unsigned char Ornament;		// 0..15
	unsigned char Volume;		// {1-15 - vol, 0 - prev vol}
	unsigned char Envelope;		// {1-14 - R13, 15 - Envelope off, 0 - prev}
	struct additional_cmd_type Additional_Command;
};

struct item_type {
	unsigned char Noise;
	unsigned short Envelope;
	struct channel_line_type Channel[3];
};

struct pattern_type {
	int length;
	struct item_type Items[256];
} patterns[256];

static int debug=1;

void PatternInterpreter(int PatNum, int LnNum, int ChNum) {

	int quit;
	short tmp;

	quit=0;

	int initial;

	do {

		initial=buffer[ChPtr[ChNum]];

		switch(initial) {

		/* ???? */
		case 0xf0 ... 0xff:
			if (debug) printf("%x: ??\n",initial);
			patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope=
				15;
			PrevOrn[ChNum] = buffer[ChPtr[ChNum]] - 0xf0;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Ornament=
				PrevOrn[ChNum];
			ChPtr[ChNum]++;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Sample=
				buffer[ChPtr[ChNum]] / 2;
	           	break;
		/* Set Sample */
		case 0xd1 ... 0xef:
			if (debug) printf("%x: Sample ",initial);
			patterns[PatNum].Items[LnNum].Channel[ChNum].Sample=
				buffer[ChPtr[ChNum]]-0xd0;
			break;
		/* Nothing? */
		case 0xd0:
			if (debug) printf("%x: Nothing ",initial);
			quit=1;
			break;
		/* Volume */
		case 0xc1 ... 0xcf:
			if (debug) printf("%x: Volume ",initial);
			patterns[PatNum].Items[LnNum].Channel[ChNum].Volume=
				buffer[ChPtr[ChNum]]-0xc0;
			break;
		/* Sound off? */
		case 0xc0:
			patterns[PatNum].Items[LnNum].Channel[ChNum].Note=-2;
			quit=1;
			break;
		/* ??? */
		case 0xb2 ... 0xbf:
			patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope=
				buffer[ChPtr[ChNum]]-0xb1;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Ornament=
				PrevOrn[ChNum];
			ChPtr[ChNum]++;
			patterns[PatNum].Items[LnNum].Envelope=
				((short)buffer[ChPtr[ChNum]])<<8;
			ChPtr[ChNum]++;
			patterns[PatNum].Items[LnNum].Envelope+=
				buffer[ChPtr[ChNum]];
			break;

		/* ??? */
		case 0xb1:
			ChPtr[ChNum]++;
			Skip[ChNum]=buffer[ChPtr[ChNum]];
			break;
		/* ??? */
		case 0xb0:
			patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope=
				15;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Ornament=
				PrevOrn[ChNum];
			break;
		/* Set note */
		case 0x50 ... 0xaf:
			if (debug) printf("%x: Note ",initial);
			patterns[PatNum].Items[LnNum].Channel[ChNum].Note=
				buffer[ChPtr[ChNum]] - 0x50;
			quit=1;
			break;
		/* ??? */
		case 0x40 ... 0x4f:
			if (buffer[ChPtr[ChNum]]==0x40) {
				//  only for Orn #0 rom pt3.69
				if (patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope == 0) {
					patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope = 15;
				}
			}
			PrevOrn[ChNum] = buffer[ChPtr[ChNum]]-0x40;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Ornament=
				PrevOrn[ChNum];
			break;
		/* Set noise */
		case 0x20 ... 0x3f:
			if (debug) printf("%x: Noise ",initial);
			NsBase = buffer[ChPtr[ChNum]]-0x20;
			break;
		/* ?? */
		case 0x10 ... 0x1f:
			if (debug) printf("%x: ??? ",initial);
			if (buffer[ChPtr[ChNum]]==0x10) {
				patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope=15;
			}
			else {
				patterns[PatNum].Items[LnNum].Channel[ChNum].Envelope=buffer[ChPtr[ChNum]]-0x10;
				ChPtr[ChNum]++;
				patterns[PatNum].Items[LnNum].Envelope=((short)(buffer[ChPtr[ChNum]]))<<8;
				ChPtr[ChNum]++;
				patterns[PatNum].Items[LnNum].Envelope+=buffer[ChPtr[ChNum]];
			}
			patterns[PatNum].Items[LnNum].Channel[ChNum].Ornament = PrevOrn[ChNum];
			ChPtr[ChNum]++;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Sample=
				buffer[ChPtr[ChNum]] / 2;
			break;
		/* Additional Command Number */
		case 0x8 ... 0x9:
			if (debug) printf("%x: ??? ",initial);
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number=
				buffer[ChPtr[ChNum]];
			break;
		/* Additional Command Number */
		case 0x1 ... 0x5:
			if (debug) printf("%x: ??? ",initial);
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number=
				buffer[ChPtr[ChNum]];
			break;
		default:
			printf("UNKNOWN VALUE %x\n",initial);
			break;
		}

		ChPtr[ChNum]++;
	} while (!quit);

	/* Handle Additional Commands */
	initial=patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number;
	switch(initial) {
	case 1:
		printf("AC: %d\n",initial);
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Delay=buffer[ChPtr[ChNum]];
		ChPtr[ChNum]++;
		memcpy(&tmp,&buffer[ChPtr[ChNum]],2);
		if (tmp<0) {
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number++;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=-tmp;
		}
		else {
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=tmp;
		}
		ChPtr[ChNum]+=2;
		break;
	case 2:
		printf("AC: %d\n",initial);
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number++;
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Delay=buffer[ChPtr[ChNum]];
		ChPtr[ChNum]+=3;
		memcpy(&tmp,&buffer[ChPtr[ChNum]],2);
		if (tmp < 0) {
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter = -tmp;
		} else {
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter = tmp;
		}
		ChPtr[ChNum]+=2;
		break;
	case 3:
	case 4:
		printf("AC: %d\n",initial);
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number++;
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=buffer[ChPtr[ChNum]];
		ChPtr[ChNum]++;
		break;
	case 5:
		printf("AC: %d\n",initial);
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number++;
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=
			buffer[ChPtr[ChNum]]<<4;
		ChPtr[ChNum]++;
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter+=
			buffer[ChPtr[ChNum]];
		ChPtr[ChNum]++;
		break;
	case 8:
		printf("AC: %d\n",initial);
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number++;
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Delay=buffer[ChPtr[ChNum]];
		ChPtr[ChNum]++;
		memcpy(&tmp,&buffer[ChPtr[ChNum]],2);
		if (tmp<0) {
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number++;
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=-tmp;
		} else {
			patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=tmp;
		}
		ChPtr[ChNum]+=2;
		break;
	case 9:
		printf("AC: %d\n",initial);
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Number=0xb;
		patterns[PatNum].Items[LnNum].Channel[ChNum].Additional_Command.Parameter=buffer[ChPtr[ChNum]];
        	ChPtr[ChNum]++;
		break;
	case 0:	/* do nothing? */
		break;
	default:
		printf("Unknown Additional Command %d!\n",initial);
		break;
	}
	SkipCounter[ChNum]=Skip[ChNum];

}

//TPattern = record
//   Length:integer;
//   Items:array[0..MaxPatLen-1] of record
//    Noise:byte;
//    Envelope:word;
//    Channel:array[0..2]of TChannelLine;
//   end;
//  end;

//TChannelLine = packed record
//   Note:shortint; {0..95} {-2 - Sound off (R--)} {-1 - No note (---)}
//   Sample:byte; {0..31}
//   Ornament:byte; {0..15}
//   Volume:shortint; {1-15 - vol, 0 - prev vol}
//   Envelope:byte; {1-14 - R13, 15 - Envelope off, 0 - prev}
//   Additional_Command:TAdditionalCommand;
//  end;



// ChPtr:packed array[0..2] of word;
// Skip:array[0..2] of byte;
// SkipCounter:array[0..2] of byte;
// PrevOrn:array[0..2] of byte;
// NsBase:integer;
// TS:integer;






#define MAXPATLEN	256

void DecodePattern(int j, int jj) {
	int i,k,quit,NsBase;

   	for(k=0;k<3;k++) {
		PrevOrn[k] = 0;
		SkipCounter[k] = 1;
		Skip[k] = 1;
	}

	memcpy( ChPtr, &buffer[pt3_pat_ptr+jj*6],6);
	printf("\tCopying 6 bytes at %x\n",pt3_pat_ptr+(jj*6));

	NsBase = 0;
	i = 0;
	quit = 0;

	while ((i < MAXPATLEN) && (!quit)) {
		for(k=0;k<3;k++) {
			SkipCounter[k]--;
			if (SkipCounter[k]==0) {
				if ((k == 0) && (buffer[ChPtr[0]]== 0)) {
					i--;
					quit = 1;
					break;
  				}
				PatternInterpreter(j,i,k);
       			}
		}
		patterns[j].Items[i].Noise = NsBase;
		i++;
	}
	patterns[j].length = i;
	printf("\tPattern: %d long\n",i);
}

int FoundPT36TS(void) {
#if 0
 function FoundPT36TS:boolean;
 var
  j1,j2,k,Pos:Integer;
 begin
 Result := False;
 if PT3.PT3_Name[13] <> '6' then exit;
 Pos := 0;
 while (Pos < 256) and (PT3.Index[Pos + $c9] <> 255) do
  begin
   j1 := PT3.Index[Pos + $c9] div 3;
   if j1 < $30/2 then exit;
   j2 := $30 - j1 - 1;
   move(PT3.Index[PT3.PT3_PatternsPointer + j2*6],ChPtr,6);
   for k := 0 to 2 do
    if (ChPtr[k] < 100) or (ChPtr[k] >= FSize - 4) then exit;
   Inc(Pos);
  end;
 if MessageDlg('This PT 3.6 module can contain Turbo Sound data. Try to import?',
    mtConfirmation, [mbYes, mbNo], 0) <> mrYes then exit;
 TS := $30;
 Result := True;
 end;
#endif
	return 1;
}

int main(int argc, char **argv) {

	int i;
	int fd,result;
	char pt3_name[100];
	char pt3_table,pt3_delay,pt3_numpos,pt3_looppos;
	short pt3_sample_ptrs[32];
	short pt3_ornament_ptrs[16];
	int found_pt36=0;

	char *filename="mockingbird.pt3";

	fd=open(filename,O_RDONLY);
	if (fd<0) {
		fprintf(stderr,"Error opening %s: %s\n",filename,strerror(errno));
		return -1;
	}
	result=read(fd,buffer,8192);
	close(fd);

	printf("Read %d bytes ($%04x) from %s\n",result,result,filename);

	/* get name, 98 bytes long? */
	/* Contains version info, name of song, and author name */
	memcpy(pt3_name,(char *)buffer,98);

	/* Get various paramaters that follow */
	pt3_table=buffer[99];
	pt3_delay=buffer[100];
	pt3_numpos=buffer[101];
	pt3_looppos=buffer[102];
	pt3_pat_ptr=buffer[103]|buffer[104]<<8;

	/* Load in the 32 sample pointers */
	for(i=0;i<32;i++) {
		pt3_sample_ptrs[i]=buffer[105+(2*i)]|buffer[106+(2*i)]<<8;
	}

	/* Load in the 16 ornament pointers */
	for(i=0;i<16;i++) {
		pt3_ornament_ptrs[i]=buffer[169+(2*i)]|buffer[170+(2*i)]<<8;
	}

	printf("PT3:\n");
	pt3_name[29]='\n';
	pt3_name[30]='\t';
	printf("\t%s\n",pt3_name);
	printf("\tTable: $%02x\n",pt3_table);
	printf("\tDelay: $%02x\n",pt3_delay);
	printf("\tNumPos: $%02x\n",pt3_numpos);
	printf("\tLoopPos: $%02x\n",pt3_looppos);
	printf("\tPatPtr: $%04x\n",pt3_pat_ptr);
	printf("\tSmplPtr:");
		for(i=0;i<32;i++) {
			if (i%8==0) printf("\n\t\t");
			printf(" $%04x",pt3_sample_ptrs[i]);
		}
	printf("\n");

	printf("\tOrnamentPtr:");
		for(i=0;i<16;i++) {
			if (i%8==0) printf("\n\t\t");
			printf(" $%04x",pt3_ornament_ptrs[i]);
		}
	printf("\n");

// File Format:
//	00..1e (  0.. 30) = "ProTracker 3.4 compilation of " or similar
//	1f..3e ( 31.. 63) = Title
//			  = " by "
//	43..62 (        ) = Author
//          63 (      99) = Table
//	    64 (     100) = Delay
//          65 (     101) = NumPos
//          66 (     102) = LoopPos
//      67..68 (103..104) = PatPtr
//	       (105..168)	32 Sample Pointer
//	       (169..200)	16 Ornament Pointers
//	    C9


 
//function PT32VTM;
//var
// ChPtr:packed array[0..2] of word;
// Skip:array[0..2] of byte;
// SkipCounter:array[0..2] of byte;
// PrevOrn:array[0..2] of byte;
// NsBase:integer;
// TS:integer;

//var
// i,j,k,kk,Pos:Integer;
// tset:boolean;
//begin
//Result := True;
//if DetectFeaturesLevel then
// begin
//  if StrLComp(@PT3.PT3_Name,'ProTracker 3.',13) = 0 then
//   case Ord(PT3.PT3_Name[13]) of
//   $30..$35: VTM1.FeaturesLevel :=  0;
//   $37..$39: VTM1.FeaturesLevel :=  2;
//   else VTM1.FeaturesLevel :=  1;
//   end
//  else if StrLComp(@PT3.PT3_Name,'Vortex Tracker II',17) = 0 then
//   VTM1.FeaturesLevel :=  1
//  else
//   VTM1.FeaturesLevel :=  0;
// end;
//if DetectModuleHeader then
// VTM1.VortexModule_Header := StrLComp(@PT3.PT3_Name,'ProTracker 3.',13) <> 0;


// Title
	//SetLength(VTM1.Title,32);
	//Move(PT3.PT3_Name[$1e],VTM1.Title[1],32);
	// VTM1.Title := TrimRight(VTM1.Title);
// Author
	//SetLength(VTM1.Author,32);
	// Move(PT3.PT3_Name[$42],VTM1.Author[1],32);
	// VTM1.Author := TrimRight(VTM1.Author);
// Table
	// VTM1.Ton_Table := PT3.PT3_Table;
// Delay
	// VTM1.Initial_Delay := PT3.PT3_Delay;
// LoopPos
	// VTM1.Positions.Loop := PT3.PT3_LoopPosition;

	// for i := 0 to 255 do
	// VTM1.Positions.Value[i] := 0;
// OrnamentPtrs
	// VTM1.Ornaments[0] := nil;
	// for i := 1 to 15 do
	// begin
	// if PT3.PT3_OrnamentPointers[i] = 0 then
	//  VTM1.Ornaments[i] := nil
	//  else
	//   begin
//   New(VTM1.Ornaments[i]);
//   VTM1.Ornaments[i].Loop := PT3.Index[PT3.PT3_OrnamentPointers[i]];
//   VTM1.Ornaments[i].Length := PT3.Index[PT3.PT3_OrnamentPointers[i] + 1];
//   for j := 0 to VTM1.Ornaments[i].Length - 1 do
//    VTM1.Ornaments[i].Items[j] := PT3.Index[PT3.PT3_OrnamentPointers[i] + 2 + j];
//  end;
// end;

// SamplePtrs
	// for i := 1 to 31 do
	// begin
	//  if PT3.PT3_SamplePointers[i]=0 then
	//   VTM1.Samples[i] := nil
	//  else
	//   begin
	//     New(VTM1.Samples[i]);
	//    VTM1.Samples[i].Loop := PT3.Index[PT3.PT3_SamplePointers[i]];
	//     VTM1.Samples[i].Length := PT3.Index[PT3.PT3_SamplePointers[i] + 1];
	//    for j := 0 to VTM1.Samples[i].Length - 1 do
	//     begin
//      VTM1.Samples[i].Items[j].Add_to_Ton :=
//                WordPtr(@PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 4])^;
//      VTM1.Samples[i].Items[j].Ton_Accumulation :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 3] and $40 <> 0;
//      VTM1.Samples[i].Items[j].Amplitude :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 3] and $f;
//      VTM1.Samples[i].Items[j].Amplitude_Sliding :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 2] and $80 <> 0;
//      VTM1.Samples[i].Items[j].Amplitude_Slide_Up :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 2] and $40 <> 0;
//      VTM1.Samples[i].Items[j].Envelope_Enabled :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 2] and 1 = 0;
//      VTM1.Samples[i].Items[j].Envelope_or_Noise_Accumulation :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 3] and $20 <> 0;
//      VTM1.Samples[i].Items[j].Add_to_Envelope_or_Noise :=
//                PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 2] shr 1;
//      if VTM1.Samples[i].Items[j].Add_to_Envelope_or_Noise and $10 <> 0 then
//       VTM1.Samples[i].Items[j].Add_to_Envelope_or_Noise :=
//         VTM1.Samples[i].Items[j].Add_to_Envelope_or_Noise or shortint($f0)
//      else
//       VTM1.Samples[i].Items[j].Add_to_Envelope_or_Noise :=
//         VTM1.Samples[i].Items[j].Add_to_Envelope_or_Noise and 15;
//       VTM1.Samples[i].Items[j].Mixer_Ton :=
//         PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 3] and $10 = 0;
//       VTM1.Samples[i].Items[j].Mixer_Noise :=
//         PT3.Index[PT3.PT3_SamplePointers[i] + j*4 + 3] and $80 = 0
//     end
//   end
// end;


//for i := 0 to MaxPatNum do
// VTM1.Patterns[i] := nil;

//VTM2 := nil; TS := Byte(PT3.PT3_Name[98]);

	if ((pt3_table!=0x20)) {
		if ((buffer[13]>='7') && (buffer[13]<='9')) {
			printf("Special case if PT3.6 or later\n");
		}
		if (found_pt36) {
			printf("Special case if PT3.6 or later\n");
		}

//if ((TS <> $20) and (PT3.PT3_Name[13] in ['7'..'9'])) or FoundPT36TS then
// begin
//  New(VTM2);
//  VTM2^ := VTM1^;
//  New(VTM2.Patterns[-1]);
//  VTM2.Patterns[-1]^ := VTM1.Patterns[-1]^;
//  for i := 1 to 15 do
//   if VTM1.Ornaments[i] <> nil then
//    begin
//     New(VTM2.Ornaments[i]);
//     VTM2.Ornaments[i]^ := VTM1.Ornaments[i]^;
//    end;
//  for i := 1 to 31 do
//   if VTM1.Samples[i] <> nil then
//    begin
//     New(VTM2.Samples[i]);
//     VTM2.Samples[i]^ := VTM1.Samples[i]^;
//    end;
// end;
	}

	int pos,j;

	pos=0;

	while((pos<256) && (buffer[pos+0xc9]!=255)) {

		j = buffer[pos + 0xc9] / 3;

		// if newer j=TS-j-1?
		printf("Positions[%d]=%d\n",pos,j);
		pos++;
#if 0

		if VTM2 <> nil then {
			DecodePattern(VTM2,j,j);
			DecodePattern(VTM1,j,TS - j - 1);
			for i := 0 to MaxPatLen - 1 do {
				tset := False;
				for k := 2 downto 0 do
				if VTM2.Patterns[j].Items[i].Channel[k].Additional_Command.Number = 11 then {
					for kk := 2 downto 0 do
					if VTM1.Patterns[j].Items[i].Channel[kk].Additional_Command.Number in [0,11] then {
						VTM1.Patterns[j].Items[i].Channel[kk].Additional_Command :=
						VTM2.Patterns[j].Items[i].Channel[k].Additional_Command;
						break;
					};
					tset := True;
					break;
				};
				if not tset then for k := 2 downto 0 do
				if VTM1.Patterns[j].Items[i].Channel[k].Additional_Command.Number = 11 then {
					for kk := 2 downto 0 do
					if VTM2.Patterns[j].Items[i].Channel[kk].Additional_Command.Number = 0 then {
						VTM2.Patterns[j].Items[i].Channel[kk].Additional_Command :=
						VTM1.Patterns[j].Items[i].Channel[k].Additional_Command;
						break;
					};
					break;
				};
			}
		}
		else {
#endif
   			DecodePattern(j,j);
#if 0
		}
#endif
	}
//	VTM1.Positions.Length := Pos;
//	if VTM2 <> nil then VTM2.Positions.Length := Pos;

	return 0;
}
