/* hpcdtoppm (Hadmut's pcdtoppm) v0.6
*  Copyright (c) 1992, 1993, 1994 by Hadmut Danisch (danisch@ira.uka.de).
*  Permission to use and distribute this software and its
*  documentation for noncommercial use and without fee is hereby granted,
*  provided that the above copyright notice appear in all copies and that
*  both that copyright notice and this permission notice appear in
*  supporting documentation. It is not allowed to sell this software in 
*  any way. This software is not public domain.
*/

/* Main part of PhotoCDAGA */


/* NOTE: Don't use this file for compiling hpcdtoppm, it is heavily */
/* modified.  (By me, Günther Röhrich)                              */
/* EMAIL: Guenther@studbox.uni-stuttgart.de  */


#include "hpcdtoppm.h"
#include <signal.h>
#include <stdlib.h>

char  *ver = "\0$VER: photoCDAGA 1.0 (20.11.94)";

uBYTE sbuffer[SECSIZE];

enum TURNS  turn     = T_UNSPEC;
enum TURNS  contori  = T_UNSPEC;
enum SIZES  size     = S_UNSPEC;
enum OUTFOR outfor   = O_UNSPEC;
enum CORR   corrmode = C_UNSPEC;

char *pcdname=NULL;
char *ppmname=NULL;
char *MapFileName;
char *BaseName = NULL;
static FILE  *fin=NULL,*fout=NULL;
static char    *suba1=0,*suba2=0;
static implane Luma, Chroma1,Chroma2;
static implane *PLuma,*PChroma1,*PChroma2;
sINT monochrome=0,do_overskip=0,bufpos=0,print_pos=0,emulate_seek=0,do_melde=0,do_crop=0,do_sharp=0,do_rep=0;
sINT flhori=0,flvert=0,do_info=0;

extern int VGAenable;

#define PrintPos(x) {if(print_pos) fprintf(stderr,"File-Offset: %8d = %8x (hex) = %d (sec)\n",(x),(x),(x)/0x800);}


static void sizecontrol(sizeinfo *,dim,dim,dim sMASK);
static void f_1 (dim,dim,sINT,sINT);
static void f_3 (dim,dim,sINT);
static void openoutput(void);
static void checkin(void);

void close_all(void)
{
  if(fin) fclose(fin);

  if(fout) fclose(fout);
}

#ifdef __GNUC__
/* this is called when CTRL-C occurs */
void GCCAbortHandler(void)
{
  printf("Interrupted by user.\n");
  exit(10);
}
#endif

void main(int argc, char **argv)
{
 int i,start=2;

 #ifdef __GNUC__            /* Modify GNU C CTRL-C handling */
 signal(SIGINT, GCCAbortHandler);
 #endif
 printf("PhotoCDAGA 1.0 (c) by Hadmut Danisch, Günther Röhrich\n");

 if(argc < 2) error(E_ARG);

 MapFileName = malloc(strlen(argv[1])+5);
 if(MapFileName == NULL) error(E_MEM);
 strcpy(MapFileName, argv[1]); /* create a copy of the file name */

 pcdname=argv[1];

 if(argc > 2)
 {
   if(argv[2][0] != '-')
   {
     ppmname = argv[2];
     start = 3;
   }
 }

 /* default settings */
 outfor = O_DCOLOR; 
 corrmode = C_LINEAR;
 turn = T_AUTO;
 size = S_Base;

 for(i=start; i<argc; i++)
 {
 if(!strncmp(argv[i], "-x", 2)) do_overskip=1;

 else if(!strncmp(argv[i], "-s", 2)) do_sharp=1;

 else if(!strncmp(argv[i], "-c", 2)) do_crop=1; 
 
 else if(!strncmp(argv[i], "-n", 2)) turn=T_NONE;

 else if(!strncmp(argv[i], "-r", 2)) turn=T_RIGHT;

 else if(!strncmp(argv[i], "-l", 2)) turn=T_LEFT;

 else if(!strncmp(argv[i], "-h", 2)) turn=T_HEAD;

 else if(!strncmp(argv[i], "-m", 2)) do_melde = 1;

 else if(!strncmp(argv[i], "-c-" ,3)) corrmode=C_DARK;

 else if(!strncmp(argv[i], "-c+" ,3)) corrmode=C_BRIGHT;

 else if(!strncmp(argv[i], "-ppm",3)) outfor=O_PPM;

 else if(!strncmp(argv[i], "-pgm" ,3)) outfor=O_PGM;

 else if(!strncmp(argv[i], "-gray", 5)) outfor=O_DGRAY;

 else if(!strncmp(argv[i], "-vga", 4)) VGAenable = 1;

 else if(!strncmp(argv[i], "-1", 2)) size = S_Base16;

 else if(!strncmp(argv[i], "-2", 2)) size = S_Base4;

 else if(!strncmp(argv[i], "-3", 2)) size = S_Base;

 else if(!strncmp(argv[i], "-b", 2))
 {
   if(argc == i+1) error(E_ARG); /* last argument */
   BaseName = argv[i+1];
   i++;
 }

 else error(E_ARG);
 }



 fin=fopen(pcdname, "r");
 if(fin == NULL)
 {
   printf("Could not open PhotoCD file.\n");
   exit(10);
 }
 

 monochrome = (outfor==O_PGM) || (outfor==O_DGRAY);

 if(monochrome && do_overskip) error(E_ARG);

 if(start == 3 && (outfor != O_PGM && outfor != O_PPM)) error(E_ARG);

 if(start == 2 && (outfor == O_PGM || outfor == O_PPM)) error(E_ARG);

 checkin();

 PLuma = &Luma;
 PChroma1= monochrome ? 0 : &Chroma1; 
 PChroma2= monochrome ? 0 : &Chroma2; 

 switch(size)
 {
   case S_Base16: f_1(BaseW/4,BaseH/4,L_Head,(L_Head+L_Base16));
                  break;

   case S_Base4:  f_1(BaseW/2,BaseH/2,(L_Head+L_Base16),(L_Head+L_Base16+L_Base4));
                  break;

   case S_Base:   f_3(BaseW,BaseH,(L_Head+L_Base16+L_Base4));
                  break;

   default: error(E_INTERN);
 }

   close_all();


 exit(0);
}


