#include <stdio.h>
#include <ctype.h>

#include "hd00Contents.h"
#include "hd00Midi.h"


int key2Ascii[] = {
  0x23, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x31, 0x00, 0x00, 0x00,

  0x0d, 0x0a, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,

  0x40, 0x00, 0x00, 0x00,
  0x31, 0x00, 0x00, 0x00,
  0x32, 0x00, 0x00, 0x00,
  0x0d, 0x0a, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00
};

int key2VnSingle[] = {
  0x23, 0x00, 0x00, 0x00,
  0x56, 0x00, 0x00, 0x00,
  0x38, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,

  0x30, 0x00, 0x00, 0x00,
  0x0d, 0x0a, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,
  0x31, 0x00, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,

  0x31, 0x00, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,
  0x31, 0x00, 0x00, 0x00,
  0x0d, 0x00, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00
};

int key2Dual[] = {
  0x23, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x31, 0x00, 0x00, 0x00,

  0x0d, 0x0a, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,

  0x40, 0x00, 0x00, 0x00,
//  0x31, 0x00, 0x00, 0x00,
  0x30, 0x00, 0x00, 0x00,
  0x32, 0x00, 0x00, 0x00,
  0x0d, 0x0a, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00
};

unsigned long get32(FILE *f)
{
  unsigned long tmp, tmp2;

  tmp = 0;

  tmp2 = getc(f);
  tmp = (tmp << 8) | (tmp2 & 0x0FF);
  tmp2 = getc(f);
  tmp = (tmp << 8) | (tmp2 & 0x0FF);
  tmp2 = getc(f);
  tmp = (tmp << 8) | (tmp2 & 0x0FF);
  tmp2 = getc(f);
  tmp = (tmp << 8) | (tmp2 & 0x0FF);

  return tmp;
}

void put32(FILE *f, unsigned long tmp)
{
  int tmp2;

  tmp2 = (tmp >> 24) & 0x0FF;
  putc(tmp2, f);
  tmp2 = (tmp >> 16) & 0x0FF;
  putc(tmp2, f);
  tmp2 = (tmp >> 8) & 0x0FF;
  putc(tmp2, f);
  tmp2 = (tmp & 0x0FF);
  putc(tmp2, f);
}

//---------------------------------------------------------------------------
int getTableOfContents(FILE *inFile, unsigned long toc[TOC_LENGTH])
{
  int i;
  unsigned int tmp, tmp2;

  fseek(inFile, TOC_OFFSET, SEEK_SET);

  /*Read Table of Contents*/
  for (i=0; i<TOC_LENGTH; i++)
  {
    toc[i] = get32(inFile);
  }

  /*printf("Table of song entry tables:\n");*/

  /*Verify if addresses in table are valid*/
  /*TODO: Compare with length of file instead.*/
  for(i=0; i<TOC_LENGTH; i++)
  {
    tmp = fseek(inFile, toc[i], SEEK_SET);
    tmp2 = getc(inFile);

    /*printf("%4d: %8X %d %X\n", i, songEntryTable[i], tmp, tmp2);*/

    if(tmp != 0 | tmp2 == EOF)
    {
      break;
    }
  }

  /*printf("noSongEntryTables: %d\n\n", i);*/
  return i;
}


//---------------------------------------------------------------------------
int getSongEntryTable(FILE *inFile, unsigned long offset, SongEntry songEntry[TOC_LENGTH])

{
  int noSongs, i;
  unsigned int tmp, tmp2, tmp3, tmp4;

  fseek(inFile, offset, SEEK_SET);
    
  for (i=0; i<TOC_LENGTH; i++)
  {
    songEntry[i].offset = get32(inFile);
    songEntry[i].length = 0;
    songEntry[i].midiOffset = 0;
  }
  for (i=0; i<TOC_LENGTH; i++)
  {
    songEntry[i].unknown = get32(inFile);
  }
  
  /*printf("CONTENT: %8X - %8X: Table of song entries %d\n", songEntryTable[j], songEntryTable[j]+0x800, j);
    printf("Table of song entries %d:\n", j);*/

  noSongs = 0;
  for(i=0; i<TOC_LENGTH; i++)
  {
    tmp = fseek(inFile, songEntry[i].offset, SEEK_SET);
    tmp2 = getc(inFile);

    /*printf("%4d: %8X ", i, songEntry[i]);*/

    if(tmp != 0 | tmp2 == EOF)
    {
      /*printf("\n");*/
      continue;
    }
    else
    {
      fseek(inFile, songEntry[i].offset, SEEK_SET);
      tmp = get32(inFile);
      tmp2 = get32(inFile);

      /* Check if this is a song entry */
      if ((tmp < 0x1FFFF) && (tmp > 0x100) && (tmp > tmp2) && (tmp2 < 0x3FFF) && (tmp2 > 0x10) && (songEntry[i].unknown != 0))
      {
	fseek(inFile, songEntry[i].offset+tmp2+14, SEEK_SET);
            
	tmp3 = getc(inFile);
	tmp4 = getc(inFile);
	/* One more check if this is a song entry ("00 00" in beginning of midi part) */
	if ((tmp3 == 0) || (tmp4 == 0))
        {
	  noSongs++;
	  songEntry[i].length = tmp;
	  songEntry[i].midiOffset = tmp2;
	}
	else
        {
	  /*printf("Not a song entry!\n");*/
	  continue;
	}
      }
      else
      {
	/*printf("Not a song entry.\n");*/
	continue;
      }
    }
  }
  return noSongs;
}

