/* 1 bitplane PCX to IMG file convertor, public domain software.
   (c) 1989 TC Developments
*/

#include <osbind.h>
#include <gemdefs.h>
#include <obdefs.h>
#include "convert.h"

#define TRUE 1
#define FALSE 0

unsigned char *data_buffer=-1;
char main_path[80];

char *write_errtext="[0][File write error  ][ Abort ]";

/****************************/
/* Main program starts here */
/****************************/

main()
{
int exit_button;
long tree;

  appl_init();
  if(Getrez()!=2)
  {
    form_alert(1,"[1][This program only|works in high resolution][ Abort ]");
    appl_exit();
    exit(0);
  }
  message("Loading resource file...");
  if(!rsrc_load("convert.rsc"))
  {
    form_alert(1,"[0][Cannot find convert.rsc  ][ Abort ]");
    appl_exit();
    exit(0);
  }
  graf_mouse(POINT_HAND);

  rsrc_gaddr(MAINTREE,R_TREE,&tree);

  do
  {
    message("Make your choices...");
    exit_button=do_dialog(MAINTREE);
    if(exit_button==BINFO) do_dialog(INFOTREE);
    if(exit_button==BCONVERT) do_convert(tree);
  } while(exit_button!=BCANCEL);

  if( (long) data_buffer!=-1L) Mfree(data_buffer);
  rsrc_free();
  appl_exit();
  return(0);    /* GEM likes a zero to be returned for no error */
}

/**********************/
/* Dialog box handler */
/**********************/

do_dialog(index)
int index;
{
int x,y,w,h,exit_button;
long tree_addr;

  rsrc_gaddr(R_TREE,index,&tree_addr);
  form_center(tree_addr,&x,&y,&w,&h);
  form_dial(FMD_START,0,0,0,0,x,y,w,h);
  form_dial(FMD_GROW,0,0,0,0,x,y,w,h);
  objc_draw(tree_addr,ROOT,MAX_DEPTH,x,y,w,h);
  exit_button=form_do(tree_addr,0);
  reset_exits(tree_addr);
  form_dial(FMD_SHRINK,0,0,0,0,x,y,w,h);
  form_dial(FMD_FINISH,0,0,0,0,x,y,w,h);
  return(exit_button);
}

/************************/
/* The convertor itself */
/************************/

do_convert(tree)
OBJECT tree[];
{
int rotation=0,compression,inverted=FALSE;
char filename[15],pathname[60],buffer[80];
long new_length;
register long i;

  strcpy(filename,"@2345678.000");
  strcpy(pathname,"\\*.PCX");

  if((tree[INVERT].ob_state) & SELECTED) inverted=TRUE;
  if((tree[ROT90].ob_state) & SELECTED) rotation=90;
  if((tree[ROT180].ob_state) & SELECTED) rotation=180;
  if((tree[ROT270].ob_state) & SELECTED) rotation=270;

  message("Select name of source .PCX file...");
  if(!get_filename(pathname,filename)) return(FALSE);
  if(!scan_file(&new_length,&compression)) return(FALSE);
  if(compression==TRUE)
  {
    if((data_buffer=(unsigned char *) Malloc(new_length))==0)
    {
      sprintf(buffer,"I need %ld bytes of free memory\0",new_length);
      message(buffer);
      form_alert(1,"[0][Insufficient memory  ][ Abort ]");
      (long) data_buffer=-1L;
      return(FALSE);
    }
    if(!expand_file(new_length)) return(FALSE);
  }
  if(inverted) for(i=128;i<new_length;i++) data_buffer[i]^=0xff;

  if(!do_outfile(new_length-128L,rotation)) return(FALSE);
  return(TRUE);
}

/******************/
/* Output handler */
/******************/

do_outfile(length,rotation)
long length;
int rotation;
{
int handle;
char pathname[60],filename[15];

  strcpy(pathname,"\\*.IMG");
  strcpy(filename,"@2345678.123");
  message("Select name of output .IMG file");  
  if(!get_filename(pathname,filename)) return(FALSE);

  if((handle=Fcreate(main_path,0))<0)
  {
    form_alert("[File creation error  ][ Abort ]");
    Mfree(data_buffer);
    (long) data_buffer=-1L;
    return(FALSE);
  }
  switch(rotation)
  {
    case 0:
      message("Converting with no rotation...");
      write_0deg(handle,length);
      break;
    case 90:
      message("Converting with 90\xf8 rotation");
      write_90deg(handle);
      break;
    case 180:
      message("Converting with 180\xf8 rotation");
      write_180deg(handle);
      break;
    case 270:
      message("Converting with 270\xf8 rotation");
      write_270deg(handle);
      break;
  }
  Fclose(handle);
  Mfree(data_buffer);
  (long) data_buffer=-1L;
  form_alert(1,"[0][Conversion complete  ][ Okay ]");
  return(TRUE);
}