static void f_1(dim w,dim h,sINT normal,sINT overskip)
{
 sizeinfo si;
 
 sizecontrol(&si,w,h,~3);

 planealloc(PLuma   ,si.rdhlen,si.rdvlen);
 if (!monochrome) planealloc(PChroma1,si.rdhlen,si.rdvlen);
 if (!monochrome) planealloc(PChroma2,si.rdhlen,si.rdvlen);

 PrintPos(normal*SECSIZE);
 SEEK(normal+1);                   
      
 if(!do_overskip)
 {
   error(readplain(&si,1,PLuma,PChroma1,PChroma2));
   if (!monochrome) 
   {
     interpolate(PChroma1);
     interpolate(PChroma2);
   }
 }
 else
 {
    error(readplain(&si,1,PLuma,nullplane,nullplane));
    SEEK(overskip+1);
    error(readplain(&si,2,nullplane,PChroma1,PChroma2));
 }

 colconvert(&si,PLuma,PChroma1,PChroma2);
 /* Now Luma holds red, Chroma1 hold green, Chroma2 holds blue */

 if(outfor == O_PPM || outfor == O_PGM) openoutput();

 writepicture(fout,&si,PLuma,PChroma1,PChroma2,turn);
}


static void f_3(dim w,dim h,sINT normal)
 {sINT cd_offset,cd_offhelp;
  sizeinfo si;

  sizecontrol(&si,w,h,~3);

  PrintPos(normal*SECSIZE);
  SEEK(normal+1);

  if(!do_overskip)
    {                 planealloc(PLuma   ,si.rdhlen,si.rdvlen);
     if (!monochrome) planealloc(PChroma1,si.rdhlen,si.rdvlen);
     if (!monochrome) planealloc(PChroma2,si.rdhlen,si.rdvlen);

     error(readplain(&si,1,PLuma,PChroma1,PChroma2));
      if (!monochrome) 
        {interpolate(PChroma1);
         interpolate(PChroma2);
        }
    }
   else
    {planealloc(PLuma   ,  si.rdhlen,  si.rdvlen);
     planealloc(PChroma1,2*si.rdhlen,2*si.rdvlen);
     planealloc(PChroma2,2*si.rdhlen,2*si.rdvlen);

     error(readplain(&si,1,PLuma,PChroma1,PChroma2));
     interpolate(PChroma1);
     interpolate(PChroma2);
     interpolate(PChroma1);
     interpolate(PChroma2);

     cd_offset=Skip4Base();
     SEEK(cd_offset+10);          EREADBUF;    cd_offhelp=(((uINT)sbuffer[2])<<8)|sbuffer[3];
     SEEK(cd_offset+12);          readhqt(3);
     SEEK(cd_offset+cd_offhelp);  decode(&si,4,nullplane,PChroma1,PChroma2,1);

     halve(PChroma1);
     halve(PChroma2);
    }
  colconvert(&si,PLuma,PChroma1,PChroma2);
  /* Now Luma holds red, Chroma1 hold green, Chroma2 holds blue */

  if(outfor == O_PPM || outfor == O_PGM) openoutput();
  writepicture(fout,&si,PLuma,PChroma1,PChroma2,turn);


 }

#define ISDIGIT(x) (((x)>='0') && ((x)<='9'))

static void number(char **s,char **i,char **f)
 {char *p;

  p= *s;
  (*i)=(*f)=0;

  if(!ISDIGIT(*p)) error(E_SUBR);
  while(ISDIGIT(*p)) p++;
  if(*p != '.') 
   { *i=*s;
     *s=p; 
     return;
   }
  p++;
  if(!ISDIGIT(*p)) error(E_SUBR);
  while(ISDIGIT(*p)) p++;
  *f=*s;
  *s=p;  
 }

