/*
 * A2I.C -- Program to convert Apple// Hi-Res Screen Dumps to
 *          320x200x16 .PCX format IBM graphics files.
 *
 *          Program assumes that the input file is an uncompressed
 *          280x192 color picture (Apple// HGR memory BSAVE) that
 *          has already been ported to the IBM environment.
 *
 * Usage:   a2i <applefile> [WAIT]
 *
 *          If no extension is given, an extension of .RAW is assumed.
 *          Output file generated uses same name as input file, with
 *          a .PCX extension.
 *
 *          If you add "WAIT" to the command line (or anything really,
 *          as all I check for is a 2nd parameter) the program will beep
 *          and wait for a keypress before exiting.  Leaving this off the
 *          command line makes the program work from a batch file for
 *          multiple conversions.
 */

/* INCLUDES */

#include <stdio.h>
#include <process.h>
#include <dir.h>
#include <conio.h>

#include "ega.h"




/* DEFINES */

#define ERROR -1
#define A_OK   0

/*
 * The following are pallette positions in the default EGA pallette that
 * closely (relatively) approximate the IBM screen colors.
 */

#define _A_BLACK   0
#define _A_BLUE    1
#define _A_GREEN   2
#define _A_VIOLET  9
#define _A_ORANGE 12
#define _A_WHITE  15




/* GLOBALS */

UBYTE apple_memory[192][40];		/* Apple "memory" map */

UBYTE far default_palette[16] = {
				 0x00, 0x01, 0x02, 0x03,
				 0x04, 0x05, 0x06, 0x07,
				 0x38, 0x39, 0x3A, 0x3B,
				 0x3C, 0x3D, 0x3E, 0x3F
				};	/* Default EGA pallette colors */

int apple_lines[8][3] = {
			 {  0,  64, 128 },
			 {  8,  72, 136 },
			 { 16,  80, 144 },
			 { 24,  88, 152 },
			 { 32,  96, 160 },
			 { 40, 104, 168 },
			 { 48, 112, 176 },
			 { 56, 120, 184 }
			};		/* Index table for Apple memory */

/*
 * The Apple HGR screen is wonderfully convoluted (in several ways).  For
 * instance, the color of a particular pixel on the Apple screen depends
 * upon THREE (3) things:
 *
 *   1) Is the Apple screen byte an EVEN or ODD byte on the scanline?
 *   2) Is the MSB of the screen byte SET?
 *   3) Is the pixel/bit in question on an EVEN or ODD column?
 *
 * Pretty confusing, huh?  a2i_colors[][][] handles this as follows:
 *
 *   Pixel_color = a2i_colors[EVEN/ODD byte?][MSB SET?][EVEN/ODD column?]
 */

int a2i_colors[2][2][2] = {
			   { /* EVEN byte        */
			    { _A_VIOLET, _A_GREEN  },	/* MSB not set */
			    { _A_BLUE  , _A_ORANGE }    /* MSB set     */
			   },
			   { /* ODD byte         */
			    { _A_GREEN , _A_VIOLET },   /* MSB not set */
			    { _A_ORANGE, _A_BLUE   }    /* MSB set     */
			   }
			  };




/* FUNCTIONS */

/*
 * load_apple_raw() -- Take a raw BSAVE file and load it into the temporary
 *                     memory buffer.  This routine unscrambles Apple's
 *                     memory mapping at the same time, so that the buffer
 *                     becomes consecutive, as opposed to Apple's convoluted
 *                     skipping from block to block.
 */
int load_apple_raw(const char *fname)
{
 int  i, vline, block, mline = 0;
 FILE *fsave;


 if((fsave = fopen(fname, "rb")) == NULL)
  return ERROR;

 rewind(fsave);

 /*
  * Even though we're dealing with Apple graphics, the screen map is still
  * heavily tied to the text screen layout.  Apple's HGR screen is divided
  * into 24 "blocks" of 8 lines per block (the main reason why when you do
  * a BLOAD to the HGR screen, you get the infamous "venetian blind" load.
  * Unfortunately, we end up stepping through memory (scanline-wise) as
  * follows:
  *
  *   0, 64, 128, 8, 72, 136, ..., 184, 1, 65, 129, ...
  *
  * (i.e. line 0 is the first line of the file, line 1 is the 25th line of
  * the file...wonderful, right?)
  *
  * If that isn't enough, every there are an additional 8 bytes of garbage
  * space every 3 scanlines (reminiscent of IBM's CGA architecture).
  *
  * The easiest way I found to handle this is to create the apple_lines[][]
  * table of base scanlines and index off of them.
  */

 for(block=0;block<8;block++) 
  for(mline=0;mline<24;mline++)
   {
   vline = apple_lines[mline / 3][mline % 3] + block;

   for(i=0;i<40;i++)
    apple_memory[vline][i] = fgetc(fsave);

   if((mline % 3) == 2)
    for(i=0;i<8;i++)
     fgetc(fsave);
   }

 fclose(fsave);

 return A_OK;
}



/*
 * apple_2_ibm() -- Take the "Apple map" image in the apple_memory[][]
 *                  array and translate it into an EGA mode 13 image on
 *                  the display.  xoff and yoff are pixel offsets on the
 *                  EGA screen that result in the Apple image (280x192)
 *                  being displayed dead center on the EGA screen (320x200).
 */