/****************************/
/* Convert with no rotation */
/****************************/

write_0deg(handle,length)
int handle;
long length;
{
unsigned char subheader[2];
unsigned char *pointer;
int bytesperline=get8086(data_buffer+66);
int header[16],numscans,i;

  subheader[0]=0x80;
  header[0]=1; header[1]=8; header[2]=1; header[3]=2;
  header[4]=0x174; header[5]=0x174;
  header[6]=get8086(data_buffer+8)+1;
  header[7]=get8086(data_buffer+10)+1;
  if(Fwrite(handle,16L,header)<0)
  {
    form_alert(1,write_errtext);
    return(FALSE);
  }
  pointer=data_buffer+128L;

  for(numscans=length/bytesperline;numscans;numscans--)
  {
    i=bytesperline;
    while(i)
    {
      if(i>255) subheader[1]=255;
      else subheader[1]=i;
 
      if(Fwrite(handle,2L,subheader)<0)
      {
        form_alert(1,write_errtext);
        return(FALSE);
      }
      if(Fwrite(handle,(long) subheader[1],pointer)<0)
      {
        form_alert(1,write_errtext);
        return(FALSE);
      }
      i-=(int) subheader[1];
      (long) pointer+=(long) subheader[1];
    }
  }
  return(TRUE);
}

/***********************************/
/* Writes a 90 degree rotated file */
/***********************************/

write_90deg(handle)
int handle;
{
unsigned char subheader[2];
int bytesperline=get8086(data_buffer+66);
int header[16],i,j;

  subheader[0]=0x80;
  header[0]=1; header[1]=8; header[2]=1; header[3]=2;
  header[4]=0x174; header[5]=0x174;
  header[6]=get8086(data_buffer+10)+1;
  header[7]=get8086(data_buffer+8)+1;
  if(Fwrite(handle,16L,header)<0)
  {
    form_alert(1,write_errtext);
    return(FALSE);
  }
  for(j=0;j<bytesperline;j++)
  {
    for(i=7;i>=0;i--)
    {
      if(!write_90scan(handle,data_buffer+128L+j,i)) return(FALSE);
    }
  }
}

/************************************/
/* Writes a 180 degree rotated file */
/************************************/

write_180deg(handle)
int handle;
{
unsigned char subheader[2];
unsigned char *pointer;
int bytesperline=get8086(data_buffer+66);
int header[16],i;

  subheader[0]=0x80;
  header[0]=1; header[1]=8; header[2]=1; header[3]=2;
  header[4]=0x174; header[5]=0x174;
  header[6]=get8086(data_buffer+8)+1;
  header[7]=get8086(data_buffer+10)+1;
  if(Fwrite(handle,16L,header)<0)
  {
    form_alert(1,write_errtext);
    return(FALSE);
  }
  pointer=data_buffer+128L+( (long)get8086(data_buffer+10)*(long)bytesperline) ;
  for(i=0;i<=get8086(data_buffer+10);i++)
  {
    if(!write_180scan(handle,pointer)) return(FALSE);
    (long) pointer-=(long) bytesperline;
  }
  return(TRUE);
}

/********************************/
/* Write a 180 degree scan line */
/********************************/

write_180scan(handle,address)
int handle;
unsigned char *address;
{
int bytesperline=get8086(data_buffer+66);
int i,j,buffpos;
unsigned char buffer[260];
unsigned char byte,byte1;

  address+=bytesperline-1L;
  j=bytesperline;

  while(j)
  {
    buffpos=0;
    if(j>255) i=255;
    else i=j;

    buffer[buffpos++]=0x80;
    buffer[buffpos++]=(unsigned char) i;
    j-=i;
    for(;i;i--)
    {
      byte1=0;
      byte=*address--;
      byte1|=(byte & 1) << 7;
      byte1|=(byte & 2) << 5;
      byte1|=(byte & 4) << 3;
      byte1|=(byte & 8) << 1;
      byte1|=(byte & 16) >> 1;
      byte1|=(byte & 32) >> 3;
      byte1|=(byte & 64) >> 5;
      byte1|=(byte & 128) >> 7;
      buffer[buffpos++]=byte1;
    }
    if(Fwrite(handle,(long) (buffer[1]+2),buffer)<0)
    {
      form_alert(1,write_errtext);
      Mfree(data_buffer);
      (long) data_buffer=-1L;
      return(FALSE);
    }
  }
  return(TRUE);
}


/************************************/
/* Writes a 270 degree rotated file */
/************************************/