static sdim makedim(sdim full,char *i,char *f)
 {sdim val;
  FLTPT fl;

  if(i) 
   {if(f) error(E_INTERN);
    if(sscanf(i,"%u",&val) != 1) error(E_SUBR);
    if((val<0) || (val >full)) error(E_SUBR);
    return val;
   }
  else
   {if(!f) error(E_INTERN);
    if(sscanf(f,SSFLTPT,&fl) != 1) error(E_SUBR);
    if((fl < 0.0) || (fl > 1.0)) error(E_SUBR);
    val= full * fl + 0.5;
    return val;
   }
 }



static void sizealign(char *str,dim full,
                      dim *rdoff,dim *rdlen,dim *imoff,dim *imlen,dim sMASK)
 {char *i1,*f1,*tr,*i2,*f2,*ptr;
  int vonbis=0;
  sdim von,len,rest;

  i1=f1=tr=i2=f2=0;

  ptr=str;
  number(&ptr,&i1,&f1);

  if(*ptr == '-') vonbis=1;
  else if (*ptr == '+') vonbis=0;
  else error(E_SUBR);
  ptr++;

  number(&ptr,&i2,&f2);
  if(*ptr) error(E_SUBR);

  von=makedim(full,i1,f1);
  len=makedim(full,i2,f2);
  if(vonbis) len-=von;
  rest=full-von-len;

  if((von<0) || (len<1) || (rest<0)) error(E_SUBR);
  
  *imlen = (dim) len;

  *rdoff = (dim) (von & sMASK); 
  *rdlen = full - *rdoff - ((dim)( rest & sMASK) );

  *imoff = ((dim) von) - *rdoff;

 }


static void sizecontrol(sizeinfo *si,dim w,dim h,dim sMASK)
{
  si->w=w;
  si->h=h;

  if(!suba1)
  {
    si->rdhlen=w;
    si->rdvlen=h;
    si->rdhoff=0;
    si->rdvoff=0;
    si->imhlen=0;
    si->imvlen=0;
    si->imhoff=0;
    si->imvoff=0;
  }
  else
  {
    sizealign(suba1,w,&si->rdhoff,&si->rdhlen,&si->imhoff,&si->imhlen,sMASK);
    sizealign(suba2,h,&si->rdvoff,&si->rdvlen,&si->imvoff,&si->imvlen,sMASK);
  }

#ifdef DEBUG
  fprintf(stderr,"Align: %5d %5d \n",si->w,si->h);
  fprintf(stderr,"Align: %5d %5d %5d %5d \n",si->rdhoff,si->rdhlen,si->rdvoff,si->rdvlen);
  fprintf(stderr,"Align: %5d %5d %5d %5d \n",si->imhoff,si->imhlen,si->imvoff,si->imvlen);
#endif

}
 


static void checkin(void)
 { 
   if (do_info || (turn==T_AUTO)) 
     { SEEK(1);
       EREADBUF;
     }

    if(turn==T_AUTO) 
     {
      switch(sbuffer[0xe02 & 0x7ff]&0x03)
       {case 0x00: turn=T_NONE;  break;
        case 0x01: turn=T_LEFT;  break;
        case 0x02: turn=T_HEAD;  break;
        case 0x03: turn=T_RIGHT; break;
        default: error(E_TCANT);
       }
      }

    if(do_info) druckeid();

 }



/************************** file access functions **************/

int READ(uBYTE *ptr,int n)
 {int d;
  if(!n) return 1;
  bufpos+=n;
  for(;;)
   {d=fread((char *)ptr,1,n,fin);
    if(d<1) return 0;
    n-=d;
    if (!n) break;
    ptr+=d;
   }
  return 1;
 }

static int friss(int n)
 {int d;

  while(n>0)
   {
    d= n>sizeof(sbuffer) ? sizeof(sbuffer) : n;
    n-=d;
    if(READ(sbuffer,d) !=1) return 1;
   }

  return 0;
 }


static void openoutput(void)
{
 if(ppmname)
 {
   fout=fopen(ppmname, "w");
   if(fout == NULL)
   {
     printf("Could not open output file.\n");
     exit(10L);
   }
 }
 else error(E_INTERN);
}


void SEEK(int x)
 {
  x *= SECSIZE;
  if(x<bufpos) error(E_INTERN);
  if(x==bufpos) return;

  if(emulate_seek)
   {if(friss(x-bufpos)) error(E_READ);
    if(x!=bufpos) error(E_INTERN);
   }
  else
   {bufpos=x;
    if (fseek(fin,x,0)) error(E_READ);
   }
#ifdef DEBUG
  fprintf(stderr,"S-Position %x\n",bufpos);
#endif

 }

int SKIPn(int n)
 {
  if(!n) return 0;
  if(n<0) error(E_INTERN);
    
  if(emulate_seek)
   {return friss(n);
   }
  else
   {bufpos+=n;
    return fseek(fin,(n),1);
   }
 }

