/* PLSTsearch v1.1 (c) Ola Ekström 1999. A search tool for plst-files,
   a.k.a. Preset-lists, produced by Protracker and its close relatives. */

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

#define MAX_LINES 22  /* the number of lines to be displayed at a time */ 
#define DASHED_LINE_LEN 51
#define SAMPLE_NAME_MAX_LEN 16

#ifndef TRUE
   #define TRUE 1
#endif
#ifndef FALSE
   #define FALSE 0
#endif

char usemez[] = "Usage: plstsearch <plst-file> [string]";

struct plst_type {
   char disk_name[6];
   char sample_name[16];
   unsigned short size;
   signed char finetune;
   unsigned char volume;
   unsigned short repeat;
   unsigned short replen;
};

int strexam(char *buffp, char *sstr);
void strtolwr(char *strptr);

int main(int argc, char *argv[])
{
   FILE *fp;
   unsigned char searchstr[18], ch = '\0';
   unsigned int i, j, hit=0, rcount=3;  /* <- to compensate for titletext */
   unsigned long int plst_size;
   struct plst_type *plst;

   puts("\n[1;31mPLSTsearch v1.1[0m by Ola Ekström (c) 1999.\n");

   if(argc<2 || argc>3) {
      puts(usemez);
      exit(1);
   }

   if(!strcmp(argv[1], "?")) {
      puts(usemez);
      exit(1);
   }

   if(argc==3) {
      if(strlen(argv[2])>SAMPLE_NAME_MAX_LEN) {
         puts("Searchstring to long. Max 16 chars alowed.");
         exit(1);
      }
      strcpy(searchstr, argv[2]);
      /* convert the searchstring to lowercase characters */
      strtolwr(searchstr);
   }

   if((fp = fopen(argv[1], "rb"))==NULL) {	/* open plst */
      puts("Error opening plst.");
      exit(1);
   }
   fseek(fp, 0L, SEEK_END);	/* find out size of plst */
   plst_size = ftell(fp);
   rewind(fp);

   printf("Entries: %u, Size in bytes: %u\n", plst_size/30, plst_size);
   /* plst-size should be evenly divided by 30, otherwise issue a warning */
   if(!plst_size || (plst_size%30) != 0) {
      puts("Warning: plst has unknown structure, result may be corrupt.");
      rcount++;
   }

   plst = (struct plst_type *)malloc(plst_size);
   if(plst==NULL) {
      puts("Cannot allocate memory.");
      exit(1);
   }
   fread(plst, sizeof(char), plst_size, fp);
   if(ferror(fp)) {
      puts("Error reading plst.");
      exit(1);
   }
   fclose(fp);

   if(argc==2) {
      puts("[1;31mNo searchstring specified, listing all entries.[0;31m");
      rcount++;
   }

   for(i=0; i<DASHED_LINE_LEN; i++) putchar('-'); /* print a row of dashes */
   puts("\n[0;33mDisk  [0;31m| Samplename      |  [0;32mSize[0;31m|"
        "[0;33mTune[0;31m|Repeat[0;31m|[0;32mReplen[0;31m");
   for(i=0; i<DASHED_LINE_LEN; i++) putchar('-');
   putchar('\n');

   for(i=0; i<plst_size/30; i++) {
      /* compare sample_name in each entry with searchstring */
      if(argc==2 || strexam(plst[i].sample_name, searchstr)) {
         if(!hit) hit = 1;
         /* display the data for the matching entry */
         printf("[0;33m");
         for(j=0; j<6; j++) {
            if(!plst[i].disk_name[j]) putchar(' ');
            else putchar(plst[i].disk_name[j]);
         }
         printf("  [0;31m");
         for(j=0; j<16; j++) {
            if(!plst[i].sample_name[j]) putchar(' ');
            else putchar(plst[i].sample_name[j]);
         }
         /* Finetune is a signed 4-bit value, but since the version of
            dice that I'm using does not support bit-fileds I'm forced
            to the rather complicated statement below. Size, repeat and
            replen is stored as size/offset in words so they are multiplied
            by 2 to get their size/offset in bytes. */
         printf(" [0;32m%6u [0;33m%4d [0;31m%6u [0;32m%6u\n",
            plst[i].size<<1, plst[i].finetune&8 ? -8+(plst[i].finetune-8) : plst[i].finetune, 
            plst[i].repeat<<1, plst[i].replen<<1);
         rcount++;
         if(rcount==MAX_LINES) {
            rcount = 0;
            printf("[0;32m[Press Return] ");
            fflush(stdout);
            while(ch!='\n') ch = getchar();
            ch = '\0';
            putchar('\v');
         }
      }
   }
   if(!hit) puts("No matching entries found.\n");
   else puts("[0m\v\n");

   return 0;
}

/* checks samplename for searchstring */
int strexam(char *buffp, char *sstr)
{
   int i, j, bufflen = 0, searchlen, hit;

   for(i=0; i<SAMPLE_NAME_MAX_LEN; i++) { /* get length of sample name */
      if(buffp[i]) bufflen++;
      else break;
   }
   searchlen = strlen(sstr);

   /* no reason to continue if the searchstring is longer than the
dir
      samplename */
   if(searchlen > bufflen) return FALSE;

   /* search the samplename */
   for(i=0; i <= bufflen - searchlen; i++) {
      for(j=0; j<searchlen; j++) { 
         if(buffp[j] == sstr[j]) hit = 1;
         else {
            hit = 0;
            break;
         }
      }
      if(hit) return TRUE;
      else buffp++;
   }

   return FALSE;
}

/* makes a string to lowercase characters */
void strtolwr(char *strptr)
{
   while(*strptr) {
      *strptr = tolower(*strptr);
      strptr++;
   }

}