write_270deg(handle)
int handle;
{
unsigned char subheader[2];
int bytesperline=get8086(data_buffer+66);
int header[16],i;

  subheader[0]=0x80;
  header[0]=1; header[1]=8; header[2]=1; header[3]=2;
  header[4]=0x174; header[5]=0x174;
  header[6]=get8086(data_buffer+10)+1;
  header[7]=get8086(data_buffer+8)+1;
  if(Fwrite(handle,16L,header)<0)
  {
    form_alert(1,write_errtext);
    return(FALSE);
  }
  for(;bytesperline;bytesperline--)
  {
    for(i=0;i<8;i++)
    {
      if(!write_270scan(handle,data_buffer+127L+bytesperline,i)) return(FALSE);
    }
  }
}

/*******************************/
/* 270 degree scan line writer */
/*******************************/

write_270scan(handle,address,bitpos)
int handle,bitpos;
unsigned char *address;
{
unsigned int byte;
long i,j;
unsigned int mask;
unsigned char buffer[260];
int buffpos,bytesperscan=get8086(data_buffer+66);

  j=(long) get8086(data_buffer+10)+1;
  if(j==(8*(j/8))) j=j/8;
  else j=(j/8)+1;

  mask=1;
  if(bitpos) mask=mask << bitpos;

  while(j)
  {
    buffpos=0;
    if(j>255) i=255;
    else i=j;

    buffer[buffpos++]=0x80;
    buffer[buffpos++]=(unsigned char) i;
    j-=i;
    for(;i;i--)
    {
      byte=0;
      byte|=(*(address+(bytesperscan*7)) & mask);
      byte|=(*(address+(bytesperscan*6)) & mask) << 1;
      byte|=(*(address+(bytesperscan*5)) & mask) << 2;
      byte|=(*(address+(bytesperscan*4)) & mask) << 3;
      byte|=(*(address+(bytesperscan*3)) & mask) << 4;
      byte|=(*(address+(bytesperscan*2)) & mask) << 5;
      byte|=(*(address+(bytesperscan)) & mask) << 6;
      byte|=((*address) & mask) << 7;
      if(bitpos) byte=byte >> bitpos;
      buffer[buffpos++]=(unsigned char) byte;
      (long) address+=(long) (bytesperscan*8);
    }
    if(Fwrite(handle,(long) (buffer[1]+2),buffer)<0)
    {
      form_alert(1,write_errtext);
      Mfree(data_buffer);
      (long) data_buffer=-1L;
      return(FALSE);
    }
  }
  return(TRUE);
}

/******************************/
/* 90 degree scan line writer */
/******************************/

write_90scan(handle,address,bitpos)
int handle,bitpos;
unsigned char *address;
{
unsigned int byte;
long i,j;
unsigned int mask;
unsigned char buffer[260];
int buffpos,bytesperscan=get8086(data_buffer+66);

  j=(long) get8086(data_buffer+10)+1;
  if(j==(8*(j/8))) j=j/8;
  else j=(j/8)+1;

  mask=1;
  if(bitpos) mask=mask << bitpos;

  address+=(long) bytesperscan* (long) get8086(data_buffer+10);
  while(j)
  {
    buffpos=0;
    if(j>255) i=255;
    else i=j;

    buffer[buffpos++]=0x80;
    buffer[buffpos++]=(unsigned char) i;
    j-=i;
    for(;i;i--)
    {
      byte=0;
      byte|=(*(address+(bytesperscan*7)) & mask) << 7;
      byte|=(*(address+(bytesperscan*6)) & mask) << 6;
      byte|=(*(address+(bytesperscan*5)) & mask) << 5;
      byte|=(*(address+(bytesperscan*4)) & mask) << 4;
      byte|=(*(address+(bytesperscan*3)) & mask) << 3;
      byte|=(*(address+(bytesperscan*2)) & mask) << 2;
      byte|=(*(address+(bytesperscan)) & mask) << 1;
      byte|=((*address) & mask);
      if(bitpos) byte=byte >> bitpos;
      buffer[buffpos++]=(unsigned char) byte;
      (long) address-=(long) (bytesperscan*8);
    }
    if(Fwrite(handle,(long) (buffer[1]+2),buffer)<0)
    {
      form_alert(1,write_errtext);
      Mfree(data_buffer);
      (long) data_buffer=-1L;
      return(FALSE);
    }
  }
  return(TRUE);
}

/************************/
/* Expands the PCX file */
/************************/

