/*
  A2PCTEXT.C

  OVERVIEW
  
    40/80 column text screen capture from ApplePC "save state" file

  PURPOSE
  
    Capture or extract 40/80 column text screens from ApplePC emulator
    "save state" files.
    
    Captured text is saved as a normal text file for import into a
    wide variety of applications.

  NOTES
  
    Location $C01F in main memory (or offset x'0000C01F' in the
    "save state" file) does not appear to be a reliable means
    of determining if the text display is 40 or 80 columns.  It
    appears that most of the softswitch area is set to zeros.

  ENHANCEMENTS

    o Examine softswitches (if possible) to automatically determine
      the text mode (40 or 80 columns)
    o HGR, HGR2, DHR, DHR2 graphic capture modes

  LANGUAGE       : ANSI/ISO C
  USER INTERFACE : Command Line
  OS             : MS-DOS (although it should be fairly portable)

  When         Who  What/Where/How/Why
  -----------  ---  ----------------------------------------------------
  1996-Jan-27  EWW  v0.1 Original.  Core logic working.  Even though
                      an output file is a required parm, output is
                      currently sent to stdout.
                    Add parm for textmode (4[0] or 8[0])
                    v0.2 Support for up to 3 user-supplied parms
                      (text mode, input_file, output_file).  If an
                      output_file is not specified, output will be
                      sent to stdout
*/

#include <ctype.h>
  /* isdigit() */
#include <stdio.h>
  /* FILE, fclose(), fgetc(), fopen(), fprintf() */
#include <stdlib.h>
  /* errno, exit(), size_t */
#include <string.h>
  /* strerror(), strlen() */
#include <errno.h> /* added for UNIX compatibility */
  /* errno */
                             
#define VERSION "v0.2"

#define PARMS_MIN 3
#define PARMS_MAX 4

#define PARM_PROGRAM 0
#define PARM_COLS    1
#define PARM_INPUT   2
#define PARM_OUTPUT  3


void About(void)
  {
  fprintf(stderr, "\nA2PCText by Erick Wagner\n");
  fprintf(stderr, "  %s  Last compiled on %s at %s\n\n", VERSION, __DATE__, __TIME__);
  fprintf(stderr, "  Usage: A2PCTEXT 40|80 [path]input_file [path]output.file\n\n");
  fprintf(stderr, "  input_file is ApplePC \"save state\" file\n");
  }


int main(int argc, char * argv[])
  {
  FILE * fp_iStream = NULL;
  FILE * fp_oStream = NULL;
  size_t nI = 0;
  int    nTextMode = 40;
  int    nRow = 0;
  int    nCol = 0;
  char   szWork1[40]; /* hold area for main memory data */
  char   szWork2[40]; /* hold area for auxiliary memory data */
  char   tTextScreen[24][(80+1)];
  const long tLineOffsets[24] =
    {
     0x00000400, 0x00000480, 0x00000500, 0x00000580, /* lines 01 - 04 */
     0x00000600, 0x00000680, 0x00000700, 0x00000780, /* lines 05 - 08 */
     0x00000428, 0x000004a8, 0x00000528, 0x000005a8, /* lines 09 - 12 */
     0x00000628, 0x000006a8, 0x00000728, 0x000007a8, /* lines 13 - 16 */
     0x00000450, 0x000004d0, 0x00000550, 0x000005d0, /* lines 17 - 20 */
     0x00000650, 0x000006d0, 0x00000750, 0x000007d0, /* lines 21 - 24 */
    };

  /*
    --------------------------
    Input parameter validation
    --------------------------
  */  
  if (argc < PARMS_MIN)
    {
    fprintf(stderr, "Too few parameters were specified\a\n");
    About();
    exit(-1);
    }

  if (argc > PARMS_MAX)
    {
    fprintf(stderr, "Too many parameters were specified\a\n");
    About();
    exit(-1);
    }

  /* Validate text mode parameter */
  for (nI = 0; (nI < strlen(argv[PARM_COLS])); nI++)
    {
    if (isdigit(argv[PARM_COLS][nI]) == 0)
      {
      fprintf(stderr, "Text mode value was non-numeric.  Use a value of 40 or 80\a\n");
      exit(-1);
      }
    }

  switch (*argv[PARM_COLS])
    {
    case '4':
      {
      nTextMode = 40;
      }
      break;
    case '8':
      {
      nTextMode = 80;
      }
      break;
    default:
      {
      nTextMode = 40;
      fprintf(stderr, "Warning: Text Mode defaulted to 40 columns\n");
      }
    }

  /*
    ---------------
    File open logic
    ---------------
  */  
  fp_iStream = fopen(argv[PARM_INPUT], "rb");

  if (fp_iStream == NULL)
    {
    fprintf(stderr, "The input file could not be opened\n");
    fprintf(stderr, "  Reason: %s\a\n", strerror(errno));
    exit(-1);
    }

  if ((argc - 1) < PARM_OUTPUT)
    {
    fp_oStream = stdout;
    }
  else
    {
    fp_oStream = fopen(argv[PARM_OUTPUT], "wt");

    if (fp_oStream == NULL)
      {
      fprintf(stderr, "The output file could not be opened\n");
      fprintf(stderr, "  Reason: %s\a\n", strerror(errno));
      exit(-1);
      }
    }

  /*
    -----------------------
    Text capture/extraction
    -----------------------
  */  
  for (nRow = 0; (nRow < 24); nRow++)
    {
    /* Get primary text screen -- Main Memory */
    fseek(fp_iStream, (0x00000000 + tLineOffsets[nRow]),SEEK_SET);
    fread(szWork1, 40, 1, fp_iStream);
    
    if (nTextMode == 80)
      {
      /* Get primary text screen -- Auxiliary Memory */
      fseek(fp_iStream, (0x00010000 + tLineOffsets[nRow]),SEEK_SET);
      fread(szWork2, 40, 1, fp_iStream);
      }
    
    for (nCol = 0; (nCol < 40); nCol++)
      {
      if (nTextMode == 80)
        {
        /* convert to standard ASCII text */
        tTextScreen[nRow][(nCol*2)]     = szWork2[nCol] & 0x7f; /* even positions */
        tTextScreen[nRow][((nCol*2)+1)] = szWork1[nCol] & 0x7f; /* odd positions  */
        }
      else
        {
        /* convert to standard ASCII text */
        tTextScreen[nRow][nCol] = szWork1[nCol] & 0x7f;
        }
      }
      
    /* terminate the string based on the text mode */
    tTextScreen[nRow][nTextMode] = '\0';
    }

  for (nRow = 0; (nRow < 24); nRow++)
    {
    fprintf(fp_oStream, "%s\n", tTextScreen[nRow]);
    }

  /*
    ----------------
    File close logic
    ----------------
  */  
  fclose(fp_iStream);

  if (fp_oStream != stdout)
    {
    fclose(fp_oStream);
    }

  return 0;
  }
