#include "ztypes.h" #include "pb_console.h" #include #include #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 */