/* ------------------------------------------------------------------------ */ /* BMP2LO.C (C) Copyright Bill Buckels 2009 */ /* All Rights Reserved. */ /* */ /* Licence Agreement */ /* ----------------- */ /* */ /* You have a royalty-free right to use, modify, reproduce and */ /* distribute this source code in any way you find useful, */ /* provided that you agree that Bill Buckels has no warranty obligations */ /* or liability resulting from said distribution in any way whatsoever. */ /* If you don't agree, remove this source code from your computer now. */ /* */ /* Written by : Bill Buckels */ /* Email: bbuckels@escape.ca */ /* */ /* Purpose : This utility will allow you to convert to */ /* Apple II Double Lo-res 80 x 48 x 16 color images */ /* from IBM-PC graphics files in the following formats: */ /* */ /* CGA 320 x 200 x 4 color BASIC BSAVED IMAGE (.BAS) Files */ /* CGA 320 x 200 x 4 color ZSOFT .PCX Files */ /* EGA 320 x 200 x 16 color Windows .BMP Files */ /* */ /* Revision : 1.0 First Release */ /* ------------------------------------------------------------------------ */ /* Written in Large Model 16 bit Microsoft C (MSC) Version 8.00c */ /* Note: Run in an MS-DOS emulator like DOSBox if you can't run it raw. */ /* ------------------------------------------------------------------------ */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <dos.h> #include <bios.h> #include <io.h> #include <malloc.h> #include <conio.h> /* ------------------------------------------------------------------------ */ /* Declarations, Vars. etc. */ /* ------------------------------------------------------------------------ */ typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned long ulong; uchar *szTitle[]= { " ", " BMP2LO(C) ", " Copyright Bill Buckels 2009 ", " All Rights Reserved. ", " Distributed as FreeWare. ", " Email: bbuckels@escape.ca ", " ", " Press Any Key To Continue... ", " --------------------------------- ", " To Position Clip Box - Arrow Keys ", " Page Up, Page Down, Home, and End ", " ", " 'S' or [ENTER] to Save ", " 'Q' or [ESC] to Quit ", " 'H' For Help ", " ", " 0-9 Toggle Color (Numeric Keys) ", " A-F Toggle Color (Alpha Keys) ", " ", NULL}; uchar *szTextTitle = "BMP2LO(C) Version 1.0 Copyright Bill Buckels 2009\n" "All Rights Reserved."; #define TRUE 1 #define FALSE 0 #define SUCCESS 0 #define VALID SUCCESS #define FAILURE -1 #define INVALID FAILURE #define MCGA '\x13' #define TEXT '\x03' #define ASCIIZ '\x00' #define CRETURN '\r' #define LFEED '\n' #define ENTERKEY '\x0d' /* character generated by the Enter Key */ #define ESCKEY '\x1b' /* character generated by the Esc key */ #define FUNCKEY '\x00' /* first character generated by function keys */ #define UPARROW 'H' /* second character generated by up-arrow key */ #define DOWNARROW 'P' /* second character generated by down-arrow key */ #define LTARROW 'K' /* second character generated by left-arrow key */ #define RTARROW 'M' /* second character generated by right-arrow key */ #define PGUP 'I' /* second character generated by page up key */ #define PGDOWN 'Q' /* second character generated by page down key */ #define HOMEKEY 'G' /* second character generated by home key */ #define ENDKEY 'O' /* second character generated by end key */ /* second character generated by numerical fkeys */ /* starting at character 59 */ /* ending at character 68 */ #define F1 ';' #define F2 '<' #define F3 '=' #define F4 '>' #define F5 '?' #define F6 '@' #define F7 'A' #define F8 'B' #define F9 'C' #define F10 'D' #define FRAMESIZE ((unsigned)64000) #define FRAMEADDR ((uchar *)0xa0000000l) /* middle of screen */ #define XMIN 0 #define YMIN 0 #define XMOS 159 #define YMOS 99 #define XMAX 319 #define YMAX 199 #define SCREENWIDTH (XMAX + 1) #define RASTERWIDTH SCREENWIDTH #define SCREENHEIGHT (YMAX + 1) #define CELL_SIZE 8 // some "helpful" macros #define vload() memcpy(FRAMEADDR,(uchar *)&rawbuffer[0],FRAMESIZE) #define vsave() memcpy((uchar *)&rawbuffer[0],FRAMEADDR,FRAMESIZE) #define zap(x) memset(FRAMEADDR,x,FRAMESIZE) #define getpixel(x,y) rawbuffer[(y*RASTERWIDTH)+x] // function prototypes uchar GetMcgaPaletteIndex(uint cgacolor), lsb(uint), msb(uint), SetCrtMode(uchar); uint byteword(uchar, uchar); int BMP16_Read(uchar *), BSAVE_Read(uchar *), CheckForCGAPCX(uchar *), EatKeys(), GetVGAIndex(uchar, uchar, uchar), LoadPalette(void), MakeBMP(uchar *, uchar *), PCX_Read(uchar *), savelofragment(uchar *, int, int); void LineBox(int, int, int, int, uint), PCMidFont(uchar *, int, int, int, int, int), PCRomFont(uchar *, int, int, int, int, int), PutPixel(int, int, uint, uchar *), setlopixel(uchar,int, int, int), SetPalette(), ShowTitle(void), TogglePalette(uchar), XBox(int, int, int, int), XPixel(int, int, uchar *); uchar outlinecolor; uchar drawcolor; uchar *rawbuffer; #define NUM_MCGA_COLORS 256 #define NUM_RGB_COLORS 3 uchar rgbinfo[NUM_MCGA_COLORS][NUM_RGB_COLORS]; /* the vga palette */ enum { BLACK = 0, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, WHITE, GRAY, LBLUE, LGREEN, LCYAN, LRED, LMAGENTA, YELLOW, BWHITE, NUM_VGA_COLORS}; /* the following 4 palettes are used to troll for standard colors in the order given below */ /* 16 Color BMP style palette (probably PBRUSH from Windows 3.1) */ unsigned char rgbBmpArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0x00, 0x00, 0x00, // BLACK 0x00, 0x00, 0xBF, // BLUE 0x00, 0xBF, 0x00, // GREEN 0x00, 0xBF, 0xBF, // CYAN 0xBF, 0x00, 0x00, // RED 0xBF, 0x00, 0xBF, // MAGENTA 0xBF, 0xBF, 0x00, // BROWN 0xC0, 0xC0, 0xC0, // WHITE 0x80, 0x80, 0x80, // GRAY 0x00, 0x00, 0xFF, // LBLUE 0x00, 0xFF, 0x00, // LGREEN 0x00, 0xFF, 0xFF, // LCYAN 0xFF, 0x00, 0x00, // LRED 0xFF, 0x00, 0xFF, // LMAGENTA 0xFF, 0xFF, 0x00, // YELLOW 0xFF, 0xFF, 0xFF}; // BWHITE /* 16 Color BMP style palette (Windows XP) */ /* notice that these johnny-come-latelies reversed GRAY and WHITE */ unsigned char rgbXmpArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0x00, 0x00, 0x00, // BLACK 0x00, 0x00, 0x80, // BLUE 0x00, 0x80, 0x00, // GREEN 0x00, 0x80, 0x80, // CYAN 0x80, 0x00, 0x00, // RED 0x80, 0x00, 0x80, // MAGENTA 0x80, 0x80, 0x00, // BROWN 0x80, 0x80, 0x80, // GRAY 0xC0, 0xC0, 0xC0, // WHITE 0x00, 0x00, 0xFF, // LBLUE 0x00, 0xFF, 0x00, // LGREEN 0x00, 0xFF, 0xFF, // LCYAN 0xFF, 0x00, 0x00, // LRED 0xFF, 0x00, 0xFF, // LMAGENTA 0xFF, 0xFF, 0x00, // YELLOW 0xFF, 0xFF, 0xFF}; // BWHITE // VGA Palette unsigned char rgbVgaArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0x00, 0x00, 0x00, /* BLACK */ 0x00, 0x00, 0xFF, /* BLUE */ 0x00, 0xFF, 0x00, /* GREEN */ 0x00, 0xFF, 0xFF, /* CYAN */ 0xFF, 0x00, 0x00, /* RED */ 0xFF, 0x00, 0xFF, /* MAGENTA */ 0xFF, 0xFF, 0x00, /* BROWN */ 0xC0, 0xC0, 0xC0, /* WHITE */ 0x55, 0x55, 0x55, /* GRAY */ 0x55, 0x55, 0xFF, /* LBLUE */ 0x55, 0xFF, 0x55, /* LGREEN */ 0x55, 0xFF, 0xFF, /* LCYAN */ 0xFF, 0x55, 0x55, /* LRED */ 0xFF, 0x55, 0xFF, /* LMAGENTA */ 0xFF, 0xFF, 0x55, /* YELLOW */ 0xFF, 0xFF, 0xFF}; /* BWHITE */ // 16 color ZSOFT PCPAINT PCX style palette unsigned char rgbPcxArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0x00, 0x00, 0x00, /* BLACK */ 0x00, 0x00, 0xAA, /* BLUE */ 0x00, 0xAA, 0x00, /* GREEN */ 0x00, 0xAA, 0xAA, /* CYAN */ 0xAA, 0x00, 0x00, /* RED */ 0xAA, 0x00, 0xAA, /* MAGENTA */ 0xAA, 0xAA, 0x00, /* BROWN */ 0xAA, 0xAA, 0xAA, /* WHITE */ 0x55, 0x55, 0x55, /* GRAY */ 0x55, 0x55, 0xFF, /* LBLUE */ 0x55, 0xFF, 0x55, /* LGREEN */ 0x55, 0xFF, 0xFF, /* LCYAN */ 0xFF, 0x55, 0x55, /* LRED */ 0xFF, 0x55, 0xFF, /* LMAGENTA */ 0xFF, 0xFF, 0x55, /* YELLOW */ 0xFF, 0xFF, 0xFF}; /* BWHITE */ /* this palette is in the actual apple II lores color order */ /* this shows us a[[roximately what we will end-up with */ uchar rgbLoresArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0, 0, 0, /* black */ 227, 30, 96, /* red */ 96, 78, 189, /* dk blue */ 255, 68, 253, /* purple */ 0, 163, 96, /* dk green */ 156, 156, 156, /* gray */ 20, 207, 253, /* med blue */ 208, 195, 255, /* lt blue */ 96, 114, 3, /* brown */ 255, 106, 60, /* orange */ 156, 156, 156, /* grey */ 255, 160, 208, /* pink */ 20, 245, 60, /* lt green */ 208, 221, 141, /* yellow */ 114, 255, 208, /* aqua */ 255, 255, 255}; /* white */ #define LOBLACK 0 #define LORED 1 #define LODKBLUE 2 #define LOPURPLE 3 #define LODKGREEN 4 #define LOGRAY 5 #define LOMEDBLUE 6 #define LOLTBLUE 7 #define LOBROWN 8 #define LOORANGE 9 #define LOGREY 10 #define LOPINK 11 #define LOLTGREEN 12 #define LOYELLOW 13 #define LOAQUA 14 #define LOWHITE 15 unsigned char VGAtoApple[NUM_VGA_COLORS]={ LOBLACK, LODKBLUE, LODKGREEN, LOMEDBLUE, LORED, LOPURPLE, LOBROWN, LOWHITE, LOGRAY, LOLTBLUE, LOLTGREEN, LOAQUA, LOORANGE, LOPINK, LOYELLOW, LOWHITE}; // to rollback the colors in case we can't automatically assign unsigned char VGAtoAppleSaved[NUM_VGA_COLORS]={ LOBLACK, LODKBLUE, LODKGREEN, LOMEDBLUE, LORED, LOPURPLE, LOBROWN, LOWHITE, LOGRAY, LOLTBLUE, LOLTGREEN, LOAQUA, LOORANGE, LOPINK, LOYELLOW, LOWHITE}; /* our working copy of the apple II lores colors */ /* this is in Apple II order */ uchar rgbArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0, 0, 0, /* black */ 227, 30, 96, /* red */ 96, 78, 189, /* dk blue */ 255, 68, 253, /* purple */ 0, 163, 96, /* dk green */ 156, 156, 156, /* gray */ 20, 207, 253, /* med blue */ 208, 195, 255, /* lt blue */ 96, 114, 3, /* brown */ 255, 106, 60, /* orange */ 156, 156, 156, /* grey */ 255, 160, 208, /* pink */ 20, 245, 60, /* lt green */ 208, 221, 141, /* yellow */ 114, 255, 208, /* aqua */ 255, 255, 255}; /* white */ /* 16 Color VGA style palettes with RGB values from corresponding Apple II lores colors */ unsigned char rgbOriginalArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={ 0, 0, 0, // BLACK - black 96, 78, 189, // BLUE - dk blue 0, 163, 96, // GREEN - dk green 20, 207, 253, // CYAN - med blue 227, 30, 96, // RED - red 255, 68, 253, // MAGENTA - purple 96, 114, 3, // BROWN - brown 255, 255, 255, // WHITE - white 156, 156, 156, // GRAY - gray or grey 208, 195, 255, // LBLUE - lt blue 20, 245, 60, // LGREEN - lt green 114, 255, 208, // LCYAN - aqua 255, 106, 60, // LRED - orange 255, 160, 208, // LMAGENTA - pink 208, 221, 141, // YELLOW - yellow 255, 255, 255}; // BWHITE - white enum { CGA_BLACK = 0, CGA_CYAN, CGA_MAGENTA, CGA_WHITE, NUM_CGA_COLORS}; /* a microsoft compatible bsaved image format descriptor */ uchar BSAVED_header[7]={ '\xfd','\x00','\xb8','\x00','\x00','\x00','\x40'}; /* bmiHeader.biClrUsed and bmiHeader.biClrImportant fields will vary depending on the process that created the bmp but the other fields (below) should be invariant. */ uchar BMP_checkheader[] ={ 0x42, 0x4D, 0x76, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /* ------------------------------------------------------------------------ */ /* Low Level Video Routines, drawing routines, etc. */ /* ------------------------------------------------------------------------ */ uchar SetCrtMode(uchar vidmode) { union REGS inregs, outregs; /* set mode */ inregs.h.ah = 0; inregs.h.al = vidmode; int86(0x10, &inregs, &outregs); /* get mode */ inregs.h.ah = 0xf; int86(0x10, &inregs, &outregs); /* return mode */ return outregs.h.al; } int LoadPalette() { union REGS regs; struct SREGS segregs; regs.h.ah = 0x10; /* function 10h */ regs.h.al = 0x12; /* subfunction 12h - set block of color registers */ regs.x.bx = 0; /* start with this reg */ regs.x.cx = NUM_MCGA_COLORS; /* do this many */ regs.x.dx = (uint)rgbinfo; /* offset to array */ segregs.es = (uint)((long)rgbinfo >> 16); /* segment of array */ int86x(0x10, ®s, ®s, &segregs);/* dump data to color registers */ return SUCCESS; } /* ---------------------------------------------------------------------- */ /* Write a pixel at x,y using color */ /* ---------------------------------------------------------------------- */ void PutPixel(int x, int y,uint pixelvalue, uchar *framebuffer) { if (x<XMIN || x>XMAX || y<YMIN || y>YMAX) return; framebuffer[(y*RASTERWIDTH)+x]=(uchar)pixelvalue; } void XPixel(int x, int y, uchar *framebuffer) { if (x<XMIN || x>XMAX || y<YMIN || y>YMAX) return; framebuffer[(y*RASTERWIDTH)+x] = framebuffer[(y*RASTERWIDTH)+x]^0xff; } void XBox(int x1, int y1, int x2, int y2) { int x, y; for (x = x1; x <= x2; x++)XPixel(x, y1, FRAMEADDR); for (y = (y1+1); y < y2; y++) { XPixel(x1, y, FRAMEADDR); XPixel(x2, y, FRAMEADDR); } for (x = x1; x <= x2; x++)XPixel(x, y2, FRAMEADDR); return; } void LineBox(int x1, int y1, int x2, int y2, uint pixelvalue) { int x, y; for (x = x1; x <= x2; x++)PutPixel(x, y1, pixelvalue, FRAMEADDR); for (y = (y1+1); y < y2; y++) { PutPixel(x1, y, pixelvalue, FRAMEADDR); PutPixel(x2, y, pixelvalue, FRAMEADDR); } for (x = x1; x <= x2; x++)PutPixel(x, y2, pixelvalue, FRAMEADDR); return; } /* ---------------------------------------------------------------------- */ /* PCRomFont */ /* Uses the rom font as a template for a bitmap font. */ /* This allows us to map a font using scaling techniques on any PC */ /* without the need for an external font file or a bios supported font. */ /* This was more prevalent in days gone by than it is today, of course. */ /* ---------------------------------------------------------------------- */ void PCRomFont(uchar *str, int xorigin, int yorigin, int scale, int fontcolor, int outlinecolor) { int scanline, yreg=yorigin, xreg=xorigin, byt, character, nibble, color; int x,y; /* flags etcetera */ uchar *romfont=(uchar *) 0xffa6000el; /* a pointer to the 8 x 8 BITMAPPED font in the PC ROM */ int target = strlen(str); /* string length */ for (scanline=0;scanline<CELL_SIZE;scanline++)/* finish the current scanline*/ { /* before advancing to the next*/ for (byt=0;byt<target;byt++) /* run the scanline*/ { /* get the bitmap */ character = romfont[(str[byt]&0x7f)*CELL_SIZE+scanline]; for (nibble=0;nibble<CELL_SIZE;nibble++) { xreg+=scale; /* chew the byte to bits and lite the pixel if it's a swallow */ if (str[byt]!=CRETURN && str[byt]!=LFEED) { if (character & 0x80>>nibble) color = fontcolor; else color = outlinecolor; if (color > -1 ) { for (x=0; x!=scale; x++) for (y=0;y!=scale;y++) PutPixel(xreg+x,yreg+y,color, FRAMEADDR); } } } } yreg+=scale; xreg=xorigin; } } /* ---------------------------------------------------------------------- */ /* PCMidFont */ /* Maps to PCRomfont, uses a centre-justified x-coordinate. */ /* ---------------------------------------------------------------------- */ void PCMidFont(uchar *str, int xmiddle, int yorigin, int scale, int fontcolor, int outlinecolor) { /* centre justified string */ PCRomFont(str,(xmiddle-(4*(strlen(str))*scale)),yorigin,scale, fontcolor, outlinecolor); } /* ---------------------------------------------------------------------- */ /* GetMcgaPalette is called during the loading of the input file. */ /* ---------------------------------------------------------------------- */ uchar GetMcgaPaletteIndex(uint cgacolor) { // I just hardcoded these in here for CGA images. // I couldn't see any point in getting too gancy with 4 colors // There isn't much hardship in toggling 4 colors anyway // My main target for automation was when a 16 color BMP is colored // in Windows Paint... then exported in this thing. uint uiIndex; switch(cgacolor) { case CGA_WHITE: uiIndex = LOWHITE; break; case CGA_MAGENTA: uiIndex = LORED; break; case CGA_CYAN: uiIndex = LODKBLUE; break; case CGA_BLACK: default: uiIndex = LOBLACK; break; } return (uchar)uiIndex; } void SetPalette() { uint idx, x, temp; memset((uchar *)&rgbinfo[0][0], 0, (NUM_MCGA_COLORS*NUM_RGB_COLORS)); /* Set the lightest and darkest color in the current palette. */ /* use the darkest color for the drawcolor */ /* use the lightest color for the outline color */ drawcolor = 254; outlinecolor = 255; // using 6 bit color model (VGA standard video uses 6 bits) // for the internal program. values are in the range 0-63 rgbinfo[255][0] = rgbinfo[255][1] = rgbinfo[255][2] = 63; // leave the drawcolor and outline colors alone for (idx = 0; idx < NUM_VGA_COLORS; idx++) { for (x = 0; x < NUM_RGB_COLORS; x++) { temp = rgbArray[idx][x]; rgbinfo[idx][x] = temp >> 2; // downshift to 6 bits of color } } } /* the following attempts to match BMP colors with a common palette */ /* that is to say... a basic default EGA style 16 color palette like */ /* the kind that Windows Paint provides as a lowest common denominator */ /* my rationale here is that if we can't automate the entire remapping */ /* we fail the whole sh*terree because otherwise we could start mashing */ /* two colors or more together and I for one would not care for that... */ int GetVGAIndex(uchar red, uchar green, uchar blue) { int idx; // try exact match for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbBmpArray[idx][0] && green == rgbBmpArray[idx][1] && blue == rgbBmpArray[idx][2])return idx; } for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbXmpArray[idx][0] && green == rgbXmpArray[idx][1] && blue == rgbXmpArray[idx][2])return idx; } for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbVgaArray[idx][0] && green == rgbVgaArray[idx][1] && blue == rgbVgaArray[idx][2])return idx; } for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbPcxArray[idx][0] && green == rgbPcxArray[idx][1] && blue == rgbPcxArray[idx][2])return idx; } // adjust gun values to match using EGA-like thresholds // this corresponds to the old PCX style palette if (red < 43) red = 0; // 0x00 else if (red < 128) red = 85; // 0x55 else if (red < 224) red = 170; // 0xaa else red = 255; // 0xff if (green < 43) green = 0; else if (green < 128) green = 85; else if (green < 213) green = 170; else green = 255; if (blue < 43) blue = 0; else if (blue < 128) blue = 85; else if (blue < 213) blue = 170; else blue = 255; // try again for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbBmpArray[idx][0] && green == rgbBmpArray[idx][1] && blue == rgbBmpArray[idx][2])return idx; } for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbXmpArray[idx][0] && green == rgbXmpArray[idx][1] && blue == rgbXmpArray[idx][2])return idx; } for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbVgaArray[idx][0] && green == rgbVgaArray[idx][1] && blue == rgbVgaArray[idx][2])return idx; } for (idx = 0; idx < NUM_VGA_COLORS; idx++) { if (red == rgbPcxArray[idx][0] && green == rgbPcxArray[idx][1] && blue == rgbPcxArray[idx][2])return idx; } // if no match yet let them pick lores color manually return INVALID; } int PaletteIndex[NUM_VGA_COLORS] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; void TogglePalette(uchar ch) { /* after the initial remapping of colors by color order */ /* it is very likely that not all entries will remap correctly */ /* so the palette can be toggled to adjust this */ int idx, jdx; ch = toupper(ch); switch(ch) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': idx = ch - 55; break; default: if (ch < '0' || ch > '9')return; idx = ch - 48; } /* update remapping index */ jdx = PaletteIndex[idx] + 1; if (jdx > 15)jdx = 0; PaletteIndex[idx] = jdx; /* update the working array */ rgbArray[idx][0] = rgbLoresArray[jdx][0]; rgbArray[idx][1] = rgbLoresArray[jdx][1]; rgbArray[idx][2] = rgbLoresArray[jdx][2]; /* update the visual */ SetPalette(); LoadPalette(); } /* ---------------------------------------------------------------------- */ /* File Related Functions */ /* Image Loaders, Image Savers, etc. */ /* ---------------------------------------------------------------------- */ /* type conversion functions */ uint byteword(uchar a, uchar b){ return b << 8 | a; } uchar lsb(uint word){ return word &0xff; } uchar msb(uint word){ return word >> 8; } int CheckForCGAPCX(uchar *name) { FILE *fp; /* reads a ZSOFT .PCX header but ignores the color map */ int i; /* we only want CGA COLOR compatible full screens. */ uchar pcxheader[128]; uint zsoft,version,codetype,pixbits; uint xmin, ymin, xmax, ymax; uint x, y; uint no_planes, bytesperline; int status = VALID; /* read the file header */ if((fp = fopen(name, "rb")) == NULL)return INVALID; for(i = 0;i < 128;i++)pcxheader[i] = fgetc(fp); fclose(fp); zsoft = pcxheader[0]; version = pcxheader[1]; codetype = pcxheader[2]; pixbits = pcxheader[3]; if(zsoft != 10) status = INVALID; if(codetype != 1) status = INVALID; if(pixbits != 2) /* accept only CGA color images */ status = INVALID; /* monochrome images can't be mapped properly */ xmin = byteword(pcxheader[4], pcxheader[5]); ymin = byteword(pcxheader[6], pcxheader[7]); xmax = byteword(pcxheader[8], pcxheader[9]); ymax = byteword(pcxheader[10], pcxheader[11]); no_planes = pcxheader[65]; bytesperline = byteword(pcxheader[66], pcxheader[67]); x = xmax - xmin; y = ymax - ymin; if(x != XMAX)status = INVALID; if(y != YMAX)status = INVALID; if(no_planes != 1)status = INVALID; if(bytesperline != 80)status = INVALID; /* full screens only */ /* we can ignore the color map since we */ /* are limiting ourselves to CGA modes */ /* so we will not handle over 2-bits per pixel */ return status; } int BSAVE_Read(uchar *name) { int fh,fh2,y,i; uchar byte; uchar *crt; uchar namebuf[128]; uchar headbuf[7]; uchar linebuffer[80]; long target = 16384l,target2,header = 7; /* open the file twice,eliminate the interleaf,read contiguous */ sprintf(namebuf, "%s.BAS", name); if((fh = open(namebuf, O_RDONLY | O_BINARY)) == - 1)return INVALID; if((fh2 = open(namebuf, O_RDONLY | O_BINARY)) == - 1) { close(fh); return INVALID; } read(fh, headbuf, sizeof(BSAVED_header)); /* only read the first 3 bytes, some of these are a little */ /* different size, depending on how they were saved. */ for(i = 0;i < 3;i++) if(headbuf[i] != BSAVED_header[i]) { close(fh); close(fh2); return INVALID; } target2 = (target / 2) + header; lseek(fh, (long)(header), SEEK_SET) ; lseek(fh2, (long)(target2), SEEK_SET); crt = (uchar *)&rawbuffer[0]; /* translate from a 2 bit color pixel to an 8 bit color pixel */ for(y = 0;y < SCREENHEIGHT;y += 2) { read(fh, linebuffer, 80); for(i = 0;i < 80;i++) { byte = linebuffer[i]; *crt++ = GetMcgaPaletteIndex((byte >> 6)); *crt++ = GetMcgaPaletteIndex((byte >> 4)&3); *crt++ = GetMcgaPaletteIndex((byte >> 2)&3); *crt++ = GetMcgaPaletteIndex((byte)&3); } read(fh2, linebuffer, 80); for(i = 0;i < 80;i++) { byte = linebuffer[i]; *crt++ = GetMcgaPaletteIndex((byte >> 6)); *crt++ = GetMcgaPaletteIndex((byte >> 4)&3); *crt++ = GetMcgaPaletteIndex((byte >> 2)&3); *crt++ = GetMcgaPaletteIndex((byte)&3); } } close(fh); close(fh2); return SUCCESS; } /* Translation for VGA to display CGA */ int PCX_Read(uchar *pcxfilename) { uchar pcxheader[128]; uchar *crt; uint packet; uchar bit[4]; FILE *fp; uchar byte,bytecount; long wordcount,target; uchar name1[128], name2[128]; sprintf(name1, "%s.PCX", pcxfilename); if(CheckForCGAPCX(name1) == - 1)return INVALID; if (NULL == (fp = fopen(name1, "rb")))return INVALID; target = filelength(fileno(fp)); for(wordcount = 0;wordcount != 128;wordcount++)byte = fgetc(fp); crt = (uchar *)&rawbuffer[0]; do { bytecount = 1; /* start with a seed count */ byte = fgetc(fp); wordcount++; /* check to see if its raw */ if(0xC0 == (0xC0 &byte)) { /* if its not, run encoded */ bytecount = 0x3f &byte; byte = fgetc(fp); wordcount++; } /* translate from 2 bit pixel to 8 bit pixel */ bit[0] = GetMcgaPaletteIndex((byte >> 6)); bit[1] = GetMcgaPaletteIndex((byte >> 4)&3); bit[2] = GetMcgaPaletteIndex((byte >> 2)&3); bit[3] = GetMcgaPaletteIndex((byte)&3); packet = 0; while(packet++ < bytecount) { *crt++ = bit[0]; *crt++ = bit[1]; *crt++ = bit[2]; *crt++ = bit[3]; } }while(wordcount < target); fclose(fp); return(0); } int BMP16_Read(uchar *basename) { uint temp, temp2; uchar *screenbuffer, bmpfile[128]; int x, y; FILE *fp; sprintf(bmpfile,"%s.BMP",basename); fp=fopen(bmpfile,"rb"); if (NULL == fp) return INVALID; /* check the invariant fields in the bmp header */ for (x=0; x < sizeof(BMP_checkheader); x++) { temp2 = fgetc(fp); temp=BMP_checkheader[x]; if (temp != temp2) { fclose(fp); return INVALID; } } /* ignore variant fields in header - 8 bytes */ /* bmiHeader.biClrUsed and bmiHeader.biClrImportant */ for (x=0; x < 8; x++)fgetc(fp); for(y=0;y<NUM_VGA_COLORS;y++) { /* RGB Quad Structure b,g,r,0 */ for(x=3;x>0;x--) { temp = fgetc(fp); rgbOriginalArray[y][x-1] = temp; } fgetc(fp); } for(y=0;y<NUM_VGA_COLORS;y++) { // attempt to reorganize remapping of the BMP // to the apple lores palette based on rgb values x = GetVgaIndex(rgbOriginalArray[y][0], rgbOriginalArray[y][1], rgbOriginalArray[y][2]); // however, if all colors do not match then // we simply use the default color order // and let them manually select the apple lores color if (x == INVALID) { for (x = 0; x < NUM_VGA_COLORS; x++) VGAtoApple[x] = VGAtoAppleSaved[x]; // reset break; } /* move x to y for the remap of the image data */ /* if they don't like it they can toggle the colors */ VGAtoApple[y] = VGAtoAppleSaved[x]; } /* remap the image to the equivalent apple2 lores colors */ /* to the best known equivalents */ for (y = SCREENHEIGHT; y > 0; y--) { screenbuffer=(uchar *)&rawbuffer[((y-1)*SCREENWIDTH)]; for (x = 0; x < SCREENWIDTH; x++) { if (x % 2 == 0) { temp = fgetc(fp); temp2 = (temp >> 4); } else { temp2 = (temp & 15); } screenbuffer[x] = VGAtoApple[temp2]; } } fclose(fp); return SUCCESS; } /* routines to save to Apple 2 Lores Format */ /* base addresses for Apple II primary text page */ /* also the base addresses for the 48 scanline pairs */ /* for Apple II lores graphics mode 40 x 48 x 16 colors */ int textbase[24]={ 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780, 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8, 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0}; #define RAGWIDTH 80 #define RAGHEIGHT 48 #define RAGSIZE 1920 #define BINSIZE 1016 unsigned char lobuf[RAGSIZE]; /* sets the pixels in the lores buffer (lobuf) */ void setlopixel(uchar color,int x, int y,int ragflag) { unsigned char *crt, c1, c2; int y1, offset; y1 = y / 2; c2 = (unsigned char ) (color & 15); if (y%2 == 0) { /* even rows in low nibble */ /* mask value to preserve high nibble */ c1 = 240; } else { /* odd rows in high nibble */ /* mask value to preserve low nibble */ c1 = 15; c2 = c2 * 16; } if (ragflag) offset = (y1 * 80) + x; else offset = (textbase[y1]-1024)+x; crt = (unsigned char *)&lobuf[offset]; crt[0] &= c1; crt[0] |= c2; } /* save 2 output files */ int savelofragment(uchar *basename, int x1, int y1) { FILE *fp; uchar outfile[128], temp, remap; int x,y,x2,y2; sprintf(outfile,"%s.DLO",basename); fp = fopen(outfile,"wb"); if (NULL == fp)return INVALID; // On the double lo res display each byte in // high memory is interleaved with a byte in low memory // in the interests of efficiency I am saving and loading // the interleaf on a scanline by scanline basis. memset(lobuf,0,RAGSIZE); for (y = 0; y< 48; y++) { y2 = y + y1; // first 40 bytes goes to auxilliary memory (even pixels) for (x = 0; x < 40; x++) { x2 = (x*2) + x1; temp = getpixel(x2,y2); remap = PaletteIndex[temp]; setlopixel(remap,x,y,1); } // followed by the interleaf (odd pixels) // next 40 bytes goes to main memory for (x = 0; x < 40; x++) { x2 = (x*2) + x1 + 1; temp = getpixel(x2,y2); remap = PaletteIndex[temp]; setlopixel(remap,x+40,y,1); } } fputc(80,fp); // bytes fputc(24,fp); // bytes (rasters / 2) fwrite(lobuf,1,RAGSIZE,fp); fclose(fp); // the bsaved images are split into two files // the first file is loaded into aux mem sprintf(outfile,"%s.DL1",basename); fp = fopen(outfile,"wb"); if (NULL == fp)return INVALID; memset(lobuf,0,BINSIZE); for (y = 0; y< 48; y++) { y2 = y + y1; for (x = 0; x < 40; x++) { x2 = (x*2) + x1; temp = getpixel(x2,y2); remap = PaletteIndex[temp]; setlopixel(remap,x,y,0); } } fwrite(lobuf,1,BINSIZE,fp); fclose(fp); // the second file is loaded into main mem sprintf(outfile,"%s.DL2",basename); fp = fopen(outfile,"wb"); if (NULL == fp)return INVALID; memset(lobuf,0,BINSIZE); for (y = 0; y< 48; y++) { y2 = y + y1; for (x = 0; x < 40; x++) { x2 = (x*2) + x1 + 1; temp = getpixel(x2,y2); remap = PaletteIndex[temp]; setlopixel(remap,x,y,0); } } fwrite(lobuf,1,BINSIZE,fp); fclose(fp); return SUCCESS; } /* ------------------------------------------------------------------------ */ /* User Input and helper functions and Main Program */ /* ------------------------------------------------------------------------ */ // eat keystrokes... avoid mindlessly cycling through the program // if the user leans on the keyboard and doesn't budge for a moment. // Any DOS program should do this... int EatKeys() { if (kbhit()) while (kbhit()) if (getch() == FUNCKEY) getch(); return SUCCESS; } // show the title at startup and when 'H' (Help) is pressed. void ShowTitle() { int y, yorg, y1, x1, xx, c; uchar *ptr; for (y = 0; szTitle[y]!= NULL; y++); y+=2; yorg = y1 = ((SCREENHEIGHT - y*CELL_SIZE) / 2) - 1; for (y = 0; szTitle[y]!= NULL; y++) { ptr = (char *)&szTitle[y][0]; PCMidFont(ptr, XMOS, y1, 1, drawcolor, outlinecolor); y1+=CELL_SIZE; } PCMidFont(ptr, XMOS, y1, 1, drawcolor, outlinecolor); PCMidFont(ptr, XMOS, y1+CELL_SIZE, 1, drawcolor, outlinecolor); // a little finishing touch for the help screen // to show the currently selected colors. xx = XMOS-136; c = 1; PCMidFont("C", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("u", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("r", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("r", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("e", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("n", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("t", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont(" ", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("C", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("o", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("l", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("o", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("r", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("s", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont(":", (xx+=8), y1, 1, c, drawcolor); PCMidFont(" ", (xx+=8), y1, 1, c, drawcolor); c = 0; PCMidFont("0", (xx+=8), y1, 1, (c++), outlinecolor); PCMidFont("1", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("2", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("3", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("4", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("5", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("6", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("7", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("8", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("9", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("A", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("B", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("C", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("D", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("E", (xx+=8), y1, 1, (c++), drawcolor); PCMidFont("F", xx, y1, 1, c, drawcolor); y1+=CELL_SIZE; y1+=CELL_SIZE; x1 = strlen(ptr) * 4; LineBox(XMOS - x1 + 2, yorg+1, XMOS + x1 - 1, y1-2, drawcolor); EatKeys(); while (!kbhit()); EatKeys(); } void main(int argc, char **argv) { int status = 0, idx, iMax; /* bounds of lores image */ int oldx1=0, oldy1=0, oldx2=79, oldy2=47; int x1=0, y1=0, x2=79, y2=47; uchar c, kdx; uchar fname[128],sname[128],outfile[128]; uchar *wordptr; uchar scratchbuf[128]; FILE *fp; if(argc == 1) { puts(szTextTitle); puts("Command line Usage is \"BMP2LO MyCGA.PCX\""); puts(" \"BMP2LO MyBSAVE.BAS\""); puts(" \"BMP2LO My16Color.BMP\""); puts(" \"BMP2LO MyCGA.PCX OutfileBaseName\""); puts(" \"BMP2LO MyBSAVE.BAS OutfileBaseName\""); puts(" \"BMP2LO My16Color.BMP OutfileBaseName\""); printf("Enter Input FileName (Blank to Exit): "); gets(fname); if (fname[0] == ASCIIZ) exit(1); printf("Enter Output FileBaseName (Blank for None) : "); gets(outfile); } else { strcpy(fname, argv[1]); if (argc > 2) strcpy(outfile, argv[2]); else outfile[0] = ASCIIZ; } if((rawbuffer = malloc((unsigned)65000)) == NULL) { puts(szTextTitle); puts("Out of Memory..."); exit(1); } strcpy(sname, fname); wordptr = strtok(sname, "."); if (outfile[0] == ASCIIZ)strcpy(outfile,sname); status = PCX_Read(sname); if(status)status = BSAVE_Read(sname); if(status)status = BMP16_Read(sname); if (status) { puts(szTextTitle); printf("%s is an Unsupported Format or cannot be opened.\n", fname); free(rawbuffer); exit(1); } status = ESCKEY; if((SetCrtMode(MCGA)) == MCGA) { SetPalette(); LoadPalette(); vload(); ShowTitle(); vload(); xbox(oldx1, oldy1, oldx2, oldy2); do { c=toupper(getch()); if (FUNCKEY == c) { kdx=getch(); switch(kdx) { case HOMEKEY: x1 = 0; case LTARROW: x1-=1; x2-=1; if (x1 < 0) { x1 = 0; x2 = 79; } break; case ENDKEY: x2=319; case RTARROW: x1+=4; x2+=4; if (x2 > 319) { x2 = 319; x1 = 319 - 79; } break; case PGUP: y1 = 0; case UPARROW: y1-=1; y2-=1; if (y1 < 0) { y1 = 0; y2 = 47; } break; case PGDOWN: y2 = 199; case DOWNARROW: y1+=4; y2+=4; if (y2 > 199) { y2 = 199; y1 = 199 - 47; } break; default: break; } } else { if (c == 'S' || c == ENTERKEY) break; switch (c) { case 'H': xbox(oldx1, oldy1, oldx2, oldy2); ShowTitle(); vload(); xbox(oldx1, oldy1, oldx2, oldy2); break; case 'Q': c = ESCKEY; break; default: if (c < '0' || c > 'F') break; if (c > '9' && c < 'A') break; TogglePalette(c); } } if (x1!=oldx1 || y1!=oldy1) { /* erase old box */ xbox(oldx1, oldy1, oldx2, oldy2); /* update old cords with new cords */ oldx1 = x1; oldy1 = y1; oldx2 = x2; oldy2 = y2; /* plot new box */ xbox(oldx1, oldy1, oldx2, oldy2); } } while (c!=ESCKEY); if(c!=ESCKEY) status = savelofragment(outfile,x1,y1); else status = ESCKEY; SetCrtMode(TEXT); } if (status != ESCKEY) { if (status == SUCCESS)printf("%s.HL1 & .HL2 and %s.HLO Saved!\n",outfile,outfile); else printf("Error saving %s.HL1 & .HL2 and %s.HLO!\n",outfile,outfile); } else printf("Exiting without saving...\n"); free(rawbuffer); puts("Have a Nice Dos!"); exit(0); } |