//---------------------------------------------------------------------------
#ifdef __BORLANDC__
int ReadLyricDatOld(FILE *inFile, int offset, FILE *outFile, int length, int mode)
#else
int ReadLyricDat(FILE *inFile, int offset, FILE *outFile, int length, int mode=ASCII_MODE)
#endif
{
  int n, k, tmp;
  int keyLen;
  int key[100];
  int *key2;

  if(mode==ASCII_MODE)
  {
    key2 = key2Ascii;
  }
  else if (mode==DUALBYTE_MODE)
  {
    key2 = key2Dual;
  }

  /* Write the first 4 bytes to output file */
  fseek(inFile, offset, SEEK_SET);
  for (n=0; n<4; n++)
  {
    tmp = getc(inFile);
    if (tmp == EOF)
    {
      return 0;
    }
    putc(tmp, outFile);
  }

  /* Determine the Key length */
  for(keyLen=0; keyLen<100; keyLen++)
  {
    key[keyLen] = getc(inFile);
    if ((keyLen>3) &&
        (key[keyLen-2]==key[1]) &&
        (key[keyLen-1]==key[2]) &&
        (key[keyLen]==key[3]))
    {
      keyLen -= 3;
      printf("Key length: %d\n", keyLen);
      break;
    }
  }
  if (keyLen>60)
  {
    printf("!");        // First attempt to determine Key length failed
    for(keyLen=8; keyLen<100; keyLen++)
    {
      if((key[keyLen-2]==key[5]) &&
         (key[keyLen-1]==key[6]) &&
         (key[keyLen]==key[7]))
      {
        keyLen -= 7;
        printf("Key length: %d\n", keyLen);
        break;
      }
    }
  }
  if (keyLen>60)
  {
//    printf("Failed to determine Key length\n");
    return 0;
  }

  /* Write key to output file */
  for(k=0; k<keyLen; k++)
  {
    putc(key[k], outFile);
  }

  /* Decode and write lyrics part to output file */
  fseek(inFile, offset+keyLen+4, SEEK_SET);
  n = keyLen+4;
//  k2 = 0;
  while (n < length)
  {
    int c0,c1,c2,c3;
    for (k=0; k<keyLen; k++)
    {
      tmp = getc(inFile);
      /* === For debugging ====
      switch (k%4)
      {   case 0:
              c0= tmp ^key[k] ^key2[k];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\n", k,key[k],key2[k],tmp, c0);
              break;

          case 1:
              c1= tmp ^key[k] ^key2[k];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\n", k,key[k],key2[k],tmp, c1);
              break;

          case 2:
              c2= tmp ^key[k] ^key2[k];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\n", k,key[k],key2[k],tmp, c2);
              break;

          case 3:
              c3= tmp ^key[k] ^key2[k];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\t[%c] %02x %02x\n", k,key[k],key2[k],tmp, c0,(char)c0, c3,c2);
              break;
      }
      */
      tmp = tmp ^key[k] ^key2[k];
/*
      if ((k%4) == 0)
      {
        tmp = (tmp ^ key2[k2]);
        k2 = (k2+1) % (keyLen/4);
      }
*/
      putc(tmp, outFile);
      n++;
      if (n>=length)
      {
        break;
      }
    }
  }
  return keyLen;
}