expand_file(new_length)
long new_length;
{
int handle,i;
long length;
unsigned char *pointer;
unsigned char *pointer2;
unsigned char counter;

  message("Re-reading file...");
  if(Fsfirst(main_path,0)<0)
  {
    form_alert(1,"[0][File not found  ][ Abort ]");
    return(FALSE);
  }
  length=*( (long *) (Fgetdta()+26));
  if((handle=Fopen(main_path,0))<0)
  {
    form_alert(1,"[0][File open error  ][ Abort ]");
    return(FALSE);
  }
  if(Fread(handle,length,data_buffer+new_length-length)<0)
  {
    form_alert(1,"[0][Read error  ][ Abort ]");
    Mfree(data_buffer);
    (long) data_buffer=-1L;
    return(FALSE);
  }
  Fclose(handle);

  message("Now decompressing data...");
  (long) pointer=(long) data_buffer+new_length-length;
  pointer2=data_buffer;
  for(i=0;i<128;i++) *pointer2++=*pointer++;

  for(;pointer<data_buffer+new_length;pointer++)
  { 
    if(((*pointer) & 0xc0)==0xc0)
    {
      counter=((*pointer) & 0x3f);
      for(;counter;counter--) *pointer2++=pointer[1];
      pointer++;
    }
    else *pointer2++=*pointer;
  }
  return(TRUE);
}

/***************************/
/* Loads & scans .PCX file */
/***************************/

scan_file(new_length,compression)
long *new_length;
int *compression;
{
long length;
char buffer[80];
register long count;
unsigned char *pointer;

  if( (long) data_buffer!=-1L) Mfree(data_buffer);

  sprintf(buffer,"Searching for %s...\0",main_path);
  message(buffer);
  if(Fsfirst(main_path,0)<0)
  {
    form_alert(1,"[0][File not found  ][ Abort ]");
    return(FALSE);
  }
  message("Reserving memory...");
  length=*( (long *) (Fgetdta()+26));
  if((data_buffer=(unsigned char *) Malloc(length))==0)
  {
    sprintf(buffer,"I need %ld bytes of free memory\0",length);
    message(buffer);
    form_alert(1,"[0][Insufficient memory  ][ Abort ]");
    (long) data_buffer=-1;
    return(FALSE);
  }
  if(!load_file(length)) return(FALSE);

  message("Analysing, please wait");
  if(data_buffer[2]!=TRUE)
  {
    *compression=FALSE;
    *new_length=length;
    return(TRUE);
  }
  *compression=TRUE;

  count=128;    /* Take the header into account */
  for(pointer=data_buffer+128;pointer<data_buffer+length;pointer++)
  {
    if(((*pointer) & 0xc0)==0xc0)
    {
      count+=(*pointer) & 0x3f;
      pointer++;      /* step over repetition byte */
    }
    else count++;
  }
  if(data_buffer[65]!=1)
  {
    sprintf(buffer,"%s has %d bitplanes",main_path,(int) data_buffer[65]);
    message(buffer);
    form_alert(1,"[0][.PCX file must have|only ONE bitplane  ][ Abort ]");
  }
  *new_length=count;
  Mfree(data_buffer);
  (long) data_buffer=-1L;
  return(TRUE);
}

/***************/
/* File loader */
/***************/

load_file(length) 
long length;
{
int handle;
char buffer[80];

  message("Opening file for reading...");
  if((handle=Fopen(main_path,0))<0)
  {
    form_alert(1,"[0][File open error  ][ Abort ]");
    return(FALSE);
  } 
  sprintf(buffer,"Reading %ld bytes to buffer...\0",length);
  message(buffer);
  if(Fread(handle,length,data_buffer)<0)
  {
    form_alert(1,"[0][File read error  ][ Abort ]");
    return(FALSE);
  }
  Fclose(handle);
  return(TRUE);
}

/********************/
/* filename handler */
/********************/

get_filename(path,name)
char *path,*name;
{
int e_button;
register int i,j=0;

  fsel_input(path,name,&e_button);
  for(i=0;i<strlen(path);i++) main_path[j++]=path[i];
  for(;main_path[j]!='\\';j--);
  j++;
  for(i=0;i<(strlen(name)+1);i++) main_path[j++]=name[i];
  return(e_button);
}

/************************/
/* exit button resetter */
/************************/

reset_exits(tree)
OBJECT tree[];
{
register int i=0;

  do
  {
    if((tree[i].ob_flags) & EXIT) 
        tree[i].ob_state=(tree[i].ob_state) & ~SELECTED;
  } while(!((tree[i++].ob_flags) & LASTOB));
}

/****************************/
/* Top line message printer */
/****************************/

message(string)
char *string;
{
char clearline[80];
char cursor[5];

  cursor[0]=0x1b; cursor[1]='Y'; cursor[2]=32; cursor[3]=32; cursor[4]=0;
  Cconws(cursor);
  memset(clearline,(int) ' ',80);
  Cconws(clearline);
  cursor[3]=32+((80-strlen(string))/2);
  Cconws(cursor);
  Cconws(string);
}

/******************************/
/* gets an 8086 style integer */
/******************************/

get8086(addr)
unsigned char *addr;
{
  return( (int) ((addr[1]*256)+addr[0]) );
}
