/* 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 /* isdigit() */ #include /* FILE, fclose(), fgetc(), fopen(), fprintf() */ #include /* errno, exit(), size_t */ #include /* strerror(), strlen() */ #include /* 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; }