/* bad mixture of C + C++ syntax... what the heck */

#define VERSION "1.2"

//#include <fstream.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "main.h"

#include "ym_conv.h"
#include "abstract.h"
#include "bit_out.h"

#include "decoder.i"
int current_working_register = 0;
char file_name[100];
char song_name[100];

int MIN_PHRASE_LEN     = 2 ; /* inclusive */
int MAX_PHRASE_LEN     = 16; /* exclusive */
int PHRASES_MAX_DEPTH  = 10;
int USE_PHRASE         = 1;
int OPTIMAL_PHRASE     = 1;
int BINARY_DATA        = 0;
int VECTREX_DECODER    = 1;
int PHRASE_OPTIMIZER   = 2;
int COUT_PUT           = 0;
int SINGLE_PACK        = 0;
int RLE_USED           = 1;
int SHANNON            = 1;

//START inclusive
int ENCODE_START = 0;
//END exclusive
int ENCODE_END = 11;

int main( int argc, char* argv[] )
{
 // static, otherwise stack overflow!
 static unsigned char buf[MAXBUFF];
 static unsigned char *out_buf[11];
 FILE *inFile = NULL;
 int filenameArg = 1;
 int vbl_len;
 printf("\nCalling ");
 for (int ii=0;ii<argc;ii++)
   printf(" %s", argv[ii]);
 printf("\n\n");
 if ( argc < 2 )
 {
   printf("Usage: ym_vpack <infile> options... (-h for more help)\n");
   printf("ym_vpack = YM (Sound processor) to Vectrex PSG readable packer. \n");
  return 1;
 }

 int i=1;
 if (argc>1)
 {
  while (argv[i])
  {
   char *pointer=argv[i];
   if ((pointer[0]=='-')||(pointer[0]=='/')||(pointer[0]==':')||(pointer[0]=='\\'))
   {
    if ((pointer[1]=='h')||(pointer[1]=='H')||(pointer[1]=='?'))
    {
     printf("-h   Help\n");
     printf("-H   Help\n");
     printf("-?   Help\n");
     printf("-p   Use phrases (yes)\n");
     printf("-n#  Minumum phrase len (2) [min 2]\n");
     printf("-m#  Maximum phrase len (15) [max 100]\n");
     printf("-d#  Maximum phrase depth (10)\n");
     printf("-o   'optimal' phrase depth (yes)\n");
     printf("-s#  search optimization (2)[0,1,2]\n");
     printf("-b   output binary data (no)\n");
     printf("-v   add Vectrex decoder (yes)\n");
     printf("-c   Vectrex 'C'-compatible asm output (no)\n");
     printf("-f   Pack a 'simple' file (no)\n");
     printf("-r   No RLE Packing (no) (n/a)\n");
     printf("-a   No Shannon coding (no) (n/a)\n");
     printf("\n     in brackets = default\n");
     printf("     -o, -p, -v are 'on' and can only be toggled off\n");
     printf("     -b, -f, -r, -a is 'off' and can only be toggled on\n");
     printf("     if optimal phrase depth is on, maximal phrase depth\n");
     printf("     has no meaning, vice versa.\n");
     printf("\nThis thingy extracts information for the first\n");
     printf(  "11 registers, from a decoded 'ym'-file (not lharc'd).\n");
     printf(  "If you don't know what an ym file is... don't worry, you\n");
     printf(  "don't need this program.\n");
     printf(  "It saves the extracted information to a file. \n");
     printf(  "As an *.asm or additionally as a data file. \n");
     printf(  "The information is packed, using a mixture of \n");
     printf(  "different packing strategies.\n");
     printf(  "You can experiment with different settings, but beware,\n");
     printf(  "packing with high phrase setting... takes time! (and even more time:-))\n");
     printf(  "\nVERSION %s \n", VERSION);
     printf("\nWritten Mai/June 1999 by Christopher Salomon\n");

     return 0;
    }

    if ((pointer[1]=='c')||(pointer[1]=='C'))
    {
     COUT_PUT = 1;
     printf("Vectrex 'C'-compatible asm output on!\n");
    }

    if ((pointer[1]=='f')||(pointer[1]=='F'))
    {
     SINGLE_PACK = 1;
     printf("SINGLE PACK coding switched on!\n");
    }
    if ((pointer[1]=='r')||(pointer[1]=='R'))
    {
     RLE_USED = 0;
     printf("RLE coding switched off!\n");
    }
    if ((pointer[1]=='a')||(pointer[1]=='A'))
    {
     SHANNON = 0;
     printf("Shannon coding switched off!\n");
    }
    if ((pointer[1]=='v')||(pointer[1]=='V'))
    {
     VECTREX_DECODER = 0;
     printf("Vectrex Decoder switched off!\n");
    }
    if ((pointer[1]=='b')||(pointer[1]=='B'))
    {
     BINARY_DATA = 1;
     printf("Binary data switched off!\n");
    }
    if ((pointer[1]=='s')||(pointer[1]=='S'))
    {
     PHRASE_OPTIMIZER = atoi(pointer+2);
     printf("Search optimizing set to:%i \n",PHRASE_OPTIMIZER);
    }
    if ((pointer[1]=='o')||(pointer[1]=='O'))
    {
     OPTIMAL_PHRASE = 0;
     printf("Optimal phrase switched off!\n");
    }
    if ((pointer[1]=='d')||(pointer[1]=='D'))
    {
     PHRASES_MAX_DEPTH  = atoi(pointer+2);
     printf("Max phrase depth set to:%i \n",PHRASES_MAX_DEPTH);
    }
    if ((pointer[1]=='m')||(pointer[1]=='M'))
    {
     MAX_PHRASE_LEN     = atoi(pointer+2)+1;
     if (MAX_PHRASE_LEN>100)
     {
      MAX_PHRASE_LEN = 100;
     }
     printf("Max phrase len set to:%i\n",MAX_PHRASE_LEN);
    }
    if ((pointer[1]=='n')||(pointer[1]=='N'))
    {
     MIN_PHRASE_LEN     = atoi(pointer+2);
     if (MIN_PHRASE_LEN<2)
     {
      MIN_PHRASE_LEN = 2;
     }
     printf("Min phrase len set to:%i\n",MIN_PHRASE_LEN);
    }
    if ((pointer[1]=='p')||(pointer[1]=='P'))
    {
     printf("Phrases not used!\n");
     USE_PHRASE = 0;
    }
   }
   else
   {
    inFile=fopen(argv[i],"rb");
    filenameArg = i;
    if( !inFile )
    {
      printf("A file error occured with %s!\n", argv[i]);
     return 2;
    }
   }
   i++;
  }
 }
 if ((COUT_PUT == 1) || (SINGLE_PACK == 1) || (RLE_USED == 0) || (SHANNON == 0))
 {
  printf("Vectrex Decoder switched off!\n");
  VECTREX_DECODER = 0;
 }

 if( !inFile )
 {
   printf("No input file given...!\n");
  return 3;
 }

 int len=fread( buf, sizeof(char),sizeof(char)*MAXBUFF,inFile );
 fclose(inFile);

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

 char *help = argv[filenameArg];
 help = strrchr(help, '\\');
 if (help == NULL)
  help = argv[filenameArg];
 strcpy(file_name,help);
 strtok(file_name," .\n");

 char name_out_raw[100];
 strcpy(name_out_raw,file_name);
 strtok(name_out_raw," .\n");
 if (COUT_PUT==1)
 {
  strcat(name_out_raw,".s");
 }
 else
 {
  strcat(name_out_raw,".asm");
 }

 //////////////////////////////////////////////////
 //////////////////////////////////////////////////
 //////////////////////////////////////////////////
 if (!SINGLE_PACK)
 {
  for (i=0; i < 11; i++)
  {
   out_buf[i] = (unsigned char *) malloc (sizeof(unsigned char) * (MAXBUFF/11+11));
  }
  printf("Length of file: %i\n",len);
  if (!strncmp("YM3!",(char *)buf,4))
  {
   printf("YM3! format\n");
   vbl_len=convert_ym3(out_buf,buf,len);
  }
  else if (!strncmp("YM3b",(char *)buf,4))
  {
   printf("YM3b format\n");
   vbl_len=convert_ym3b(out_buf,buf,len);
  }
  else  if (!strncmp("YM4!",(char *)buf,4))
  {
   printf("YM4! format\n");
   vbl_len=convert_ym4(out_buf,buf);
  }
  else  if (!strncmp("YM5!",(char *)buf,4))
  {
   printf("YM5! format\n");
   vbl_len=convert_ym5(out_buf,buf);
  }
  else  if (!strncmp("YM6!",(char *)buf,4))
  {
   printf("YM6! format\n");
   printf("I have found no documentation for this format, for now\n");
   printf("YM5! is assumed for this. - works most of the time...\n");
   vbl_len=convert_ym5(out_buf,buf);
  }
  else
  {
   printf("Unkown or unsupported format!\n");
   return 3;
  }

  if (vbl_len == 0)
  {
   printf("Unsupported format!\n");
   return 4;
  }
 }
 else
 {
  out_buf[0] = (unsigned char *) malloc (sizeof(unsigned char) * (MAXBUFF+1));
  unsigned char *o=out_buf[0];
  vbl_len = len;
  for (int i=0; i< vbl_len; i++)
  {
   *o = buf[i];
   o++;
  }
  ENCODE_END = 1;
 }

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

 FILE *file=fopen(name_out_raw,"wb+");
 FILE *file2=NULL;
 if (BINARY_DATA)
 {
  strcpy(name_out_raw,file_name);
  strtok(name_out_raw," .\n");
  strcat(name_out_raw,".bin");
  file2=fopen(name_out_raw,"wb+"); // allways overwrite ;-)
 }
 init_bit_out(file, file2);

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

 abstract_buffer *abuffer;
 int overall_used_bits = 0;
 FILE* outFile = get_dbOutFile();
 FILE* outFile2 = get_binOutFile();

 if (outFile != NULL)
 {
  if (VECTREX_DECODER)
  {
   out_decoder();
  }

  if (COUT_PUT==1)
  {
   if (!SINGLE_PACK)
   {
    fprintf(outFile,"; Two global names will be produced for this song:\n");
    fprintf(outFile,"; \"%s_data\" - you must use the address of this one, like \n", file_name);
    fprintf(outFile,";\n;               ym_init_(&%s_data); \n;\n", file_name);
    fprintf(outFile,"; and:\n");
    fprintf(outFile,"; \"%s_name\" - you must use the address of this one, like \n", file_name);
    fprintf(outFile,";\n;               print_str(-70,0,&%s_name); \n;\n", file_name);
    fprintf(outFile,"; \n");

    fprintf(outFile," .area __code \n", file_name);
    fprintf(outFile,"%s_start: \n", file_name);
    fprintf(outFile," .word %i ; vbl_len \n", vbl_len);
   }
   else
   {
    fprintf(outFile,"; One global name will be produced for this file:\n");
    fprintf(outFile,"; \"%s_data\" - you must use the address of this one, like \n", file_name);
    fprintf(outFile,";\n;               unpack_init(&%s_data); \n;\n", file_name);
    fprintf(outFile,"; \n");

    fprintf(outFile," .area __code \n", file_name);
    fprintf(outFile,"%s_start: \n", file_name);
    fprintf(outFile," .word %i ; len \n", vbl_len);
   }
  }
  else
  {
   fprintf(outFile,"%s_start: \n", file_name);
   fprintf(outFile," DW %i ; vbl_len \n", vbl_len);
  }
 }
 if (outFile2 != NULL)
 {
  fwrite(((unsigned char *)(&vbl_len))+1,1,1,outFile2);
  fwrite(((unsigned char *)(&vbl_len)),1,1,outFile2);
 }

 // pack register
 printf("\nStart packing data...\n");
 int ym_register;
 for (ym_register=ENCODE_START; ym_register< ENCODE_END; ym_register++)
 {
  current_working_register = ym_register;
  abuffer = build_abstract(out_buf[ym_register], vbl_len);
  if (USE_PHRASE != 0)
  {
   if (OPTIMAL_PHRASE == 0)
    abstract_search_insert_phrases(abuffer);
   else
    abstract_search_insert_phrases_optimal(abuffer);
  }
  if (SHANNON)
  {
   abstract_Shannon(abuffer);
  }
  if (RLE_USED)
  {
   abstract_RLE(abuffer);
  }
  int used_bits = get_bits_used_from_abstract_complete(abuffer);
  abstract_out(abuffer);
  printf("Register %i -> Bytes used: %i \n",current_working_register, (used_bits+7)/8);
  overall_used_bits += used_bits;
  delete_abstract(abuffer);
 }
 overall_used_bits += 16 + 68*8; // vbl_len
 printf("Bytes used alltogether (best guess :-)): %i \n", (overall_used_bits+7)/8);

 if (outFile != NULL)
 {
  fprintf(outFile,"; This data does not appear in binary output! \n");

  if (COUT_PUT==1)
  {
   fprintf(outFile," .globl _%s_data \n", file_name);
   fprintf(outFile,"_%s_data: \n", file_name);
   fprintf(outFile," .word %s_start \n", file_name);
  }
  else
  {
   fprintf(outFile,"%s_data: \n", file_name);
   fprintf(outFile," DW %s_start \n", file_name);
  }
  for (ym_register=ENCODE_START; ym_register< ENCODE_END; ym_register++)
  {
   if (COUT_PUT==1)
   {
    /* rem -3 ??? !!! CSA todo */
    fprintf(outFile," .word %s_reg_%i - 3", file_name, ym_register);
   }
   else
   {
    /* rem -3 ??? !!! CSA todo */
    fprintf(outFile," DW %s_reg_%i - 3", file_name, ym_register);
   }
   if (USE_PHRASE==0)
   {
    fprintf(outFile,", 0");
   }
   else
   {
    fprintf(outFile,", %s_pd_%i", file_name, ym_register);
   }
   fprintf(outFile,", %s_reg_%i_data\n", file_name, ym_register);
  }
  if (COUT_PUT==0)
  {
   if (!SINGLE_PACK)
   {
    fprintf(outFile,"SONG_DATA EQU %s_data \n", file_name);
   }
  }
  if (COUT_PUT==1)
  {
   if (!SINGLE_PACK)
   {
    fprintf(outFile," .globl _%s_name \n", file_name);
    fprintf(outFile,"_%s_name: \n .byte ", file_name);
   }
  }
  else
  {
   if (!SINGLE_PACK)
   {
    fprintf(outFile,"%s_name: \n DB ", file_name);
   }
  }
  if (!SINGLE_PACK)
  {
   for (int i = 0; i < strlen(song_name); i++)
   {
    unsigned char c = (unsigned char) toupper(song_name[i]);
    if (c <= 'Z')
    {
     if (COUT_PUT==1)
     {
      fprintf(outFile,"0H%02X, ", c);
     }
     else
     {
      fprintf(outFile,"$%02X, ", c);
     }
    }
   }
   if (COUT_PUT==1)
   {
    fprintf(outFile,"0H80 \n");
   }
   else
   {
    fprintf(outFile,"$80 \n");
   }
// strcpy(song_name, (char *)in_buf+pos);
  }
 }
 deinit_bit_out();

 return 0;
}
