4 * Copyright 1996 Ulrich Schmid
6 * 2007 Kirill K. Smirnov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
37 static inline unsigned short GET_USHORT(const BYTE* buffer, unsigned i)
39 return (BYTE)buffer[i] + 0x100 * (BYTE)buffer[i + 1];
42 static inline short GET_SHORT(const BYTE* buffer, unsigned i)
44 return (BYTE)buffer[i] + 0x100 * (signed char)buffer[i+1];
47 static inline unsigned GET_UINT(const BYTE* buffer, unsigned i)
49 return GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i + 2);
52 static HLPFILE *first_hlpfile = 0;
53 static BYTE *file_buffer;
54 static UINT file_buffer_size;
79 static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR);
80 static BOOL HLPFILE_ReadFileToBuffer(HFILE);
81 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**);
82 static BOOL HLPFILE_SystemCommands(HLPFILE*);
83 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end);
84 static BYTE* HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr);
85 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE*);
86 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE*);
87 static BOOL HLPFILE_Uncompress_Topic(HLPFILE*);
88 static BOOL HLPFILE_GetContext(HLPFILE*);
89 static BOOL HLPFILE_GetKeywords(HLPFILE*);
90 static BOOL HLPFILE_GetMap(HLPFILE*);
91 static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*, unsigned);
92 static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*, unsigned*);
93 static void HLPFILE_Uncompress2(const BYTE*, const BYTE*, BYTE*, const BYTE*);
94 static BOOL HLPFILE_Uncompress3(char*, const char*, const BYTE*, const BYTE*);
95 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz);
96 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile);
98 /***********************************************************************
100 * HLPFILE_PageByNumber
102 static HLPFILE_PAGE *HLPFILE_PageByNumber(HLPFILE* hlpfile, UINT wNum)
107 WINE_TRACE("<%s>[%u]\n", hlpfile->lpszPath, wNum);
109 for (page = hlpfile->first_page; page && temp; page = page->next) temp--;
111 WINE_ERR("Page of number %u not found in file %s\n", wNum, hlpfile->lpszPath);
116 * this finds the page containing the offset. The offset can either
117 * refer to the top of the page (offset == page->offset), or
118 * to some paragraph inside the page...
119 * As of today, we only return the page... we should also return
120 * a paragraph, and then, while opening a new page, compute the
121 * y-offset of the paragraph to be shown and scroll the window
124 /******************************************************************
125 * HLPFILE_PageByOffset
129 HLPFILE_PAGE *HLPFILE_PageByOffset(HLPFILE* hlpfile, LONG offset)
134 if (!hlpfile) return 0;
136 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, offset);
138 if (offset == 0xFFFFFFFF) return NULL;
141 for (found = NULL, page = hlpfile->first_page; page; page = page->next)
143 if (page->offset <= offset && (!found || found->offset < page->offset))
147 WINE_ERR("Page of offset %u not found in file %s\n",
148 offset, hlpfile->lpszPath);
152 /**************************************************************************
155 * HLPFILE_BPTreeCompare function for '|CONTEXT' B+ tree file
158 static int comp_PageByHash(void *p, const void *key,
159 int leaf, void** next)
161 LONG lKey = (LONG)key;
162 LONG lTest = GET_UINT(p, 0);
164 *next = (char *)p+(leaf?8:6);
165 WINE_TRACE("Comparing '%u' with '%u'\n", lKey, lTest);
166 if (lTest < lKey) return -1;
167 if (lTest > lKey) return 1;
171 /***********************************************************************
173 * HLPFILE_HlpFilePageByHash
175 HLPFILE_PAGE *HLPFILE_PageByHash(HLPFILE* hlpfile, LONG lHash)
179 if (!hlpfile) return 0;
181 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lHash);
183 /* For win 3.0 files hash values are really page numbers */
184 if (hlpfile->version <= 16)
185 return HLPFILE_PageByNumber(hlpfile, lHash);
187 ptr = HLPFILE_BPTreeSearch(hlpfile->Context, (void*)lHash, comp_PageByHash);
190 WINE_ERR("Page of hash %x not found in file %s\n", lHash, hlpfile->lpszPath);
194 return HLPFILE_PageByOffset(hlpfile, GET_UINT(ptr, 4));
197 /***********************************************************************
201 HLPFILE_PAGE *HLPFILE_PageByMap(HLPFILE* hlpfile, LONG lMap)
205 if (!hlpfile) return 0;
207 WINE_TRACE("<%s>[%x]\n", hlpfile->lpszPath, lMap);
209 for (i = 0; i < hlpfile->wMapLen; i++)
211 if (hlpfile->Map[i].lMap == lMap)
212 return HLPFILE_PageByOffset(hlpfile, hlpfile->Map[i].offset);
215 WINE_ERR("Page of Map %x not found in file %s\n", lMap, hlpfile->lpszPath);
219 /***********************************************************************
223 HLPFILE_PAGE* HLPFILE_Contents(HLPFILE *hlpfile)
225 HLPFILE_PAGE* page = NULL;
227 if (!hlpfile) return NULL;
229 page = HLPFILE_PageByOffset(hlpfile, hlpfile->contents_start);
230 if (!page) page = hlpfile->first_page;
234 /***********************************************************************
238 LONG HLPFILE_Hash(LPCSTR lpszContext)
243 while ((c = *lpszContext++))
246 if (c >= 'A' && c <= 'Z') x = c - 'A' + 17;
247 if (c >= 'a' && c <= 'z') x = c - 'a' + 17;
248 if (c >= '1' && c <= '9') x = c - '0';
249 if (c == '0') x = 10;
250 if (c == '.') x = 12;
251 if (c == '_') x = 13;
252 if (x) lHash = lHash * 43 + x;
257 /***********************************************************************
259 * HLPFILE_ReadHlpFile
261 HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath)
265 for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next)
267 if (!strcmp(lpszPath, hlpfile->lpszPath))
269 hlpfile->wRefCount++;
274 hlpfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
275 sizeof(HLPFILE) + lstrlen(lpszPath) + 1);
276 if (!hlpfile) return 0;
278 hlpfile->lpszPath = (char*)hlpfile + sizeof(HLPFILE);
279 hlpfile->contents_start = 0xFFFFFFFF;
280 hlpfile->next = first_hlpfile;
281 hlpfile->wRefCount = 1;
283 strcpy(hlpfile->lpszPath, lpszPath);
285 first_hlpfile = hlpfile;
286 if (hlpfile->next) hlpfile->next->prev = hlpfile;
288 phrases.offsets = NULL;
289 phrases.buffer = NULL;
294 if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath))
296 HLPFILE_FreeHlpFile(hlpfile);
300 HeapFree(GetProcessHeap(), 0, phrases.offsets);
301 HeapFree(GetProcessHeap(), 0, phrases.buffer);
302 HeapFree(GetProcessHeap(), 0, topic.map);
303 HeapFree(GetProcessHeap(), 0, file_buffer);
308 /***********************************************************************
310 * HLPFILE_DoReadHlpFile
312 static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath)
319 unsigned index, old_index, offset, len, offs;
321 hFile = OpenFile(lpszPath, &ofs, OF_READ);
322 if (hFile == HFILE_ERROR) return FALSE;
324 ret = HLPFILE_ReadFileToBuffer(hFile);
326 if (!ret) return FALSE;
328 if (!HLPFILE_SystemCommands(hlpfile)) return FALSE;
330 /* load phrases support */
331 if (!HLPFILE_UncompressLZ77_Phrases(hlpfile))
332 HLPFILE_Uncompress_Phrases40(hlpfile);
334 if (!HLPFILE_Uncompress_Topic(hlpfile)) return FALSE;
335 if (!HLPFILE_ReadFont(hlpfile)) return FALSE;
344 if (hlpfile->version <= 16)
346 index = (ref - 0x0C) / hlpfile->dsize;
347 offset = (ref - 0x0C) % hlpfile->dsize;
351 index = (ref - 0x0C) >> 14;
352 offset = (ref - 0x0C) & 0x3FFF;
355 if (hlpfile->version <= 16 && index != old_index && index != 0)
357 /* we jumped to the next block, adjust pointers */
362 WINE_TRACE("ref=%08x => [%u/%u]\n", ref, index, offset);
364 if (index >= topic.wMapLen) {WINE_WARN("maplen\n"); break;}
365 buf = topic.map[index] + offset;
366 if (buf + 0x15 >= topic.end) {WINE_WARN("extra\n"); break;}
367 end = min(buf + GET_UINT(buf, 0), topic.end);
368 if (index != old_index) {offs = 0; old_index = index;}
373 if (!HLPFILE_AddPage(hlpfile, buf, end, index * 0x8000L + offs)) return FALSE;
379 if (!HLPFILE_AddParagraph(hlpfile, buf, end, &len)) return FALSE;
384 WINE_ERR("buf[0x14] = %x\n", buf[0x14]);
387 if (hlpfile->version <= 16)
389 ref += GET_UINT(buf, 0xc);
390 if (GET_UINT(buf, 0xc) == 0)
394 ref = GET_UINT(buf, 0xc);
395 } while (ref != 0xffffffff);
397 HLPFILE_GetKeywords(hlpfile);
398 HLPFILE_GetMap(hlpfile);
399 if (hlpfile->version <= 16) return TRUE;
400 return HLPFILE_GetContext(hlpfile);
403 /***********************************************************************
407 static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned offset)
411 UINT titlesize, blocksize, datalen;
415 blocksize = GET_UINT(buf, 0);
416 datalen = GET_UINT(buf, 0x10);
417 title = buf + datalen;
418 if (title > end) {WINE_WARN("page2\n"); return FALSE;};
420 titlesize = GET_UINT(buf, 4);
421 page = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_PAGE) + titlesize + 1);
422 if (!page) return FALSE;
423 page->lpszTitle = (char*)page + sizeof(HLPFILE_PAGE);
425 if (titlesize > blocksize - datalen)
427 /* need to decompress */
428 if (hlpfile->hasPhrases)
429 HLPFILE_Uncompress2(title, end, (BYTE*)page->lpszTitle, (BYTE*)page->lpszTitle + titlesize);
430 else if (hlpfile->hasPhrases40)
431 HLPFILE_Uncompress3(page->lpszTitle, page->lpszTitle + titlesize, title, end);
434 WINE_FIXME("Text size is too long, splitting\n");
435 titlesize = blocksize - datalen;
436 memcpy(page->lpszTitle, title, titlesize);
440 memcpy(page->lpszTitle, title, titlesize);
442 page->lpszTitle[titlesize] = '\0';
444 if (hlpfile->first_page)
446 hlpfile->last_page->next = page;
447 page->prev = hlpfile->last_page;
448 hlpfile->last_page = page;
452 hlpfile->first_page = page;
453 hlpfile->last_page = page;
457 page->file = hlpfile;
459 page->first_paragraph = NULL;
460 page->first_macro = NULL;
461 page->wNumber = GET_UINT(buf, 0x21);
462 page->offset = offset;
464 page->browse_bwd = GET_UINT(buf, 0x19);
465 page->browse_fwd = GET_UINT(buf, 0x1D);
467 WINE_TRACE("Added page[%d]: title='%s' %08x << %08x >> %08x\n",
468 page->wNumber, page->lpszTitle,
469 page->browse_bwd, page->offset, page->browse_fwd);
471 memset(&attributes, 0, sizeof(attributes));
473 /* now load macros */
474 ptr = page->lpszTitle + strlen(page->lpszTitle) + 1;
475 while (ptr < page->lpszTitle + titlesize)
477 unsigned len = strlen(ptr);
480 WINE_TRACE("macro: %s\n", ptr);
481 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + len + 1);
482 macro->lpszMacro = macro_str = (char*)(macro + 1);
483 memcpy(macro_str, ptr, len + 1);
484 /* FIXME: shall we really link macro in reverse order ??
485 * may produce strange results when played at page opening
487 macro->next = page->first_macro;
488 page->first_macro = macro;
495 static long fetch_long(BYTE** ptr)
501 ret = (*(unsigned long*)(*ptr) - 0x80000000L) / 2;
506 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
513 static unsigned long fetch_ulong(BYTE** ptr)
519 ret = *(unsigned long*)(*ptr) / 2;
524 ret = *(unsigned short*)(*ptr) / 2;
530 static short fetch_short(BYTE** ptr)
536 ret = (*(unsigned short*)(*ptr) - 0x8000) / 2;
541 ret = (*(unsigned char*)(*ptr) - 0x80) / 2;
547 static unsigned short fetch_ushort(BYTE** ptr)
553 ret = *(unsigned short*)(*ptr) / 2;
558 ret = *(unsigned char*)(*ptr) / 2;
564 /******************************************************************
565 * HLPFILE_DecompressGfx
567 * Decompress the data part of a bitmap or a metafile
569 static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE packing)
576 WINE_TRACE("Unpacking (%d) from %u bytes to %u bytes\n", packing, csz, sz);
580 case 0: /* uncompressed */
582 WINE_WARN("Bogus gfx sizes (uncompressed): %u / %u\n", sz, csz);
586 tmp = dst = HeapAlloc(GetProcessHeap(), 0, sz);
587 if (!dst) return NULL;
588 HLPFILE_UncompressRLE(src, src + csz, &tmp, sz);
590 WINE_WARN("Bogus gfx sizes (RunLen): %u/%u\n", tmp - dst, sz);
593 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
594 dst = HeapAlloc(GetProcessHeap(), 0, sz77);
595 if (!dst) return NULL;
596 HLPFILE_UncompressLZ77(src, src + csz, dst);
598 WINE_WARN("Bogus gfx sizes (LZ77): %u / %u\n", sz77, sz);
600 case 3: /* LZ77 then RLE */
601 sz77 = HLPFILE_UncompressedLZ77_Size(src, src + csz);
602 tmp = HeapAlloc(GetProcessHeap(), 0, sz77);
603 if (!tmp) return FALSE;
604 HLPFILE_UncompressLZ77(src, src + csz, tmp);
605 dst = tmp2 = HeapAlloc(GetProcessHeap(), 0, sz);
608 HeapFree(GetProcessHeap(), 0, tmp);
611 HLPFILE_UncompressRLE(tmp, tmp + sz77, &tmp2, sz);
612 if (tmp2 - dst != sz)
613 WINE_WARN("Bogus gfx sizes (LZ77+RunLen): %u / %u\n", tmp2 - dst, sz);
614 HeapFree(GetProcessHeap(), 0, tmp);
617 WINE_FIXME("Unsupported packing %u\n", packing);
623 /******************************************************************
628 static BOOL HLPFILE_LoadBitmap(BYTE* beg, BYTE type, BYTE pack,
629 HLPFILE_PARAGRAPH* paragraph)
634 unsigned long off, csz;
637 bi = HeapAlloc(GetProcessHeap(), 0, sizeof(*bi));
638 if (!bi) return FALSE;
640 ptr = beg + 2; /* for type and pack */
642 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
643 bi->bmiHeader.biXPelsPerMeter = fetch_ulong(&ptr);
644 bi->bmiHeader.biYPelsPerMeter = fetch_ulong(&ptr);
645 bi->bmiHeader.biPlanes = fetch_ushort(&ptr);
646 bi->bmiHeader.biBitCount = fetch_ushort(&ptr);
647 bi->bmiHeader.biWidth = fetch_ulong(&ptr);
648 bi->bmiHeader.biHeight = fetch_ulong(&ptr);
649 bi->bmiHeader.biClrUsed = fetch_ulong(&ptr);
650 bi->bmiHeader.biClrImportant = fetch_ulong(&ptr);
651 bi->bmiHeader.biCompression = BI_RGB;
652 if (bi->bmiHeader.biBitCount > 32) WINE_FIXME("Unknown bit count %u\n", bi->bmiHeader.biBitCount);
653 if (bi->bmiHeader.biPlanes != 1) WINE_FIXME("Unsupported planes %u\n", bi->bmiHeader.biPlanes);
654 bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight;
655 WINE_TRACE("planes=%d bc=%d size=(%d,%d)\n",
656 bi->bmiHeader.biPlanes, bi->bmiHeader.biBitCount,
657 bi->bmiHeader.biWidth, bi->bmiHeader.biHeight);
659 csz = fetch_ulong(&ptr);
660 fetch_ulong(&ptr); /* hotspot size */
662 off = GET_UINT(ptr, 0); ptr += 4;
663 /* GET_UINT(ptr, 0); hotspot offset */ ptr += 4;
665 /* now read palette info */
668 unsigned nc = bi->bmiHeader.biClrUsed;
671 /* not quite right, especially for bitfields type of compression */
672 if (!nc && bi->bmiHeader.biBitCount <= 8)
673 nc = 1 << bi->bmiHeader.biBitCount;
675 bi = HeapReAlloc(GetProcessHeap(), 0, bi, sizeof(*bi) + nc * sizeof(RGBQUAD));
676 if (!bi) return FALSE;
677 for (i = 0; i < nc; i++)
679 bi->bmiColors[i].rgbBlue = ptr[0];
680 bi->bmiColors[i].rgbGreen = ptr[1];
681 bi->bmiColors[i].rgbRed = ptr[2];
682 bi->bmiColors[i].rgbReserved = 0;
686 pict_beg = HLPFILE_DecompressGfx(beg + off, csz, bi->bmiHeader.biSizeImage, pack);
688 paragraph->u.gfx.u.bmp.hBitmap = CreateDIBitmap(hdc = GetDC(0), &bi->bmiHeader,
692 if (!paragraph->u.gfx.u.bmp.hBitmap)
693 WINE_ERR("Couldn't create bitmap\n");
695 HeapFree(GetProcessHeap(), 0, bi);
696 if (pict_beg != beg + off) HeapFree(GetProcessHeap(), 0, pict_beg);
701 /******************************************************************
702 * HLPFILE_LoadMetaFile
706 static BOOL HLPFILE_LoadMetaFile(BYTE* beg, BYTE pack, HLPFILE_PARAGRAPH* paragraph)
709 unsigned long size, csize;
710 unsigned long off, hsoff;
712 LPMETAFILEPICT lpmfp;
714 WINE_TRACE("Loading metafile\n");
716 ptr = beg + 2; /* for type and pack */
718 lpmfp = ¶graph->u.gfx.u.mfp;
719 lpmfp->mm = fetch_ushort(&ptr); /* mapping mode */
721 lpmfp->xExt = GET_USHORT(ptr, 0);
722 lpmfp->yExt = GET_USHORT(ptr, 2);
725 size = fetch_ulong(&ptr); /* decompressed size */
726 csize = fetch_ulong(&ptr); /* compressed size */
727 fetch_ulong(&ptr); /* hotspot size */
728 off = GET_UINT(ptr, 0);
729 hsoff = GET_UINT(ptr, 4);
732 WINE_TRACE("sz=%lu csz=%lu (%d,%d) offs=%lu/%u,%lu\n",
733 size, csize, lpmfp->xExt, lpmfp->yExt, off, ptr - beg, hsoff);
735 bits = HLPFILE_DecompressGfx(beg + off, csize, size, pack);
736 if (!bits) return FALSE;
738 paragraph->cookie = para_metafile;
740 lpmfp->hMF = SetMetaFileBitsEx(size, bits);
743 WINE_FIXME("Couldn't load metafile\n");
745 if (bits != beg + off) HeapFree(GetProcessHeap(), 0, bits);
750 /******************************************************************
751 * HLPFILE_LoadGfxByAddr
755 static BOOL HLPFILE_LoadGfxByAddr(HLPFILE *hlpfile, BYTE* ref,
757 HLPFILE_PARAGRAPH* paragraph)
761 numpict = GET_USHORT(ref, 2);
762 WINE_TRACE("Got picture magic=%04x #=%d\n",
763 GET_USHORT(ref, 0), numpict);
765 for (i = 0; i < numpict; i++)
771 WINE_TRACE("Offset[%d] = %x\n", i, GET_UINT(ref, (1 + i) * 4));
772 beg = ptr = ref + GET_UINT(ref, (1 + i) * 4);
779 case 5: /* device dependent bmp */
780 case 6: /* device independent bmp */
781 HLPFILE_LoadBitmap(beg, type, pack, paragraph);
784 HLPFILE_LoadMetaFile(beg, pack, paragraph);
786 default: WINE_FIXME("Unknown type %u\n", type); return FALSE;
789 /* FIXME: hotspots */
791 /* FIXME: implement support for multiple picture format */
792 if (numpict != 1) WINE_FIXME("Supporting only one bitmap format per logical bitmap (for now). Using first format\n");
798 /******************************************************************
799 * HLPFILE_LoadGfxByIndex
803 static BOOL HLPFILE_LoadGfxByIndex(HLPFILE *hlpfile, unsigned index,
804 HLPFILE_PARAGRAPH* paragraph)
810 WINE_TRACE("Loading picture #%d\n", index);
812 if (index < hlpfile->numBmps && hlpfile->bmps[index] != NULL)
814 paragraph->u.gfx.u.bmp.hBitmap = hlpfile->bmps[index];
818 sprintf(tmp, "|bm%u", index);
820 if (!HLPFILE_FindSubFile(tmp, &ref, &end)) {WINE_WARN("no sub file\n"); return FALSE;}
824 ret = HLPFILE_LoadGfxByAddr(hlpfile, ref, end - ref, paragraph);
827 if (ret && paragraph->cookie == para_bitmap)
829 if (index >= hlpfile->numBmps)
831 hlpfile->numBmps = index + 1;
833 hlpfile->bmps = HeapReAlloc(GetProcessHeap(), 0, hlpfile->bmps,
834 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
836 hlpfile->bmps = HeapAlloc(GetProcessHeap(), 0,
837 hlpfile->numBmps * sizeof(hlpfile->bmps[0]));
840 hlpfile->bmps[index] = paragraph->u.gfx.u.bmp.hBitmap;
845 /******************************************************************
850 static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG hash,
851 BOOL clrChange, unsigned wnd)
856 /* FIXME: should build a string table for the attributes.link.lpszPath
857 * they are reallocated for each link
859 link = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_LINK) + strlen(str) + 1);
860 if (!link) return NULL;
862 link->cookie = cookie;
863 link->lpszString = link_str = (char*)link + sizeof(HLPFILE_LINK);
864 strcpy(link_str, str);
866 link->bClrChange = clrChange ? 1 : 0;
870 WINE_TRACE("Link[%d] to %s@%08x:%d\n",
871 link->cookie, link->lpszString,
872 link->lHash, link->window);
876 /***********************************************************************
878 * HLPFILE_AddParagraph
880 static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end, unsigned* len)
883 HLPFILE_PARAGRAPH *paragraph, **paragraphptr;
885 BYTE *format, *format_end;
886 char *text, *text_base, *text_end;
887 long size, blocksize, datalen;
889 unsigned nc, ncol = 1;
891 if (!hlpfile->last_page) {WINE_WARN("no page\n"); return FALSE;};
892 page = hlpfile->last_page;
893 for (paragraphptr = &page->first_paragraph; *paragraphptr;
894 paragraphptr = &(*paragraphptr)->next) /* Nothing */;
896 if (buf + 0x19 > end) {WINE_WARN("header too small\n"); return FALSE;};
898 blocksize = GET_UINT(buf, 0);
899 size = GET_UINT(buf, 0x4);
900 datalen = GET_UINT(buf, 0x10);
901 text = text_base = HeapAlloc(GetProcessHeap(), 0, size);
902 if (!text) return FALSE;
903 if (size > blocksize - datalen)
905 /* need to decompress */
906 if (hlpfile->hasPhrases)
907 HLPFILE_Uncompress2(buf + datalen, end, (BYTE*)text, (BYTE*)text + size);
908 else if (hlpfile->hasPhrases40)
909 HLPFILE_Uncompress3(text, text + size, buf + datalen, end);
912 WINE_FIXME("Text size is too long, splitting\n");
913 size = blocksize - datalen;
914 memcpy(text, buf + datalen, size);
918 memcpy(text, buf + datalen, size);
920 text_end = text + size;
923 format_end = buf + GET_UINT(buf, 0x10);
925 if (buf[0x14] == 0x20 || buf[0x14] == 0x23)
928 *len = fetch_ushort(&format);
930 else *len = end-buf-15;
932 if (buf[0x14] == 0x23)
938 WINE_TRACE("#cols %u\n", ncol);
940 if (type == 0 || type == 2)
945 for (nc = 0; nc < ncol; nc++)
947 WINE_TRACE("looking for format at offset %u for column %d\n", format - (buf + 0x15), nc);
948 if (buf[0x14] == 0x23)
950 if (buf[0x14] == 0x01)
954 bits = GET_USHORT(format, 0); format += 2;
955 if (bits & 0x0001) fetch_long(&format);
956 if (bits & 0x0002) fetch_short(&format);
957 if (bits & 0x0004) fetch_short(&format);
958 if (bits & 0x0008) fetch_short(&format);
959 if (bits & 0x0010) fetch_short(&format);
960 if (bits & 0x0020) fetch_short(&format);
961 if (bits & 0x0040) fetch_short(&format);
962 if (bits & 0x0100) format += 3;
965 int ntab = fetch_short(&format);
970 ts = fetch_ushort(&format);
971 if (ts & 0x4000) fetch_ushort(&format);
974 /* 0x0400, 0x0800 and 0x1000 don't need space */
975 if ((bits & 0xE080) != 0)
976 WINE_FIXME("Unsupported bits %04x, potential trouble ahead\n", bits);
978 while (text < text_end && format < format_end)
980 WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end);
981 textsize = strlen(text) + 1;
984 paragraph = HeapAlloc(GetProcessHeap(), 0,
985 sizeof(HLPFILE_PARAGRAPH) + textsize);
986 if (!paragraph) return FALSE;
987 *paragraphptr = paragraph;
988 paragraphptr = ¶graph->next;
990 paragraph->next = NULL;
991 paragraph->link = attributes.link;
992 if (paragraph->link) paragraph->link->wRefCount++;
993 paragraph->cookie = para_normal_text;
994 paragraph->u.text.wFont = attributes.wFont;
995 paragraph->u.text.wVSpace = attributes.wVSpace;
996 paragraph->u.text.wHSpace = attributes.wHSpace;
997 paragraph->u.text.wIndent = attributes.wIndent;
998 paragraph->u.text.lpszText = (char*)paragraph + sizeof(HLPFILE_PARAGRAPH);
999 strcpy(paragraph->u.text.lpszText, text);
1001 attributes.wVSpace = 0;
1002 attributes.wHSpace = 0;
1004 /* else: null text, keep on storing attributes */
1007 if (*format == 0xff)
1013 WINE_TRACE("format=%02x\n", *format);
1017 WINE_FIXME("NIY20\n");
1022 WINE_FIXME("NIY21\n");
1027 attributes.wFont = GET_USHORT(format, 1);
1028 WINE_TRACE("Changing font to %d\n", attributes.wFont);
1033 attributes.wVSpace++;
1038 attributes.wVSpace++;
1039 attributes.wIndent = 0;
1044 attributes.wIndent++;
1058 BYTE pos = (*format - 0x86);
1059 BYTE type = format[1];
1063 size = fetch_long(&format);
1065 paragraph = HeapAlloc(GetProcessHeap(), 0,
1066 sizeof(HLPFILE_PARAGRAPH) + textsize);
1067 if (!paragraph) return FALSE;
1068 *paragraphptr = paragraph;
1069 paragraphptr = ¶graph->next;
1071 paragraph->next = NULL;
1072 paragraph->link = attributes.link;
1073 if (paragraph->link) paragraph->link->wRefCount++;
1074 paragraph->cookie = para_bitmap;
1075 paragraph->u.gfx.pos = pos;
1079 fetch_ushort(&format); /* hot spot */
1082 switch (GET_SHORT(format, 0))
1085 HLPFILE_LoadGfxByIndex(hlpfile, GET_SHORT(format, 2),
1089 WINE_FIXME("does it work ??? %x<%lu>#%u\n",
1090 GET_SHORT(format, 0),
1091 size, GET_SHORT(format, 2));
1092 HLPFILE_LoadGfxByAddr(hlpfile, format + 2, size - 4,
1096 WINE_FIXME("??? %u\n", GET_SHORT(format, 0));
1101 WINE_FIXME("Got an embedded element %s\n", format + 6);
1104 WINE_FIXME("Got a type %d picture\n", type);
1107 if (attributes.wVSpace) paragraph->u.gfx.pos |= 0x8000;
1114 HLPFILE_FreeLink(attributes.link);
1115 attributes.link = NULL;
1121 WINE_FIXME("NIY non-break space/hyphen\n");
1133 WINE_TRACE("macro => %s\n", format + 3);
1134 HLPFILE_FreeLink(attributes.link);
1135 attributes.link = HLPFILE_AllocLink(hlp_link_macro, (const char*)format + 3,
1136 0, !(*format & 4), -1);
1137 format += 3 + GET_USHORT(format, 1);
1142 WINE_WARN("jump topic 1 => %u\n", GET_UINT(format, 1));
1143 HLPFILE_FreeLink(attributes.link);
1144 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1146 GET_UINT(format, 1)-16,
1157 HLPFILE_FreeLink(attributes.link);
1158 attributes.link = HLPFILE_AllocLink((*format & 1) ? hlp_link_link : hlp_link_popup,
1160 GET_UINT(format, 1),
1161 !(*format & 4), -1);
1170 char* ptr = (char*) format + 8;
1171 BYTE type = format[3];
1175 if (type == 1) wnd = *ptr++;
1176 if (type == 4 || type == 6)
1179 ptr += strlen(ptr) + 1;
1182 str = hlpfile->lpszPath;
1185 for (wnd = hlpfile->numWindows - 1; wnd >= 0; wnd--)
1187 if (!strcmp(ptr, hlpfile->windows[wnd].name)) break;
1190 WINE_WARN("Couldn't find window info for %s\n", ptr);
1192 HLPFILE_FreeLink(attributes.link);
1193 attributes.link = HLPFILE_AllocLink((*format & 4) ? hlp_link_link : hlp_link_popup,
1194 str, GET_UINT(format, 4),
1195 !(*format & 1), wnd);
1197 format += 3 + GET_USHORT(format, 1);
1201 WINE_WARN("format %02x\n", *format);
1206 HeapFree(GetProcessHeap(), 0, text_base);
1210 /******************************************************************
1215 static BOOL HLPFILE_ReadFont(HLPFILE* hlpfile)
1218 unsigned i, len, idx;
1219 unsigned face_num, dscr_num, face_offset, dscr_offset;
1222 if (!HLPFILE_FindSubFile("|FONT", &ref, &end))
1224 WINE_WARN("no subfile FONT\n");
1225 hlpfile->numFonts = 0;
1226 hlpfile->fonts = NULL;
1232 face_num = GET_USHORT(ref, 0);
1233 dscr_num = GET_USHORT(ref, 2);
1234 face_offset = GET_USHORT(ref, 4);
1235 dscr_offset = GET_USHORT(ref, 6);
1237 WINE_TRACE("Got NumFacenames=%u@%u NumDesc=%u@%u\n",
1238 face_num, face_offset, dscr_num, dscr_offset);
1240 hlpfile->numFonts = dscr_num;
1241 hlpfile->fonts = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_FONT) * dscr_num);
1243 len = (dscr_offset - face_offset) / face_num;
1244 /* EPP for (i = face_offset; i < dscr_offset; i += len) */
1245 /* EPP WINE_FIXME("[%d]: %*s\n", i / len, len, ref + i); */
1246 for (i = 0; i < dscr_num; i++)
1248 flag = ref[dscr_offset + i * 11 + 0];
1249 family = ref[dscr_offset + i * 11 + 2];
1251 hlpfile->fonts[i].LogFont.lfHeight = -ref[dscr_offset + i * 11 + 1] / 2 - 3;
1252 hlpfile->fonts[i].LogFont.lfWidth = 0;
1253 hlpfile->fonts[i].LogFont.lfEscapement = 0;
1254 hlpfile->fonts[i].LogFont.lfOrientation = 0;
1255 hlpfile->fonts[i].LogFont.lfWeight = (flag & 1) ? 700 : 400;
1256 hlpfile->fonts[i].LogFont.lfItalic = (flag & 2) ? TRUE : FALSE;
1257 hlpfile->fonts[i].LogFont.lfUnderline = (flag & 4) ? TRUE : FALSE;
1258 hlpfile->fonts[i].LogFont.lfStrikeOut = (flag & 8) ? TRUE : FALSE;
1259 hlpfile->fonts[i].LogFont.lfCharSet = DEFAULT_CHARSET;
1260 hlpfile->fonts[i].LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1261 hlpfile->fonts[i].LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1262 hlpfile->fonts[i].LogFont.lfQuality = DEFAULT_QUALITY;
1263 hlpfile->fonts[i].LogFont.lfPitchAndFamily = DEFAULT_PITCH;
1267 case 0x01: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_MODERN; break;
1268 case 0x02: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_ROMAN; break;
1269 case 0x03: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SWISS; break;
1270 case 0x04: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_SCRIPT; break;
1271 case 0x05: hlpfile->fonts[i].LogFont.lfPitchAndFamily |= FF_DECORATIVE; break;
1272 default: WINE_FIXME("Unknown family %u\n", family);
1274 idx = GET_USHORT(ref, dscr_offset + i * 11 + 3);
1278 memcpy(hlpfile->fonts[i].LogFont.lfFaceName, ref + face_offset + idx * len, min(len, LF_FACESIZE - 1));
1279 hlpfile->fonts[i].LogFont.lfFaceName[min(len, LF_FACESIZE - 1)] = '\0';
1283 WINE_FIXME("Too high face ref (%u/%u)\n", idx, face_num);
1284 strcpy(hlpfile->fonts[i].LogFont.lfFaceName, "Helv");
1286 hlpfile->fonts[i].hFont = 0;
1287 hlpfile->fonts[i].color = RGB(ref[dscr_offset + i * 11 + 5],
1288 ref[dscr_offset + i * 11 + 6],
1289 ref[dscr_offset + i * 11 + 7]);
1290 #define X(b,s) ((flag & (1 << b)) ? "-"s: "")
1291 WINE_TRACE("Font[%d]: flags=%02x%s%s%s%s%s%s pSize=%u family=%u face=%s[%u] color=%08x\n",
1297 X(4, "dblUnderline"),
1299 ref[dscr_offset + i * 11 + 1],
1301 hlpfile->fonts[i].LogFont.lfFaceName, idx,
1302 GET_UINT(ref, dscr_offset + i * 11 + 5) & 0x00FFFFFF);
1307 /***********************************************************************
1309 * HLPFILE_ReadFileToBuffer
1311 static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile)
1313 BYTE header[16], dummy[1];
1315 if (_hread(hFile, header, 16) != 16) {WINE_WARN("header\n"); return FALSE;};
1318 if (GET_UINT(header, 0) != 0x00035F3F)
1319 {WINE_WARN("wrong header\n"); return FALSE;};
1321 file_buffer_size = GET_UINT(header, 12);
1322 file_buffer = HeapAlloc(GetProcessHeap(), 0, file_buffer_size + 1);
1323 if (!file_buffer) return FALSE;
1325 memcpy(file_buffer, header, 16);
1326 if (_hread(hFile, file_buffer + 16, file_buffer_size - 16) != file_buffer_size - 16)
1327 {WINE_WARN("filesize1\n"); return FALSE;};
1329 if (_hread(hFile, dummy, 1) != 0) WINE_WARN("filesize2\n");
1331 file_buffer[file_buffer_size] = '\0'; /* FIXME: was '0', sounds ackward to me */
1336 /**************************************************************************
1339 * HLPFILE_BPTreeCompare function for HLPFILE directory.
1342 static int comp_FindSubFile(void *p, const void *key,
1343 int leaf, void** next)
1345 *next = (char *)p+strlen(p)+(leaf?5:3);
1346 WINE_TRACE("Comparing '%s' with '%s'\n", (char *)p, (char *)key);
1347 return strcmp(p, key);
1350 /***********************************************************************
1352 * HLPFILE_FindSubFile
1354 static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend)
1358 WINE_TRACE("looking for file '%s'\n", name);
1359 ptr = HLPFILE_BPTreeSearch(file_buffer + GET_UINT(file_buffer, 4),
1360 name, comp_FindSubFile);
1361 if (!ptr) return FALSE;
1362 *subbuf = file_buffer + GET_UINT(ptr, strlen(name)+1);
1363 if (*subbuf >= file_buffer + file_buffer_size)
1365 WINE_ERR("internal file %s does not fit\n", name);
1368 *subend = *subbuf + GET_UINT(*subbuf, 0);
1369 if (*subend > file_buffer + file_buffer_size)
1371 WINE_ERR("internal file %s does not fit\n", name);
1374 if (GET_UINT(*subbuf, 0) < GET_UINT(*subbuf, 4) + 9)
1376 WINE_ERR("invalid size provided for internal file %s\n", name);
1382 /***********************************************************************
1384 * HLPFILE_SystemCommands
1386 static BOOL HLPFILE_SystemCommands(HLPFILE* hlpfile)
1388 BYTE *buf, *ptr, *end;
1389 HLPFILE_MACRO *macro, **m;
1391 unsigned short magic, minor, major, flags;
1393 hlpfile->lpszTitle = NULL;
1395 if (!HLPFILE_FindSubFile("|SYSTEM", &buf, &end)) return FALSE;
1397 magic = GET_USHORT(buf + 9, 0);
1398 minor = GET_USHORT(buf + 9, 2);
1399 major = GET_USHORT(buf + 9, 4);
1400 /* gen date on 4 bytes */
1401 flags = GET_USHORT(buf + 9, 10);
1402 WINE_TRACE("Got system header: magic=%04x version=%d.%d flags=%04x\n",
1403 magic, major, minor, flags);
1404 if (magic != 0x036C || major != 1)
1405 {WINE_WARN("Wrong system header\n"); return FALSE;}
1408 hlpfile->tbsize = 0x800;
1409 hlpfile->compressed = 0;
1411 else if (flags == 0)
1413 hlpfile->tbsize = 0x1000;
1414 hlpfile->compressed = 0;
1416 else if (flags == 4)
1418 hlpfile->tbsize = 0x1000;
1419 hlpfile->compressed = 1;
1423 hlpfile->tbsize = 0x800;
1424 hlpfile->compressed = 1;
1427 if (hlpfile->compressed)
1428 hlpfile->dsize = 0x4000;
1430 hlpfile->dsize = hlpfile->tbsize - 0x0C;
1432 hlpfile->version = minor;
1433 hlpfile->flags = flags;
1435 for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4)
1437 char *str = (char*) ptr + 4;
1438 switch (GET_USHORT(ptr, 0))
1441 if (hlpfile->lpszTitle) {WINE_WARN("title\n"); break;}
1442 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1443 if (!hlpfile->lpszTitle) return FALSE;
1444 lstrcpy(hlpfile->lpszTitle, str);
1445 WINE_TRACE("Title: %s\n", hlpfile->lpszTitle);
1449 if (hlpfile->lpszCopyright) {WINE_WARN("copyright\n"); break;}
1450 hlpfile->lpszCopyright = HeapAlloc(GetProcessHeap(), 0, strlen(str) + 1);
1451 if (!hlpfile->lpszCopyright) return FALSE;
1452 lstrcpy(hlpfile->lpszCopyright, str);
1453 WINE_TRACE("Copyright: %s\n", hlpfile->lpszCopyright);
1457 if (GET_USHORT(ptr, 2) != 4) {WINE_WARN("system3\n");break;}
1458 hlpfile->contents_start = GET_UINT(ptr, 4);
1459 WINE_TRACE("Setting contents start at %08lx\n", hlpfile->contents_start);
1463 macro = HeapAlloc(GetProcessHeap(), 0, sizeof(HLPFILE_MACRO) + lstrlen(str) + 1);
1465 p = (char*)macro + sizeof(HLPFILE_MACRO);
1467 macro->lpszMacro = p;
1469 for (m = &hlpfile->first_macro; *m; m = &(*m)->next);
1474 if (GET_USHORT(ptr, 2) != 90) {WINE_WARN("system6\n");break;}
1476 if (hlpfile->windows)
1477 hlpfile->windows = HeapReAlloc(GetProcessHeap(), 0, hlpfile->windows,
1478 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1480 hlpfile->windows = HeapAlloc(GetProcessHeap(), 0,
1481 sizeof(HLPFILE_WINDOWINFO) * ++hlpfile->numWindows);
1483 if (hlpfile->windows)
1485 unsigned flags = GET_USHORT(ptr, 4);
1486 HLPFILE_WINDOWINFO* wi = &hlpfile->windows[hlpfile->numWindows - 1];
1488 if (flags & 0x0001) strcpy(wi->type, &str[2]);
1489 else wi->type[0] = '\0';
1490 if (flags & 0x0002) strcpy(wi->name, &str[12]);
1491 else wi->name[0] = '\0';
1492 if (flags & 0x0004) strcpy(wi->caption, &str[23]);
1493 else lstrcpynA(wi->caption, hlpfile->lpszTitle, sizeof(wi->caption));
1494 wi->origin.x = (flags & 0x0008) ? GET_USHORT(ptr, 76) : CW_USEDEFAULT;
1495 wi->origin.y = (flags & 0x0010) ? GET_USHORT(ptr, 78) : CW_USEDEFAULT;
1496 wi->size.cx = (flags & 0x0020) ? GET_USHORT(ptr, 80) : CW_USEDEFAULT;
1497 wi->size.cy = (flags & 0x0040) ? GET_USHORT(ptr, 82) : CW_USEDEFAULT;
1498 wi->style = (flags & 0x0080) ? GET_USHORT(ptr, 84) : SW_SHOW;
1499 wi->win_style = WS_OVERLAPPEDWINDOW;
1500 wi->sr_color = (flags & 0x0100) ? GET_UINT(ptr, 86) : 0xFFFFFF;
1501 wi->nsr_color = (flags & 0x0200) ? GET_UINT(ptr, 90) : 0xFFFFFF;
1502 WINE_TRACE("System-Window: flags=%c%c%c%c%c%c%c%c type=%s name=%s caption=%s (%d,%d)x(%d,%d)\n",
1503 flags & 0x0001 ? 'T' : 't',
1504 flags & 0x0002 ? 'N' : 'n',
1505 flags & 0x0004 ? 'C' : 'c',
1506 flags & 0x0008 ? 'X' : 'x',
1507 flags & 0x0010 ? 'Y' : 'y',
1508 flags & 0x0020 ? 'W' : 'w',
1509 flags & 0x0040 ? 'H' : 'h',
1510 flags & 0x0080 ? 'S' : 's',
1511 wi->type, wi->name, wi->caption, wi->origin.x, wi->origin.y,
1512 wi->size.cx, wi->size.cy);
1516 WINE_WARN("Unsupported SystemRecord[%d]\n", GET_USHORT(ptr, 0));
1519 if (!hlpfile->lpszTitle)
1520 hlpfile->lpszTitle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1);
1524 /***********************************************************************
1526 * HLPFILE_UncompressedLZ77_Size
1528 static INT HLPFILE_UncompressedLZ77_Size(BYTE *ptr, BYTE *end)
1535 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1539 int code = GET_USHORT(ptr, 0);
1540 int len = 3 + (code >> 12);
1544 else newsize++, ptr++;
1551 /***********************************************************************
1553 * HLPFILE_UncompressLZ77
1555 static BYTE *HLPFILE_UncompressLZ77(BYTE *ptr, BYTE *end, BYTE *newptr)
1562 for (i = 0; i < 8 && ptr < end; i++, mask >>= 1)
1566 int code = GET_USHORT(ptr, 0);
1567 int len = 3 + (code >> 12);
1568 int offset = code & 0xfff;
1570 * We must copy byte-by-byte here. We cannot use memcpy nor
1571 * memmove here. Just example:
1572 * a[]={1,2,3,4,5,6,7,8,9,10}
1576 * {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 11, 12}
1578 for (; len>0; len--, newptr++) *newptr = *(newptr-offset-1);
1581 else *newptr++ = *ptr++;
1588 /***********************************************************************
1590 * HLPFILE_UncompressLZ77_Phrases
1592 static BOOL HLPFILE_UncompressLZ77_Phrases(HLPFILE* hlpfile)
1594 UINT i, num, dec_size, head_size;
1597 if (!HLPFILE_FindSubFile("|Phrases", &buf, &end)) return FALSE;
1599 if (hlpfile->version <= 16)
1604 num = phrases.num = GET_USHORT(buf, 9);
1605 if (buf + 2 * num + 0x13 >= end) {WINE_WARN("1a\n"); return FALSE;};
1607 if (hlpfile->version <= 16)
1608 dec_size = end - buf - 15 - 2 * num;
1610 dec_size = HLPFILE_UncompressedLZ77_Size(buf + 0x13 + 2 * num, end);
1612 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1613 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1614 if (!phrases.offsets || !phrases.buffer)
1616 HeapFree(GetProcessHeap(), 0, phrases.offsets);
1617 HeapFree(GetProcessHeap(), 0, phrases.buffer);
1621 for (i = 0; i <= num; i++)
1622 phrases.offsets[i] = GET_USHORT(buf, head_size + 2 * i) - 2 * num - 2;
1624 if (hlpfile->version <= 16)
1625 memcpy(phrases.buffer, buf + 15 + 2*num, dec_size);
1627 HLPFILE_UncompressLZ77(buf + 0x13 + 2 * num, end, (BYTE*)phrases.buffer);
1629 hlpfile->hasPhrases = TRUE;
1633 /***********************************************************************
1635 * HLPFILE_Uncompress_Phrases40
1637 static BOOL HLPFILE_Uncompress_Phrases40(HLPFILE* hlpfile)
1640 INT dec_size, cpr_size;
1641 BYTE *buf_idx, *end_idx;
1642 BYTE *buf_phs, *end_phs;
1643 long* ptr, mask = 0;
1645 unsigned short bc, n;
1647 if (!HLPFILE_FindSubFile("|PhrIndex", &buf_idx, &end_idx) ||
1648 !HLPFILE_FindSubFile("|PhrImage", &buf_phs, &end_phs)) return FALSE;
1650 ptr = (long*)(buf_idx + 9 + 28);
1651 bc = GET_USHORT(buf_idx, 9 + 24) & 0x0F;
1652 num = phrases.num = GET_USHORT(buf_idx, 9 + 4);
1654 WINE_TRACE("Index: Magic=%08x #entries=%u CpsdSize=%u PhrImgSize=%u\n"
1655 "\tPhrImgCprsdSize=%u 0=%u bc=%x ukn=%x\n",
1656 GET_UINT(buf_idx, 9 + 0),
1657 GET_UINT(buf_idx, 9 + 4),
1658 GET_UINT(buf_idx, 9 + 8),
1659 GET_UINT(buf_idx, 9 + 12),
1660 GET_UINT(buf_idx, 9 + 16),
1661 GET_UINT(buf_idx, 9 + 20),
1662 GET_USHORT(buf_idx, 9 + 24),
1663 GET_USHORT(buf_idx, 9 + 26));
1665 dec_size = GET_UINT(buf_idx, 9 + 12);
1666 cpr_size = GET_UINT(buf_idx, 9 + 16);
1668 if (dec_size != cpr_size &&
1669 dec_size != HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs))
1671 WINE_WARN("size mismatch %u %u\n",
1672 dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1673 dec_size = max(dec_size, HLPFILE_UncompressedLZ77_Size(buf_phs + 9, end_phs));
1676 phrases.offsets = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned) * (num + 1));
1677 phrases.buffer = HeapAlloc(GetProcessHeap(), 0, dec_size);
1678 if (!phrases.offsets || !phrases.buffer)
1680 HeapFree(GetProcessHeap(), 0, phrases.offsets);
1681 HeapFree(GetProcessHeap(), 0, phrases.buffer);
1685 #define getbit() (ptr += (mask < 0), mask = mask*2 + (mask<=0), (*ptr & mask) != 0)
1687 phrases.offsets[0] = 0;
1688 for (i = 0; i < num; i++)
1690 for (n = 1; getbit(); n += 1 << bc);
1692 if (bc > 1 && getbit()) n += 2;
1693 if (bc > 2 && getbit()) n += 4;
1694 if (bc > 3 && getbit()) n += 8;
1695 if (bc > 4 && getbit()) n += 16;
1696 phrases.offsets[i + 1] = phrases.offsets[i] + n;
1700 if (dec_size == cpr_size)
1701 memcpy(phrases.buffer, buf_phs + 9, dec_size);
1703 HLPFILE_UncompressLZ77(buf_phs + 9, end_phs, (BYTE*)phrases.buffer);
1705 hlpfile->hasPhrases40 = TRUE;
1709 /***********************************************************************
1711 * HLPFILE_Uncompress_Topic
1713 static BOOL HLPFILE_Uncompress_Topic(HLPFILE* hlpfile)
1715 BYTE *buf, *ptr, *end, *newptr;
1716 unsigned int i, newsize = 0;
1717 unsigned int topic_size;
1719 if (!HLPFILE_FindSubFile("|TOPIC", &buf, &end))
1720 {WINE_WARN("topic0\n"); return FALSE;}
1722 buf += 9; /* Skip file header */
1723 topic_size = end - buf;
1724 if (hlpfile->compressed)
1726 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1728 for (i = 0; i < topic.wMapLen; i++)
1730 ptr = buf + i * hlpfile->tbsize;
1732 /* I don't know why, it's necessary for printman.hlp */
1733 if (ptr + 0x44 > end) ptr = end - 0x44;
1735 newsize += HLPFILE_UncompressedLZ77_Size(ptr + 0xc, min(end, ptr + hlpfile->tbsize));
1738 topic.map = HeapAlloc(GetProcessHeap(), 0,
1739 topic.wMapLen * sizeof(topic.map[0]) + newsize);
1740 if (!topic.map) return FALSE;
1741 newptr = (BYTE*)(topic.map + topic.wMapLen);
1742 topic.end = newptr + newsize;
1744 for (i = 0; i < topic.wMapLen; i++)
1746 ptr = buf + i * hlpfile->tbsize;
1747 if (ptr + 0x44 > end) ptr = end - 0x44;
1749 topic.map[i] = newptr;
1750 newptr = HLPFILE_UncompressLZ77(ptr + 0xc, min(end, ptr + hlpfile->tbsize), newptr);
1755 /* basically, we need to copy the TopicBlockSize byte pages
1756 * (removing the first 0x0C) in one single area in memory
1758 topic.wMapLen = (topic_size - 1) / hlpfile->tbsize + 1;
1759 topic.map = HeapAlloc(GetProcessHeap(), 0,
1760 topic.wMapLen * (sizeof(topic.map[0]) + hlpfile->dsize));
1761 if (!topic.map) return FALSE;
1762 newptr = (BYTE*)(topic.map + topic.wMapLen);
1763 topic.end = newptr + topic_size;
1765 for (i = 0; i < topic.wMapLen; i++)
1767 topic.map[i] = newptr + i * hlpfile->dsize;
1768 memcpy(topic.map[i], buf + i * hlpfile->tbsize + 0x0C, hlpfile->dsize);
1774 /***********************************************************************
1776 * HLPFILE_Uncompress2
1779 static void HLPFILE_Uncompress2(const BYTE *ptr, const BYTE *end, BYTE *newptr, const BYTE *newend)
1781 BYTE *phptr, *phend;
1785 while (ptr < end && newptr < newend)
1787 if (!*ptr || *ptr >= 0x10)
1791 code = 0x100 * ptr[0] + ptr[1];
1792 index = (code - 0x100) / 2;
1794 phptr = (BYTE*)phrases.buffer + phrases.offsets[index];
1795 phend = (BYTE*)phrases.buffer + phrases.offsets[index + 1];
1797 if (newptr + (phend - phptr) > newend)
1799 WINE_FIXME("buffer overflow %p > %p for %d bytes\n",
1800 newptr, newend, phend - phptr);
1803 memcpy(newptr, phptr, phend - phptr);
1804 newptr += phend - phptr;
1805 if (code & 1) *newptr++ = ' ';
1810 if (newptr > newend) WINE_FIXME("buffer overflow %p > %p\n", newptr, newend);
1813 /******************************************************************
1814 * HLPFILE_Uncompress3
1818 static BOOL HLPFILE_Uncompress3(char* dst, const char* dst_end,
1819 const BYTE* src, const BYTE* src_end)
1821 unsigned int idx, len;
1823 for (; src < src_end; src++)
1825 if ((*src & 1) == 0)
1828 if (idx > phrases.num)
1830 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1835 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1836 if (dst + len <= dst_end)
1837 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1840 else if ((*src & 0x03) == 0x01)
1842 idx = (*src + 1) * 64;
1844 if (idx > phrases.num)
1846 WINE_ERR("index in phrases %d/%d\n", idx, phrases.num);
1851 len = phrases.offsets[idx + 1] - phrases.offsets[idx];
1852 if (dst + len <= dst_end)
1853 memcpy(dst, &phrases.buffer[phrases.offsets[idx]], len);
1856 else if ((*src & 0x07) == 0x03)
1858 len = (*src / 8) + 1;
1859 if (dst + len <= dst_end)
1860 memcpy(dst, src + 1, len);
1865 len = (*src / 16) + 1;
1866 if (dst + len <= dst_end)
1867 memset(dst, ((*src & 0x0F) == 0x07) ? ' ' : 0, len);
1872 if (dst > dst_end) WINE_ERR("buffer overflow (%p > %p)\n", dst, dst_end);
1876 /******************************************************************
1877 * HLPFILE_UncompressRLE
1881 static void HLPFILE_UncompressRLE(const BYTE* src, const BYTE* end, BYTE** dst, unsigned dstsz)
1884 BYTE* sdst = *dst + dstsz;
1892 if ((*dst) + ch <= sdst)
1893 memcpy(*dst, src, ch);
1898 if ((*dst) + ch <= sdst)
1899 memset(*dst, (char)*src, ch);
1905 WINE_WARN("Buffer X-flow: d(%u) instead of d(%u)\n",
1906 *dst - (sdst - dstsz), dstsz);
1909 /**************************************************************************
1910 * HLPFILE_BPTreeSearch
1912 * Searches for an element in B+ tree
1915 * buf [I] pointer to the embedded file structured as a B+ tree
1916 * key [I] pointer to data to find
1917 * comp [I] compare function
1920 * Pointer to block identified by key, or NULL if failure.
1923 void* HLPFILE_BPTreeSearch(BYTE* buf, const void* key,
1924 HLPFILE_BPTreeCompare comp)
1930 BYTE *pages, *ptr, *newptr;
1934 magic = GET_USHORT(buf, 9);
1935 if (magic != 0x293B)
1937 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1940 page_size = GET_USHORT(buf, 9+4);
1941 cur_page = GET_USHORT(buf, 9+26);
1942 level = GET_USHORT(buf, 9+32);
1943 pages = buf + 9 + 38;
1946 ptr = pages + cur_page*page_size;
1947 entries = GET_SHORT(ptr, 2);
1949 for (i = 0; i < entries; i++)
1951 if (comp(ptr, key, 0, (void **)&newptr) > 0) break;
1954 cur_page = GET_USHORT(ptr-2, 0);
1956 ptr = pages + cur_page*page_size;
1957 entries = GET_SHORT(ptr, 2);
1959 for (i = 0; i < entries; i++)
1961 ret = comp(ptr, key, 1, (void **)&newptr);
1962 if (ret == 0) return ptr;
1963 if (ret > 0) return NULL;
1969 /**************************************************************************
1970 * HLPFILE_BPTreeEnum
1972 * Enumerates elements in B+ tree.
1975 * buf [I] pointer to the embedded file structured as a B+ tree
1976 * cb [I] compare function
1977 * cookie [IO] cookie for cb function
1979 void HLPFILE_BPTreeEnum(BYTE* buf, HLPFILE_BPTreeCallback cb, void* cookie)
1985 BYTE *pages, *ptr, *newptr;
1988 magic = GET_USHORT(buf, 9);
1989 if (magic != 0x293B)
1991 WINE_ERR("Invalid magic in B+ tree: 0x%x\n", magic);
1994 page_size = GET_USHORT(buf, 9+4);
1995 cur_page = GET_USHORT(buf, 9+26);
1996 level = GET_USHORT(buf, 9+32);
1997 pages = buf + 9 + 38;
2000 ptr = pages + cur_page*page_size;
2001 cur_page = GET_USHORT(ptr, 4);
2003 while (cur_page != 0xFFFF)
2005 ptr = pages + cur_page*page_size;
2006 entries = GET_SHORT(ptr, 2);
2008 for (i = 0; i < entries; i++)
2010 cb(ptr, (void **)&newptr, cookie);
2013 cur_page = GET_USHORT(pages+cur_page*page_size, 6);
2018 /***********************************************************************
2020 * HLPFILE_GetContext
2022 static BOOL HLPFILE_GetContext(HLPFILE *hlpfile)
2027 if (!HLPFILE_FindSubFile("|CONTEXT", &cbuf, &cend)) {WINE_WARN("context0\n"); return FALSE;}
2030 hlpfile->Context = HeapAlloc(GetProcessHeap(), 0, clen);
2031 if (!hlpfile->Context) return FALSE;
2032 memcpy(hlpfile->Context, cbuf, clen);
2037 /***********************************************************************
2039 * HLPFILE_GetKeywords
2041 static BOOL HLPFILE_GetKeywords(HLPFILE *hlpfile)
2046 if (!HLPFILE_FindSubFile("|KWBTREE", &cbuf, &cend)) return FALSE;
2048 hlpfile->kwbtree = HeapAlloc(GetProcessHeap(), 0, clen);
2049 if (!hlpfile->kwbtree) return FALSE;
2050 memcpy(hlpfile->kwbtree, cbuf, clen);
2052 if (!HLPFILE_FindSubFile("|KWDATA", &cbuf, &cend))
2054 WINE_ERR("corrupted help file: kwbtree present but kwdata absent\n");
2055 HeapFree(GetProcessHeap(), 0, hlpfile->kwbtree);
2059 hlpfile->kwdata = HeapAlloc(GetProcessHeap(), 0, clen);
2060 if (!hlpfile->kwdata)
2062 HeapFree(GetProcessHeap(), 0, hlpfile->kwdata);
2065 memcpy(hlpfile->kwdata, cbuf, clen);
2070 /***********************************************************************
2074 static BOOL HLPFILE_GetMap(HLPFILE *hlpfile)
2077 unsigned entries, i;
2079 if (!HLPFILE_FindSubFile("|CTXOMAP", &cbuf, &cend)) {WINE_WARN("no map section\n"); return FALSE;}
2081 entries = GET_USHORT(cbuf, 9);
2082 hlpfile->Map = HeapAlloc(GetProcessHeap(), 0, entries * sizeof(HLPFILE_MAP));
2083 if (!hlpfile->Map) return FALSE;
2084 hlpfile->wMapLen = entries;
2085 for (i = 0; i < entries; i++)
2087 hlpfile->Map[i].lMap = GET_UINT(cbuf+11,i*8);
2088 hlpfile->Map[i].offset = GET_UINT(cbuf+11,i*8+4);
2093 /******************************************************************
2094 * HLPFILE_DeleteLink
2098 void HLPFILE_FreeLink(HLPFILE_LINK* link)
2100 if (link && !--link->wRefCount)
2101 HeapFree(GetProcessHeap(), 0, link);
2104 /***********************************************************************
2106 * HLPFILE_DeleteParagraph
2108 static void HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph)
2110 HLPFILE_PARAGRAPH* next;
2114 next = paragraph->next;
2116 if (paragraph->cookie == para_metafile)
2117 DeleteMetaFile(paragraph->u.gfx.u.mfp.hMF);
2119 HLPFILE_FreeLink(paragraph->link);
2121 HeapFree(GetProcessHeap(), 0, paragraph);
2126 /***********************************************************************
2130 static void HLPFILE_DeleteMacro(HLPFILE_MACRO* macro)
2132 HLPFILE_MACRO* next;
2137 HeapFree(GetProcessHeap(), 0, macro);
2142 /***********************************************************************
2146 static void HLPFILE_DeletePage(HLPFILE_PAGE* page)
2153 HLPFILE_DeleteParagraph(page->first_paragraph);
2154 HLPFILE_DeleteMacro(page->first_macro);
2155 HeapFree(GetProcessHeap(), 0, page);
2160 /***********************************************************************
2162 * HLPFILE_FreeHlpFile
2164 void HLPFILE_FreeHlpFile(HLPFILE* hlpfile)
2168 if (!hlpfile || --hlpfile->wRefCount > 0) return;
2170 if (hlpfile->next) hlpfile->next->prev = hlpfile->prev;
2171 if (hlpfile->prev) hlpfile->prev->next = hlpfile->next;
2172 else first_hlpfile = hlpfile->next;
2174 if (hlpfile->numFonts)
2176 for (i = 0; i < hlpfile->numFonts; i++)
2178 DeleteObject(hlpfile->fonts[i].hFont);
2180 HeapFree(GetProcessHeap(), 0, hlpfile->fonts);
2183 if (hlpfile->numBmps)
2185 for (i = 0; i < hlpfile->numBmps; i++)
2187 DeleteObject(hlpfile->bmps[i]);
2189 HeapFree(GetProcessHeap(), 0, hlpfile->bmps);
2192 HLPFILE_DeletePage(hlpfile->first_page);
2193 HLPFILE_DeleteMacro(hlpfile->first_macro);
2195 if (hlpfile->numWindows) HeapFree(GetProcessHeap(), 0, hlpfile->windows);
2196 HeapFree(GetProcessHeap(), 0, hlpfile->Context);
2197 HeapFree(GetProcessHeap(), 0, hlpfile->Map);
2198 HeapFree(GetProcessHeap(), 0, hlpfile->lpszTitle);
2199 HeapFree(GetProcessHeap(), 0, hlpfile->lpszCopyright);
2200 HeapFree(GetProcessHeap(), 0, hlpfile);