#include "ztypes.h"
#include "pb_console.h"
#include <Quickdraw.h>
#include <QDOffscreen.h>
#define MAX_BIT 512 /* Must be less than or equal to CODE_TABLE_SIZE */
#define CODE_SIZE 8
#define CODE_TABLE_SIZE 4096
#define PREFIX 0
#define PIXEL 1
#define RED 0
#define GREEN 1
#define BLUE 2
#define CURRENT_VERSION "GIF87a"
#define HASH_SIZE 8192
#define HUFFMAN_FLAG 2 /* flags */
#define HUFFMAN_ONEDICT 4 /* flags: One huffman dictionary? Only Shogun has more */
#define TRANSPARENCY_FLAG 2 /* flags */
#define NOT_COLOR_DOUBLE 8 /* flags */
#define hashfunc(a, b) ((long) ((long) (a) + (long) (b)) % HASH_SIZE)
int unpack_image(unsigned char *in, unsigned char *out, int width, long numcodes, size_t booga);
void get_huffman_dictionary(void);
static short mask[16] = {
0x0000, 0x0001, 0x0003, 0x0007,
0x000f, 0x001f, 0x003f, 0x007f,
0x00ff, 0x01ff, 0x03ff, 0x07ff,
0x0fff, 0x1fff, 0x3fff, 0x7fff
};
typedef struct image_s {
short width;
short height;
short colours;
long pixels;
unsigned char *image;
unsigned char (*colourmap)[3];
} image_t;
typedef struct compress_s {
short next_code;
short slen;
short sptr;
short tlen;
short tptr;
} compress_t;
typedef struct nlist_s {
struct nlist *next;
short prefix;
short pixel;
short code;
} nlist_t;
typedef struct header_s {
unsigned char part;
unsigned char flags;
unsigned short unknown1;
unsigned short images;
unsigned short unknown2;
unsigned char dir_size;
unsigned char unknown3;
unsigned short checksum;
unsigned short unknown4;
unsigned short version;
} header_t;
typedef unsigned char color_t[3];
typedef struct colormap_s {
unsigned char num_colors;
color_t table[1];
} colormap_t;
typedef struct pdirectory_s {
short image_number;
short image_width;
short image_height;
short image_flags;
long image_data_addr;
long image_cm_addr;
short huffman_dict_addr;
} pdirectory_t;
static colormap_t *get_color_table(FILE *fp, pdirectory_t *directory);
static void *get_image (FILE *, pdirectory_t *);
static void decompress_image (FILE *, image_t *);
static short read_code (FILE *, compress_t *);
static header_t picfile_header;
static int max_pic_index;
static int *pic_index;
static pdirectory_t *picture_directory = NULL;
static short code_table[CODE_TABLE_SIZE][2];
static unsigned char buffer[CODE_TABLE_SIZE];
static unsigned char code_buffer[CODE_TABLE_SIZE];
static signed short *huffman_dictionary = NULL;
static FILE *picfile;
#if defined(__STDC__)
static unsigned char read_byte (FILE *fp)
#else
static unsigned char read_byte (fp)
FILE *fp;
#endif
{
int c;
if ((c = fgetc (fp)) == EOF) {
perror ("fgetc");
exit (EXIT_FAILURE);
}
return ((unsigned char) c);
}/* read_byte */
#if defined(__STDC__)
static unsigned short read_word (FILE *fp)
#else
static unsigned short read_word (fp)
FILE *fp;
#endif
{
unsigned short w;
w = (unsigned short) read_byte (fp) << 8;
w += (unsigned short) read_byte (fp) ;
return (w);
}/* read_word */
#if defined(__STDC__)
int read_picture_data (void)
#else
int read_picture_data ()
#endif
{
int i;
char linebuf[80];
FILE *fp;
long dir_pos;
if ((fp = fopen ("CPIC.DATA", "rb")) == NULL) {
fatal ("Couldn't open picture file");
}
picfile_header.part = read_byte (fp);
picfile_header.flags = read_byte (fp);
picfile_header.unknown1 = read_word (fp);
picfile_header.images = read_word (fp);
picfile_header.unknown2 = read_word (fp);
picfile_header.dir_size = read_byte (fp);
picfile_header.unknown3 = read_byte (fp);
picfile_header.checksum = read_word (fp);
picfile_header.unknown4 = read_word (fp);
picfile_header.version = read_word (fp);
sprintf (linebuf, "Total number of images = %d", (int) picfile_header.images);
#ifdef V6_DEBUG
output_string(linebuf);
new_line();
#endif
if ((picture_directory = (pdirectory_t *) NewPtr (picfile_header.images * sizeof (pdirectory_t))) == NULL) {
fatal("Insufficient memory\n");
}
dir_pos = ftell(fp);
max_pic_index = 0;
for (i = 0; (unsigned int) i < picfile_header.images; i++) {
picture_directory[i].image_number = read_word (fp);
if (picture_directory[i].image_number > max_pic_index) {
max_pic_index = picture_directory[i].image_number;
}
picture_directory[i].image_width = read_word (fp);
picture_directory[i].image_height = read_word (fp);
picture_directory[i].image_flags = read_word (fp);
picture_directory[i].image_data_addr = (unsigned long) read_byte (fp) << 16;
picture_directory[i].image_data_addr += (unsigned long) read_byte (fp) << 8;
picture_directory[i].image_data_addr += (unsigned long) read_byte (fp);
if (picfile_header.dir_size >= 14) {
picture_directory[i].image_cm_addr = (unsigned long) read_byte (fp) << 16;
picture_directory[i].image_cm_addr += (unsigned long) read_byte (fp) << 8;
picture_directory[i].image_cm_addr += (unsigned long) read_byte (fp);
} else {
picture_directory[i].image_cm_addr = 0;
/* (void) read_byte (fp); */
}
if (picfile_header.dir_size >= 16) {
picture_directory[i].huffman_dict_addr = read_word(fp);
}
fseek(fp,dir_pos += picfile_header.dir_size,SEEK_SET);
}
picfile = fp;
if (picfile_header.flags & HUFFMAN_ONEDICT)
get_huffman_dictionary();
pic_index = (void *)NewPtr((max_pic_index + 1) * sizeof(int));
memset(pic_index, 0xFF, (max_pic_index + 1) * sizeof(int));
for (i = 0; (unsigned int) i < picfile_header.images; i++) {
pic_index[picture_directory[i].image_number] = i;
}
return (0);
}/* read_picture_data */
void get_huffman_dictionary()
{
unsigned long fpos;
int maxentry, curentry;
short entry;
short *entryptr;
fpos = ftell(picfile);
maxentry = 1;
curentry = 0;
while (curentry++ <= maxentry) {
entry = (signed char)read_byte(picfile);
if (entry > 0) {
maxentry = MAX(maxentry,entry + entry + 1);
}
}
fseek(picfile, fpos, SEEK_SET);
if (huffman_dictionary)
DisposPtr((Ptr)huffman_dictionary);
entryptr = huffman_dictionary = (void *)NewPtr(sizeof(short) * (maxentry+1));
while (maxentry-- >= 0) {
*entryptr++ = (signed char)read_byte(picfile);
}
}
get_num_pictures(void)
{
return max_pic_index;
} /* get_num_pictures */
int get_picture_size(zword_t picture_number, zword_t *w, zword_t *h)
{
int picture_sequence;
if (picture_directory == NULL)
read_picture_data();
if ((picture_number <= max_pic_index) &&
((picture_sequence = pic_index[picture_number]) != -1)) {
if (picture_directory[picture_sequence].image_number != picture_number)
DebugStr("\pMisindexed pictures");
*w = picture_directory[picture_sequence].image_width;
*h = picture_directory[picture_sequence].image_height;
if (!(picture_directory[picture_sequence].image_flags&NOT_COLOR_DOUBLE)) {
*w <<= 1;
*h <<= 1;
}
}
else {
return FALSE;
}
return TRUE;
} /* get_picture_size */
RgnHandle pixmaptorgn(unsigned char *pixmap, int width, int height)
/* constructs a region containing the non-zero areas in the pixmap */
/* The region originates at 0,0 */
{
unsigned char *row;
unsigned char *rowflags;
unsigned char current = 0;
unsigned char row_num_flag;
unsigned char emptyflag = 1;
RgnHandle outputrgn;
RgnPtr rgnptr;
unsigned short *curptr;
int i,j;
int rgnleft, rgnright, rgntop, rgnbottom;
#define OUTPUT(w) { \
if (((unsigned long)curptr - (unsigned long)rgnptr) == rgnptr->rgnSize) { \
HUnlock((Handle)outputrgn); \
SetHandleSize((Handle)outputrgn, rgnptr->rgnSize + 1024L); \
HLock((Handle)outputrgn); \
rgnptr = *outputrgn; \
curptr = (unsigned short *)((unsigned long)rgnptr + rgnptr->rgnSize); \
rgnptr->rgnSize += 1024L; \
} \
*curptr++ = w; \
}
row = pixmap;
rowflags = (unsigned char *)NewPtrClear((size_t)width + 1);
outputrgn = (RgnHandle)NewHandle(sizeof(Region));
HLock((Handle)outputrgn);
rgnptr = *outputrgn;
rgnptr->rgnBBox.top = 0;
rgnptr->rgnBBox.left = 0;
rgnptr->rgnBBox.bottom = height;
rgnptr->rgnBBox.right = width;
rgnleft = width;
rgnright = 0;
rgntop = height;
rgnbottom = 0;
rgnptr->rgnSize = sizeof(Region);
curptr = (unsigned short *)(rgnptr+1);
for (j = 0; j <= height; j++) {
row_num_flag = 0;
for (i = 0; i <= width; i++) {
if (rowflags[i])
current = !current;
if ((((j == height) || (i == width))?0:(row[i]!=0)) != (current != 0)) {
if (!row_num_flag) {
OUTPUT(j);
row_num_flag = 1;
emptyflag = 0;
}
OUTPUT(i);
if (i > rgnright) rgnright = i;
if (i < rgnleft) rgnleft = i;
if (j < rgntop) rgntop = j;
if (j > rgnbottom) rgnbottom = j;
current = !current;
rowflags[i] = !rowflags[i];
}
}
if (row_num_flag)
OUTPUT(0x7FFF);
row += width;
}
if (!emptyflag) {
OUTPUT(0x7FFF);
rgnptr->rgnBBox.top = rgntop;
rgnptr->rgnBBox.left = rgnleft;
rgnptr->rgnBBox.bottom = rgnbottom;
rgnptr->rgnBBox.right = rgnright;
}
DisposPtr((Ptr)rowflags);
rgnptr->rgnSize = ((unsigned long)curptr - (unsigned long)rgnptr);
SetHandleSize((Handle)outputrgn, (size_t)rgnptr->rgnSize);
HUnlock((Handle)outputrgn);
return outputrgn;
#undef OUTPUT
}
void os_draw_picture(x, y, n, reverse)
int x,y,n;
{
zword_t w,h;
int i,j,err;
Rect r;
BitMap bm;
GrafPtr saveport;
PenState p;
RgnHandle rgn;
RGBColor saved_fg, saved_bg;
PixMapHandle mypmh;
unsigned char *mypixels, *ipixels, *image;
unsigned long rowbytes;
GWorldPtr mygworld;
CTabHandle myctab;
colormap_t *colormap;
RgnHandle maskrgn;
w = picture_directory[pic_index[n]].image_width;
h = picture_directory[pic_index[n]].image_height;
r.left = x - 1 ;
r.right = x - 1 + w;
r.top = y - 1;
r.bottom = y - 1 + h;
GetPort(&saveport);
SetPort(FrontWindow());
bm.bounds = r;
ipixels = image = get_image(picfile, &picture_directory[pic_index[n]]);
bm.rowBytes = ((w+15)>>3)&~1;
#if 1
maskrgn = pixmaptorgn(ipixels, w, h);
#else
maskrgn = NewRgn();
SetRectRgn(maskrgn, 0, 0, w, h);
#endif
OffsetRgn(maskrgn, r.left, r.top);
if (picfile_header.flags&NOT_COLOR_DOUBLE){
bm.baseAddr = (void *)NewPtrClear(bm.rowBytes * (long)h);
for (i = 0, mypixels = (unsigned char *)bm.baseAddr; i < h; i++, ipixels+=w, mypixels+=bm.rowBytes) {
for (j = 0; j < w; j++)
mypixels[j/8] |= (ipixels[j]&1) << (7-(j&7));
}
}
else {
colormap = get_color_table(picfile, &picture_directory[pic_index[n]]);
if (colormap != NULL) {
myctab = (CTabHandle)NewHandleClear(sizeof(ColorTable) + sizeof(CSpecArray)*(colormap->num_colors+1));
(**myctab).ctSeed = GetCTSeed();
(**myctab).ctSize = colormap->num_colors + 1;
(**myctab).ctFlags |= 0x8000;
(**myctab).ctTable[0].rgb.red = 0xFFFF;
(**myctab).ctTable[0].rgb.green = 0xFFFF;
(**myctab).ctTable[0].rgb.blue = 0xFFFF;
(**myctab).ctTable[1].rgb.red = 0;
(**myctab).ctTable[1].rgb.green = 0;
(**myctab).ctTable[1].rgb.blue = 0;
for (i = 2; i < (colormap->num_colors + 2); i++) {
(**myctab).ctTable[i].rgb.red = colormap->table[i-2][RED]<<8;
(**myctab).ctTable[i].rgb.green = colormap->table[i-2][GREEN]<<8;
(**myctab).ctTable[i].rgb.blue = colormap->table[i-2][BLUE]<<8;
}
DisposPtr((Ptr)colormap);
}
else {
myctab = NULL;
}
err = NewGWorld(&mygworld, 8, &r, myctab /*CTable*/,
/*GDevice*/NULL, keepLocal|useTempMem);
DisposHandle((Handle)myctab);
mypmh = GetGWorldPixMap(mygworld);
LockPixels(mypmh);
rowbytes = (**mypmh).rowBytes&0x3FFF;
mypixels = (unsigned char *)GetPixBaseAddr(mypmh);
for (i = 0; i < h; i++, ipixels+=w, mypixels+=rowbytes) {
memcpy(mypixels, ipixels, w);
}
}
DisposPtr((Ptr)image);
get_picture_size(n, &w, &h); /* this takes any doubling into account. Who made up this stuff? */
r.left = x - 1 ;
r.right = x - 1 + w;
r.top = y - 1;
r.bottom = y - 1 + h;
GetForeColor(&saved_fg);
GetBackColor(&saved_bg);
ForeColor(blackColor);
BackColor(whiteColor);
OffsetRect(&r, BORDER, BORDER);
MapRgn(maskrgn, &bm.bounds, &r);
if (picfile_header.flags&NOT_COLOR_DOUBLE){
CopyBits(&bm, &qd.thePort->portBits, &bm.bounds, &r, srcCopy, maskrgn);
}
else {
CopyBits((BitMap *)*mypmh, &qd.thePort->portBits, &bm.bounds, &r, srcCopy, maskrgn);
}
ValidRect(&r);
#if 0
GetPenState(&p);
PenMode(patCopy);
rgn = NewRgn();
RectRgn(rgn, &r);
FillRgn(rgn, qd.gray);
FrameRgn(rgn);
DisposeRgn(rgn);
SetPenState(&p);
#endif
RGBForeColor(&saved_fg);
RGBBackColor(&saved_bg);
DisposeHandle((Handle)maskrgn);
if (picfile_header.flags&NOT_COLOR_DOUBLE) {
DisposPtr(bm.baseAddr);
}
else {
UnlockPixels(mypmh);
DisposeGWorld(mygworld);
}
SetPort(saveport);
}
void os_erase_picture(x, y, n)
int x,y,n;
{
zword_t w,h;
Rect r;
GrafPtr saveport;
PenState p;
get_picture_size(n, &w, &h);
r.left = x - 1 ;
r.right = x - 1 + w;
r.top = y - 1;
r.bottom = y - 1 + h;
GetPort(&saveport);
SetPort(FrontWindow());
GetPenState(&p);
PenMode(patCopy);
OffsetRect(&r, BORDER, BORDER);
EraseRect(&r);
SetPenState(&p);
SetPort(saveport);
}
#if defined(__STDC__)
static colormap_t *get_color_table(FILE *fp, pdirectory_t *directory)
#else
static colormap_t *get_color_table (fp, directory)
FILE *fp;
pdirectory_t *directory;
#endif
{
colormap_t *result = NULL;
unsigned char num_colors;
int i;
static unsigned char last_colourmap[16][3] = {
0, 0, 0,
0, 0,170,
0,170, 0,
0,170,170,
170, 0, 0,
170, 0,170,
170,170, 0,
170,170,170,
85, 85, 85,
85, 85,255,
85,255, 85,
85,255,255,
255, 85, 85,
255, 85,255,
255,255, 85,
255,255,255
};
num_colors=0;
if (directory->image_cm_addr) {
if (fseek (fp, directory->image_cm_addr, SEEK_SET) != 0) {
perror ("fseek");
exit (EXIT_FAILURE);
}
num_colors = read_byte (fp);
}
result = (void *)NewPtr(sizeof(colormap_t) + (sizeof(color_t) * ((short)MAX(num_colors,16) - 1)));
for (i = 0; i < num_colors; i++) {
result->table[i][RED] = read_byte (fp);
result->table[i][GREEN] = read_byte (fp);
result->table[i][BLUE] = read_byte (fp);
if (i < 16) {
last_colourmap[i][RED] = result->table[i][RED];
last_colourmap[i][GREEN] = result->table[i][GREEN];
last_colourmap[i][BLUE] = result->table[i][BLUE];
}
}
for (i = num_colors; i < 16; i++) {
result->table[i][RED] = last_colourmap[i][RED];
result->table[i][GREEN] = last_colourmap[i][GREEN];
result->table[i][BLUE] = last_colourmap[i][BLUE];
}
result->num_colors = (num_colors<16)?16:num_colors;
return result;
}
#if defined(__STDC__)
static void *get_image (FILE *fp, pdirectory_t *directory)
#else
static void *get_image (fp, directory)
FILE *fp;
pdirectory_t *directory;
#endif
{
int colours = 18, i;
image_t image;
unsigned long image_compsize, image_numcodes;
char *comp_image;
#if 0
for (i = 0; i < 32; i++) {
colourmap[i][RED] = ega_colourmap[i][RED];
colourmap[i][GREEN] = ega_colourmap[i][GREEN];
colourmap[i][BLUE] = ega_colourmap[i][BLUE];
}
if (directory->image_cm_addr) {
if (fseek (fp, directory->image_cm_addr, SEEK_SET) != 0) {
perror ("fseek");
exit (EXIT_FAILURE);
}
colours = read_byte (fp);
read_bytes (fp, colours * 3, &colourmap[2][RED]);
colours += 2;
}
#endif
/* fprintf (stderr, "Number = %5d, width = %5d, height = %5d, flags = %4x, colourmap = %6ld, data = %6ld, colours = %2d\n",
(int) directory->image_number, (int) directory->image_width, (int) directory->image_height,
(int) directory->image_flags, directory->image_cm_addr, directory->image_data_addr, colours);
*/
if (directory->image_data_addr == 0)
return;
image.width = directory->image_width;
image.height = directory->image_height;
image.colours = colours;
image.pixels = 0;
if ((image.image = (unsigned char *) NewPtr ((size_t)directory->image_width * directory->image_height)) == NULL) {
fprintf (stderr, "Insufficient memory\n");
exit (EXIT_FAILURE);
}
#if 0
image.colourmap = colourmap;
#endif
if (directory->image_flags & HUFFMAN_FLAG) {
if (!(picfile_header.flags & HUFFMAN_ONEDICT)) {
if (fseek (fp, 2L * directory->huffman_dict_addr, SEEK_SET) != 0) {
perror ("fseek");
exit (EXIT_FAILURE);
}
get_huffman_dictionary();
}
if (fseek (fp, directory->image_data_addr, SEEK_SET) != 0) {
perror ("fseek");
exit (EXIT_FAILURE);
}
if (fseek (fp, directory->image_data_addr, SEEK_SET) != 0) {
perror ("fseek");
exit (EXIT_FAILURE);
}
image_compsize = 0;
image_numcodes = 0;
fread((char *)&image_compsize + 1, 3, 1, fp);
fread((char *)&image_numcodes + 1, 3, 1, fp);
comp_image = (void *)NewPtr(image_compsize);
fread((char *)comp_image, 1, image_compsize, fp);
unpack_image((unsigned char *)comp_image, (unsigned char *)image.image,
directory->image_width, image_numcodes, (size_t)directory->image_width * directory->image_height);
/* decompress_image (fp, &image);*/
DisposPtr((Ptr)comp_image);
}
return (image.image);
}/* get image */
#define GETBIT(b,p) ((((unsigned char *)(p))[(b)>>3])&(1<<(7-((b)&7))))
unpack_image(unsigned char *in, unsigned char *out, int width, long numcodes, size_t booga)
{
unsigned long bit=0;
signed char entry, temp;
signed char state = 0;
char *lines, *lastptr, *curptr;
lines = (void *)NewPtrClear(2 * width);
lastptr = lines;
curptr = lines+width;
entry = 0;
do {
if (GETBIT(bit, in))
entry++;
entry = huffman_dictionary[((unsigned char)entry)];
if (entry >= 0) {
entry += entry;
}
else {
entry -= 0x90;
if (entry < 0) {
entry += 0x10;
/* here's the wierd XOR with entry */
temp = entry ^ *lastptr++;
*out++ = temp;
*curptr++ = temp;
if (lastptr == lines+width) {
lastptr = lines;
curptr = lines+width;
memcpy(lastptr, curptr, width);
}
state = entry;
}
else {
do {
/* here's the wierd XOR with state */
temp = state ^ *lastptr++;
*curptr++ = temp;
*out++ = temp;
if (lastptr == lines+width) {
lastptr = lines;
curptr = lines+width;
memcpy(lastptr, curptr, width);
}
} while (--entry != -1);
}
entry = 0;
numcodes--;
}
bit++;
}
while (numcodes > 0);
DisposPtr((Ptr)lines);
}
#if defined(__STDC__)
static void decompress_image (FILE *fp, image_t *image)
#else
static void decompress_image (fp, image)
FILE *fp;
image_t *image;
#endif
{
int i;
short code, old = 0, first, clear_code;
compress_t comp;
clear_code = 1 << CODE_SIZE;
comp.next_code = clear_code + 2;
comp.slen = 0;
comp.sptr = 0;
comp.tlen = CODE_SIZE + 1;
comp.tptr = 0;
for (i = 0; i < CODE_TABLE_SIZE; i++) {
code_table[i][PREFIX] = CODE_TABLE_SIZE;
code_table[i][PIXEL] = i;
}
for (;;) {
if ((code = read_code (fp, &comp)) == (clear_code + 1))
return;
if (code == clear_code) {
comp.tlen = CODE_SIZE + 1;
comp.next_code = clear_code + 2;
code = read_code (fp, &comp);
} else {
first = (code == comp.next_code) ? old : code;
while (code_table[first][PREFIX] != CODE_TABLE_SIZE)
first = code_table[first][PREFIX];
code_table[comp.next_code][PREFIX] = old;
code_table[comp.next_code++][PIXEL] = code_table[first][PIXEL];
}
old = code;
i = 0;
do
buffer[i++] = (unsigned char) code_table[code][PIXEL];
while ((code = code_table[code][PREFIX]) != CODE_TABLE_SIZE);
do
image->image[image->pixels++] = buffer[--i];
while (i > 0);
}
}/* decompress_image */
#if defined(__STDC__)
static short read_code (FILE *fp, compress_t *comp)
#else
static short read_code (fp, comp)
FILE *fp;
compress_t *comp;
#endif
{
short code, bsize, tlen, tptr;
int i;
short sw;
code = 0;
tlen = comp->tlen;
tptr = 0;
while (tlen) {
if (comp->slen == 0) {
if ((comp->slen = fread (code_buffer, 1, MAX_BIT, fp)) == 0) {
perror ("fread");
exit (EXIT_FAILURE);
}
comp->slen *= 8;
comp->sptr = 0;
}
bsize = ((comp->sptr + 8) & ~7) - comp->sptr;
bsize = (tlen > bsize) ? bsize : tlen;
code |= ((code_buffer[comp->sptr >> 3] >> (comp->sptr & 7)) & mask[bsize]) << tptr;
tlen -= bsize;
tptr += bsize;
comp->slen -= bsize;
comp->sptr += bsize;
}
if ((comp->next_code == mask[comp->tlen]) && (comp->tlen < 12))
comp->tlen++;
return (code);
}/* read_code */