/* ------------------------------------------------------------------------ */
/* 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);
}
|