//---------------------------------------------------------------------------
int ReadLyricDat(FILE *inFile, int offset, FILE *outFile, int length, int mode)
{
int     Tmp, KeyLen, Key[128], *Key2, Char4[4];
char    KeyChar;

printf("%x\n", mode);
switch( mode)
{   case LNG_CHINESE:
        Key2= key2Dual;
        break;

    case LNG_VIETNAMESE:
        Key2= key2VnSingle;
        break;

    default:
        Key2= key2Ascii;
        break;
}
/*
{   case ASCII_MODE:
        Key2= key2Ascii;
        break;

    case DUALBYTE_MODE:
        Key2= key2Dual;
        break;
}
*/

/* Write the first 4 bytes to output file */
fseek(inFile, offset, SEEK_SET);
for( int I= 0; I < 4; I++)
{       Char4[I]= getc(inFile);
        if( Char4[I]== EOF)
                return 0;
        putc( Char4[I], outFile);
}

/* Determine the Key length */
// Read the beginning section (mask1)
for( int I= 0; I < 128; I++)
        Key[I]= getc(inFile);
// Try to find the end of mask1 applying 4 bytes from the mask
KeyLen= 0;
for( int I= 0; I < 70 && !KeyLen; I++)
        for( int J= 32; J < 128-3; J++)
//        {       printf("%d/%d %02x\n", I,J,Char4[1]);
                if( (Key[I] ^Key[J] ^Key2[I])== Char4[1]
                  && (Key[I+1] ^Key[J+1] ^Key2[I+1])== Char4[0]
                  && !(Key[I+2] ^Key[J+2] ^Key2[I+2])
                  && !(Key[I+3] ^Key[J+3] ^Key2[I+3]))
                {       KeyLen= J-I;
                        printf("Key length: %d\n", KeyLen);
                        break;
                }
//        }
if(!KeyLen)
        return 0;

for( int I= 0; I < KeyLen; I++)
        putc( Key[I], outFile);

/* Decode and write lyrics part to output file */
fseek(inFile, offset+ KeyLen+4, SEEK_SET);
int     J= KeyLen+4;

while (J < length)
{       int c0,c1,c2,c3;
        for ( int K=0; K < KeyLen; K++)
        {       Tmp = getc(inFile);
      /* === For debugging ====*/
      switch (K%4)
      {   case 0:
              c0= Tmp ^Key[K] ^Key2[K];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\n", K,Key[K],Key2[K],Tmp, c0);
              break;

          case 1:
              c1= Tmp ^Key[K] ^Key2[K];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\n", K,Key[K],Key2[K],Tmp, c1);
              break;

          case 2:
              c2= Tmp ^Key[K] ^Key2[K];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\n", K,Key[K],Key2[K],Tmp, c2);
              break;

          case 3:
              c3= Tmp ^Key[K] ^Key2[K];
              printf("%3d k=0x%02x k2=0x%02x v=%02x o=%02x\t[%c] %02x %02x\n", K,Key[K],Key2[K],Tmp, c0,(char)c0, c3,c2);
              break;
      }
/*      */
            Tmp= Tmp ^Key[K] ^Key2[K];
/*
      if ((k%4) == 0)
      {
        tmp = (tmp ^ key2[k2]);
        k2 = (k2+1) % (keyLen/4);
      }
*/
            putc( Tmp, outFile);
            J++;
            if ( J >=length)
                    break;
        }
}
return KeyLen;
}

//---------------------------------------------------------------------------
unsigned int findMidi(FILE *f)
{
  unsigned int i;
  unsigned int prev[3];


  prev[0] = 0xCC;
  prev[1] = 0xCC;
  prev[2] = 0xCC;
  printf("; ");
  for (i=0; i < 50000; i++)
  {
    prev[0] = prev[1];
    prev[1] = prev[2];
    prev[2] = getc(f);

    if (i<18)
    {
      printf("%2X ", prev[2]);
    }

    if (prev[0] == 0 && prev[1] == 0xFF && (prev[2] & 0x0F0) == 0xC0)
    {
      printf("- %4X %4d", i-2, i-2);
      return 1;
    }
  }
  return 0;
}


void findLyric(FILE *f, unsigned int len)
{
  unsigned int i, ch, nCh;

  for (i=0; i<len; i++)
  {
    ch = getc(f);
    if (isalpha(ch))
    {
      printf("%c", ch);
      nCh++;
      if(nCh > 40)
      {
        printf(" %4d %4d", i,len);
        return;
      }
    }
  }
}





void mapLyric(FILE *f, FILE *outFile, int keyLength)
{
  int i, ch;

  /* First 2 letters of titel*/
  ch = getc(f);
  printf("%02x", ch);
  fprintf(outFile,"%02x", ch);
  ch = getc(f);
  printf(" %c", ch);
  fprintf(outFile," %c", ch);
  ch = getc(f);
  printf(" %02x", ch);
  fprintf(outFile," %02x", ch);
  ch = getc(f);
  printf(" %c\n", ch);
  fprintf(outFile," %c\n", ch);

  printf("Key length: %d\n", keyLength);
  fprintf(outFile,"Key length: %d\n", keyLength);

  printf("Key:");
  fprintf(outFile,"Key:");
  for(i=0; i<keyLength; i++)
  {
    ch = getc(f);
    printf(" %02x", ch);
    fprintf(outFile," %02x", ch);
  }

  for(i=0;; i++)
  {
    ch = getc(f);

    if (ch == EOF)
    {
      break;
    }

    if ((i % 4) == 0)
    {
      ch = (ch & 0x00FF);
      if (ch==0x0d)
      {
        printf("\n");
      }
      else if (isprint(ch))
      {
        printf("%c", ch);
        fprintf(outFile,"%c", ch);
      }
      else
      {
        printf("[%x]", ch);
        fprintf(outFile,"[%x]",ch);
      }
    }
  }
  printf("\n");
}