void apple_2_ibm(void)
{
 int   xoff = 20, yoff = 4;
 int   i, j, k, which, line[280];
 UBYTE abyte, abit;


 for(i=0;i<192;i++)		       /* 192 lines in an Apple HGR screen */
  {

  /*
   * The first thing we do is convert an Apple scanline to an EGA scanline,
   * meaning that we must decode each Apple byte (7 pixels per byte) into
   * EGA pixels.  line[] is the line buffer, each index holding one pixel's
   * color value.
   */

  for(j=0;j<40;j++)                    /* 280 pixels/line = 40 Apple bytes */
   {
   abyte = apple_memory[i][j];	       /* Get an Apple byte                */

   which = (abyte & 0x80) == 0x80;     /* Which Apple color scheme?        */

   /*
    * Now, cycle through the Apple byte one pixel at a time.  Since Apple
    * bytes are encoded "backwards" to IBM screenlines, the least significant
    * bit of the Apple byte is the far right pixel of the IBM byte.
    * Therefore, the decompression process works as follows:
    *
    *   1) Grab LSB of Apple byte.
    *   2) Set equivalent EGA pixel to correct Apple color (remember, Apple
    *      colors depend upon:  A) whether the pixel is in an even or odd
    *      column (on the Apple screen), and B) whether the high bit of the
    *      Apple byte is set or not.
    *   3) Shift Apple byte right one bit.
    *   4) Repeat steps 1-3 for remaining 6 pixels in Apple byte
    */

   for(k=0;k<7;k++)		       /* 7 pixels per Apple byte          */
    {
    abit = abyte & 0x01;

    line[j*7 + k] = a2i_colors[j % 2][which][k % 2] * abit;

    abyte >>= 1;
    }
   }

  /*
   * Now that we've built an EGA screen line, write it out to screen memory.
   * The actual color displayed on the Apple screen depends not only on
   * whether a given pixel is set or not, but also on whether adjacent
   * pixels are set (if two adjacent pixels are on, the resulting color is
   * WHITE, *not* the individual pixel colors).  The algorithm is:
   *
   *   IF (current pixel first on scanline)
   *     Check the next pixel
   *     IF (both pixels aren't black)
   *       Write out a white pixel
   *     ELSE
   *       Write out the color of the first pixel
   *   ELSE IF (current pixel last on scanline)
   *     Check the previous pixel
   *     IF (both pixels aren't black)
   *       Write out white pixel
   *     ELSE
   *       Write out the color of the last pixel
   *   ELSE
   *     IF (current pixel not black) AND (previous OR next pixel not black)
   *       Write out a white pixel
   *     ELSE IF (current pixel black) AND (previous pixel same color as
   *                                        next pixel)
   *       Write out color of previous pixel
   *     ELSE
   *       Write out color of current pixel
   *
   * The convoluted part of the algorithm (the last part of the main IF
   * structure) is because 2 ON pixels with 1 OFF pixel between them on
   * the Apple screen are seen as a solid color (no gaps).
   */

  for(k=0;k<280;k++)
   {
   if(k == 0)
    {
    if(line[k]!=_A_BLACK && line[k+1]!=_A_BLACK)
     put_pixel(k + xoff, i + yoff, _A_WHITE);
    else
     put_pixel(k + xoff, i + yoff, line[k] );
    }
   else
   if(k == 279)
    {
    if(line[k]!=_A_BLACK && line[k-1]!=_A_BLACK)
     put_pixel(k + xoff, i + yoff, _A_WHITE);
    else
     put_pixel(k + xoff, i + yoff, line[k] );
    }
   else
    {
    if(line[k]!=_A_BLACK && (line[k-1]!=_A_BLACK || line[k+1]!=_A_BLACK))
     put_pixel(k + xoff, i + yoff, _A_WHITE );
    else
    if(line[k]==_A_BLACK && line[k-1]==line[k+1])
     put_pixel(k + xoff, i + yoff, line[k-1]);
    else
     put_pixel(k + xoff, i + yoff, line[k]  );
    }
   }
  }
}




/* MAIN */

void main(int argc, char **argv)
{
 char drive[MAXDRIVE], dir[MAXDIR], file[MAXFILE], ext[MAXEXT];
 char infile[13], outfile[13];


 if(argc == 1)
  {
  printf("Usage:  a2i <applefile> [WAIT]\n");
  exit(0);
  }

 init_egfx(0x0D);			/* Initialize EGA mode 13 */

 fnsplit(argv[1], drive, dir, file, ext);

 if(ext[0] == '\0')
  fnmerge(infile, "", "", file, ".RAW");
 else
  fnmerge(infile, "", "", file, ext   );

 fnmerge(outfile, "", "", file, ".PCX");

 gotoxy(12, 10);  printf("Loading...");

 if(load_apple_raw(infile) != ERROR)
  {
  gotoxy(12, 10);  printf("Converting...");

  apple_2_ibm();

  sleep(2);

  save_pcx(outfile, default_palette);

  cls(0);

  fload_pcx(0, outfile);

  if(argc == 3)
   {
   putch(7);  
 
   getch();
   }
  }
 else
  printf("Error...");

 mode(3);
}
