X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/d7482997dd1ca71b70df43c15dd5956f435a1a7e..8f664e7e91c918cd13248f6b684580c4dd2cdb31:/winhelp.c diff --git a/winhelp.c b/winhelp.c index 005409e..26c87f3 100644 --- a/winhelp.c +++ b/winhelp.c @@ -75,7 +75,7 @@ #include "winhelp.h" #include "tree234.h" -#ifdef TESTMODE +#ifdef WINHELP_TESTMODE /* * This lot is useful for testing. Something like it will also be * needed to use this module standalone. @@ -83,12 +83,13 @@ #define smalloc malloc #define srealloc realloc #define sfree free -#define mknew(type) ( (type *) smalloc (sizeof (type)) ) -#define mknewa(type, number) ( (type *) smalloc ((number) * sizeof (type)) ) -#define resize(array, len) ( srealloc ((array), (len) * sizeof (*(array))) ) +#define snew(type) ( (type *) smalloc (sizeof (type)) ) +#define snewn(number, type) ( (type *) smalloc ((number) * sizeof (type)) ) +#define sresize(array, len, type) \ + ( (type *) srealloc ((array), (len) * sizeof (type)) ) #define lenof(array) ( sizeof(array) / sizeof(*(array)) ) char *dupstr(char *s) { - char *r = mknewa(char, 1+strlen(s)); strcpy(r,s); return r; + char *r = snewn(1+strlen(s), char); strcpy(r,s); return r; } #endif @@ -182,6 +183,7 @@ struct WHLP_tag { int para_flags; int para_attrs[7]; int ncontexts; + int picture_index; }; /* Functions to return the index and leaf data for B-tree contents. */ @@ -195,6 +197,14 @@ static void whlp_file_add(struct file *f, const void *data, int len); static void whlp_file_add_char(struct file *f, int data); static void whlp_file_add_short(struct file *f, int data); static void whlp_file_add_long(struct file *f, int data); +static void whlp_file_add_cushort(struct file *f, int data); +#if 0 /* currently unused */ +static void whlp_file_add_csshort(struct file *f, int data); +#endif +static void whlp_file_add_culong(struct file *f, int data); +#if 0 /* currently unused */ +static void whlp_file_add_cslong(struct file *f, int data); +#endif static void whlp_file_fill(struct file *f, int len); static void whlp_file_seek(struct file *f, int pos, int whence); static int whlp_file_offset(struct file *f); @@ -375,10 +385,6 @@ static unsigned long context_hash(char *context) "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"; unsigned long hash; - /* Sanity check the size of unsigned long */ - enum { assertion = 1 / - (((unsigned long)0xFFFFFFFF) + 2 == (unsigned long)1) }; - /* * The hash algorithm starts the hash at 0 and updates it with * each character. Therefore, logically, the hash of an empty @@ -395,7 +401,38 @@ static unsigned long context_hash(char *context) */ hash = 0; while (*context) { - hash = hash * 43 + bytemapping[(unsigned char)*context]; + /* + * Be careful of overflowing `unsigned long', for maximum + * portability. + */ + + /* + * Multiply `hash' by 43. + */ + { + unsigned long bottom, top; + bottom = (hash & 0xFFFFUL) * 43; + top = ((hash >> 16) & 0xFFFFUL) * 43; + top += (bottom >> 16); + bottom &= 0xFFFFUL; + top &= 0xFFFFUL; + hash = (top << 16) | bottom; + } + + /* + * Add the mapping value for this byte to `hash'. + */ + { + int val = bytemapping[(unsigned char)*context]; + + if (val > 0 && hash > (0xFFFFFFFFUL - val)) { + hash -= (0xFFFFFFFFUL - val) + 1; + } else if (val < 0 && hash < (unsigned long)-val) { + hash += (0xFFFFFFFFUL + val) + 1; + } else + hash += val; + } + context++; } return hash; @@ -403,7 +440,7 @@ static unsigned long context_hash(char *context) WHLP_TOPIC whlp_register_topic(WHLP h, char *context_name, char **clash) { - context *ctx = mknew(context); + context *ctx = snew(context); context *otherctx; /* @@ -463,7 +500,7 @@ void whlp_prepare(WHLP h) while ( (ctx = index234(h->pre_contexts, 0)) != NULL ) { delpos234(h->pre_contexts, 0); - ctx->name = mknewa(char, 20); + ctx->name = snewn(20, char); do { sprintf(ctx->name, "t%08d", ctx_num++); ctx->hash = context_hash(ctx->name); @@ -485,7 +522,7 @@ char *whlp_topic_id(WHLP_TOPIC topic) void whlp_begin_topic(WHLP h, WHLP_TOPIC topic, char *title, ...) { - struct topiclink *link = mknew(struct topiclink); + struct topiclink *link = snew(struct topiclink); int len, slen; char *macro; va_list ap; @@ -501,7 +538,7 @@ void whlp_begin_topic(WHLP h, WHLP_TOPIC topic, char *title, ...) link->recordtype = 2; /* topic header */ link->len1 = 4*7; /* standard linkdata1 size */ - link->data1 = mknewa(unsigned char, link->len1); + link->data1 = snewn(link->len1, unsigned char); slen = strlen(title); assert(slen+1 <= TOPIC_BLKSIZE); @@ -519,7 +556,7 @@ void whlp_begin_topic(WHLP h, WHLP_TOPIC topic, char *title, ...) len--; /* lose the last \0 on the last macro */ link->len2 = len; - link->data2 = mknewa(unsigned char, link->len2); + link->data2 = snewn(link->len2, unsigned char); memcpy(link->data2, h->linkdata2, link->len2); topic->title = dupstr(title); @@ -552,7 +589,7 @@ void whlp_browse_link(WHLP h, WHLP_TOPIC before, WHLP_TOPIC after) static void whlp_linkdata(WHLP h, int which, int c) { int *len = (which == 1 ? &h->link->len1 : &h->link->len2); - char *data = (which == 1 ? h->linkdata1 : h->linkdata2); + unsigned char *data = (which == 1 ? h->linkdata1 : h->linkdata2); assert(*len < TOPIC_BLKSIZE); data[(*len)++] = c; } @@ -643,7 +680,7 @@ void whlp_set_tabstop(WHLP h, int tabstop, int alignment) if (alignment == WHLP_ALIGN_RIGHT) tabstop |= 0x10000; - p = mknew(int); + p = snew(int); *p = tabstop; add234(h->tabstops, p); h->para_flags |= 0x0200; @@ -651,7 +688,7 @@ void whlp_set_tabstop(WHLP h, int tabstop, int alignment) void whlp_begin_para(WHLP h, int para_type) { - struct topiclink *link = mknew(struct topiclink); + struct topiclink *link = snew(struct topiclink); int i; /* @@ -769,6 +806,94 @@ void whlp_tab(WHLP h) whlp_linkdata(h, 1, 0x83); } +int whlp_add_picture(WHLP h, int wd, int ht, const void *vpicdata, + const unsigned long *palette) +{ + struct file *f; + char filename[80]; + const unsigned char *picdata = (const unsigned char *)vpicdata; + int picstart, picoff, imgoff, imgstart; + int palettelen; + int i, index; + int wdrounded; + + /* + * Determine the limit of the colour palette. + */ + palettelen = -1; + for (i = 0; i < wd*ht; i++) + if (palettelen < picdata[i]) + palettelen = picdata[i]; + palettelen++; + + /* + * Round up the width to the next multiple of 4. + */ + wdrounded = (wd + 3) & ~3; + + index = h->picture_index++; + sprintf(filename, "bm%d", index); + + f = whlp_new_file(h, filename); + whlp_file_add_short(f, 0x706C); /* magic number */ + whlp_file_add_short(f, 1); /* number of pictures */ + picoff = whlp_file_offset(f); + whlp_file_add_long(f, 0); /* offset of first (only) picture */ + picstart = whlp_file_offset(f); + whlp_file_add_char(f, 6); /* DIB */ + whlp_file_add_char(f, 0); /* no packing */ + whlp_file_add_culong(f, 100); /* xdpi */ + whlp_file_add_culong(f, 100); /* ydpi */ + whlp_file_add_cushort(f, 1); /* planes (?) */ + whlp_file_add_cushort(f, 8); /* bitcount */ + whlp_file_add_culong(f, wd); /* width */ + whlp_file_add_culong(f, ht); /* height */ + whlp_file_add_culong(f, palettelen);/* colours used */ + whlp_file_add_culong(f, palettelen);/* colours important */ + whlp_file_add_culong(f, wdrounded*ht); /* `compressed' data size */ + whlp_file_add_culong(f, 0); /* hotspot size (no hotspots) */ + imgoff = whlp_file_offset(f); + whlp_file_add_long(f, 0); /* offset of `compressed' data */ + whlp_file_add_long(f, 0); /* offset of hotspot data (none) */ + for (i = 0; i < palettelen; i++) + whlp_file_add_long(f, palette[i]); + imgstart = whlp_file_offset(f); + /* + * Windows Help files, like BMP, start from the bottom scanline. + */ + for (i = ht; i-- > 0 ;) { + whlp_file_add(f, picdata + i*wd, wd); + if (wd < wdrounded) + whlp_file_add(f, "\0\0\0", wdrounded - wd); + } + + /* Now go back and fix up internal offsets */ + whlp_file_seek(f, picoff, 0); + whlp_file_add_long(f, picstart); + whlp_file_seek(f, imgoff, 0); + whlp_file_add_long(f, imgstart - picstart); + whlp_file_seek(f, 0, 2); + + return index; +} + +void whlp_ref_picture(WHLP h, int picid) +{ + /* + * Write a NUL into linkdata2. + */ + whlp_linkdata(h, 2, 0); + /* + * Write the formatting command and its followup data to + * specify a picture in a separate file. + */ + whlp_linkdata(h, 1, 0x86); + whlp_linkdata(h, 1, 3); /* type (picture without hotspots) */ + whlp_linkdata_cslong(h, 1, 4); + whlp_linkdata_short(h, 1, 0); + whlp_linkdata_short(h, 1, picid); +} + void whlp_text(WHLP h, char *text) { while (*text) { @@ -800,10 +925,10 @@ void whlp_end_para(WHLP h) whlp_linkdata_cslong(h, 1, data1cut); whlp_linkdata_cushort(h, 1, h->link->len2); - h->link->data1 = mknewa(unsigned char, h->link->len1); + h->link->data1 = snewn(h->link->len1, unsigned char); memcpy(h->link->data1, h->linkdata1 + data1cut, h->link->len1 - data1cut); memcpy(h->link->data1 + h->link->len1 - data1cut, h->linkdata1, data1cut); - h->link->data2 = mknewa(unsigned char, h->link->len2); + h->link->data2 = snewn(h->link->len2, unsigned char); memcpy(h->link->data2, h->linkdata2, h->link->len2); addpos234(h->text, h->link, count234(h->text)); @@ -870,12 +995,12 @@ static void whlp_topic_layout(WHLP h) /* * Create a final TOPICLINK containing no usable data. */ - link = mknew(struct topiclink); + link = snew(struct topiclink); link->nexttopic = NULL; if (h->prevtopic) h->prevtopic->nexttopic = link; h->prevtopic = link; - link->data1 = mknewa(unsigned char, 0x1c); + link->data1 = snewn(0x1c, unsigned char); link->block_size = 0; link->data2 = NULL; link->len1 = 0x1c; @@ -1043,7 +1168,7 @@ static void whlp_topic_layout(WHLP h) void whlp_index_term(WHLP h, char *index, WHLP_TOPIC topic) { - struct indexrec *idx = mknew(struct indexrec); + struct indexrec *idx = snew(struct indexrec); idx->term = dupstr(index); idx->topic = topic; @@ -1168,7 +1293,7 @@ int whlp_create_font(WHLP h, char *font, int family, int halfpoints, sfree(fontname); } - fontdesc = mknew(struct fontdesc); + fontdesc = snew(struct fontdesc); fontdesc->font = font; fontdesc->family = family; fontdesc->halfpoints = halfpoints; @@ -1247,7 +1372,7 @@ static void whlp_make_btree(struct file *f, int flags, int pagesize, int npages = 0, pagessize = 0; int npages_this_level, nentries, nlevels; int total_leaf_entries; - char btdata[MAX_PAGE_SIZE]; + unsigned char btdata[MAX_PAGE_SIZE]; int btlen; int page_start, fixups_offset, unused_bytes; void *element; @@ -1302,7 +1427,7 @@ static void whlp_make_btree(struct file *f, int flags, int pagesize, npages_this_level++; if (npages >= pagessize) { pagessize = npages + 32; - page_elements = resize(page_elements, pagessize); + page_elements = sresize(page_elements, pagessize, void *); } page_elements[npages++] = element; @@ -1388,7 +1513,7 @@ static void whlp_make_btree(struct file *f, int flags, int pagesize, npages_this_level++; if (npages >= pagessize) { pagessize = npages + 32; - page_elements = resize(page_elements, pagessize); + page_elements = sresize(page_elements, pagessize, void *); } page_elements[npages++] = page_elements[current]; @@ -1458,7 +1583,7 @@ static void whlp_make_btree(struct file *f, int flags, int pagesize, static struct file *whlp_new_file(WHLP h, char *name) { struct file *f; - f = mknew(struct file); + f = snew(struct file); f->data = NULL; f->pos = f->len = f->size = 0; if (name) { @@ -1481,7 +1606,7 @@ static void whlp_file_add(struct file *f, const void *data, int len) { if (f->pos + len > f->size) { f->size = f->pos + len + 1024; - f->data = resize(f->data, f->size); + f->data = sresize(f->data, f->size, unsigned char); } memcpy(f->data + f->pos, data, len); f->pos += len; @@ -1510,11 +1635,51 @@ static void whlp_file_add_long(struct file *f, int data) whlp_file_add(f, s, 4); } +static void whlp_file_add_cushort(struct file *f, int data) +{ + if (data <= 0x7F) { + whlp_file_add_char(f, data*2); + } else { + whlp_file_add_char(f, 1 + (data%128 * 2)); + whlp_file_add_char(f, data/128); + } +} + +#if 0 /* currently unused */ +static void whlp_file_add_csshort(struct file *f, int data) +{ + if (data >= -0x40 && data <= 0x3F) + whlp_file_add_cushort(f, data+64); + else + whlp_file_add_cushort(f, data+16384); +} +#endif + +static void whlp_file_add_culong(struct file *f, int data) +{ + if (data <= 0x7FFF) { + whlp_file_add_short(f, data*2); + } else { + whlp_file_add_short(f, 1 + (data%32768 * 2)); + whlp_file_add_short(f, data/32768); + } +} + +#if 0 /* currently unused */ +static void whlp_file_add_cslong(struct file *f, int data) +{ + if (data >= -0x4000 && data <= 0x3FFF) + whlp_file_add_culong(f, data+16384); + else + whlp_file_add_culong(f, data+67108864); +} +#endif + static void whlp_file_fill(struct file *f, int len) { if (f->pos + len > f->size) { f->size = f->pos + len + 1024; - f->data = resize(f->data, f->size); + f->data = sresize(f->data, f->size, unsigned char); } memset(f->data + f->pos, 0, len); f->pos += len; @@ -1541,7 +1706,7 @@ WHLP whlp_new(void) WHLP ret; struct file *f; - ret = mknew(struct WHLP_tag); + ret = snew(struct WHLP_tag); /* * Internal B-trees. @@ -1571,6 +1736,7 @@ WHLP whlp_new(void) ret->prevtopic = NULL; ret->ncontexts = 0; ret->link = NULL; + ret->picture_index = 0; return ret; } @@ -1789,9 +1955,15 @@ void whlp_abandon(WHLP h) sfree(h); } -#ifdef TESTMODE +#ifdef WINHELP_TESTMODE + +#ifdef PICTURE_FROM_CMDLINE +#include "png.h" +#include "colquant.h" +#include "dither.h" +#endif -int main(void) +int main(int argc, char **argv) { WHLP h; WHLP_TOPIC t1, t2, t3; @@ -2095,7 +2267,74 @@ int main(void) whlp_begin_para(h, WHLP_PARA_SCROLL); whlp_set_font(h, 1); - whlp_text(h, "This third topic is almost as boring as the first. Woo!"); + whlp_text(h, "This third topic is not nearly as boring as the first, " + "because it has a picture: "); + { +#ifndef PICTURE_FROM_CMDLINE + const unsigned long palette[] = { + 0xFF0000, + 0xFFFF00, + 0x00FF00, + 0x00FFFF, + 0x0000FF, + }; + const unsigned char picture[] = { + 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 1, 2, 3, 4, 4, + 0, 0, 0, 1, 2, 3, 4, 4, + 0, 0, 0, 1, 2, 3, 4, 4, + 0, 0, 1, 2, 3, 4, 4, 4, + 0, 0, 1, 2, 3, 4, 4, 4, + 0, 0, 1, 2, 3, 4, 4, 4, + 0, 1, 2, 3, 4, 4, 4, 4, + 0, 1, 2, 3, 4, 4, 4, 4, + 0, 1, 2, 3, 4, 4, 4, 4, + }; + int wid = 8, ht = 12; +#else + png_pixel ppalette[256]; + unsigned long palette[256]; + unsigned char *picture; + png *png; + colquant *cq; + int plen, i, err, wid, ht; + + if (argc < 2) { + fprintf(stderr, "in this mode I need a .png file on the" + " command line\n"); + return 1; + } + png = png_decode_file(argv[1], &err); + if (!png) { + fprintf(stderr, "%s: PNG read error: %s\n", argv[1], + png_error_msg[err]); + return 1; + } + + cq = colquant_new(256, 8); + colquant_data(cq, png->pixels, png->width * png->height); + plen = colquant_get_palette(cq, ppalette); + colquant_free(cq); + assert(plen <= 256); + for (i = 0; i < plen; i++) { + palette[i] = ppalette[i].r >> 8; + palette[i] <<= 8; + palette[i] |= ppalette[i].g >> 8; + palette[i] <<= 8; + palette[i] |= ppalette[i].b >> 8; + } + picture = malloc(png->width * png->height); + dither_image(png->width, png->height, png->pixels, + ppalette, plen, picture); + wid = png->width; + ht = png->height; + png_free(png); + +#endif + whlp_ref_picture(h, whlp_add_picture(h, wid, ht, picture, palette)); + } whlp_end_para(h); /*