void mapLyric2b(FILE *f, FILE *outFile, int keyLength)
{
  int i, ch0, ch;

  /* First 2 letters of titel*/
  ch = getc(f);
  printf("%02x", ch);
  fprintf(outFile,"%02x", ch);
  ch = getc(f);
  printf(" %c", ch);
  fprintf(outFile," %c", ch);
  ch = getc(f);
  printf(" %02x", ch);
  fprintf(outFile," %02x", ch);
  ch = getc(f);
  printf(" %c\n", ch);
  fprintf(outFile," %c\n", ch);

  printf("Key length: %d\n", keyLength);
  fprintf(outFile,"Key length: %d\n", keyLength);

  printf("Key:");
  fprintf(outFile,"Key:");
  for(i=0; i<keyLength; i++)
  {
    ch = getc(f);
    printf(" %02x", ch);
    fprintf(outFile," %02x", ch);
  }

  for(i=0;; i++)
  {
    ch = getc(f);

    if (ch == EOF)
    {
      break;
    }

    if ((i % 4) == 0 || (i % 4) == 1)
    {
      ch = (ch & 0x00FF);
      if (ch==0x0d)
        fprintf(outFile,"\n");
      else if (ch)
        fprintf(outFile,"%c", ch);
    }
/*
    if ((i % 4) == 0)
      ch0 = (ch & 0x00FF);

    if ((i % 4) == 1)
    {
      ch = (ch & 0x00FF);
	if( ch0)	// Dual byte
        fprintf(outFile,"%c%c", ch0,ch);
      else if (ch==0x0d)
        fprintf(outFile,"\n");
      else
        fprintf(outFile,"%c", ch0);
    }
*/
  }
  printf("\n");
}

#ifdef __BORLANDC__
void writeDat(FILE *lyricsFile, FILE *midiFile, FILE *outFile, int mode)
#else
void writeDat(FILE *lyricsFile, FILE *midiFile, FILE *outFile, int mode=ASCII_MODE)
#endif
{
  int i, tmp, keyLen, k, done;
  unsigned long tmpLong, len, len2;
  int key[100];
  int *key2;

  if (mode==ASCII_MODE)
  {
    key2 = key2Ascii;
  }
  else if (mode==DUALBYTE_MODE)
  {
    key2 = key2Dual;
  }


  /* Reserve space for length and offset */
  put32(outFile, 0);
  put32(outFile, 0);
  len = 8;

  /* Write first 4 bytes from lyrics file (first 2 letters of Song Title)*/
  tmpLong = get32(lyricsFile);
  put32(outFile, tmpLong);
  len += 4;

  /* Write key */
  for (keyLen=0;;keyLen+=4)
  {
    key[keyLen] = getc(lyricsFile);
    key[keyLen+1] = getc(lyricsFile);
    key[keyLen+2] = getc(lyricsFile);
    key[keyLen+3] = getc(lyricsFile);
    if (key[keyLen+2] == 0 && key[keyLen+3] == 0)
    {
      break;
    }
    putc(key[keyLen], outFile);
    putc(key[keyLen+1], outFile);
    putc(key[keyLen+2], outFile);
    putc(key[keyLen+3], outFile);
    len += 4;
  }
  
  /* Encode and write lyrics file */
  fseek(lyricsFile, keyLen+4, SEEK_SET);
  done = 0;
  while(!done)
  {
    for (k=0; k<keyLen; k++)
    {
      tmp = getc(lyricsFile);
      if(tmp == EOF)
      {
        done = 1;
        break;
      }
      tmp = tmp ^ key[k] ^ key2[k];
      putc(tmp, outFile);
      len++;
    }
  }
  /* Save the midiOffset*/
  len2=len-12;

  /* Write midi part from midi file */
  for (;;)
  {
    tmp = getc(midiFile);
    if(tmp == EOF)
    {
      break;
    }
    putc(tmp, outFile);
    len++;
  }

  fseek(outFile, 0, SEEK_SET);
  put32(outFile, len);
  put32(outFile, len2);
}
